三、MCU中BootLoader實(shí)現(xiàn)的方式
要先將BL的代碼通過下載器燒錄到MCU中,然后再通過CAN總線將接收到的app代碼寫入到指定的Flash扇區(qū)中,校驗(yàn)接收的app代碼,如果完全正確,則將程序指針指向app的入口地址,完成了BL向app的轉(zhuǎn)換。在此過程中要解決下面幾個問題:
1)BL代碼的CMD文件如何編寫,即BL階段,MCU內(nèi)存如何分配。
2)對應(yīng)的app的CMD文件中,如何分配MCU內(nèi)存。
3)app的編譯輸出文件.out用什么格式傳送給MCU。
4)接收app代碼的協(xié)議和Flash寫入機(jī)制
5)如何指向app的入口地址
1)BL的CMD文件一般分成兩部分
上圖中,CMD文件是在以前編寫的F28034.CMD文件基礎(chǔ)上改寫的,其實(shí)是F28035內(nèi)存的分配方式
從上圖中,可以看出來,BL與app的Flash存儲區(qū)域是分開的。而且app明顯要比BL區(qū)域大的多。當(dāng)將BL下載到MCU中時,要同時將所有的Flash扇區(qū)擦除。
接收的app代碼做為CAN傳送的數(shù)據(jù),需要一個緩沖區(qū)。為了避免數(shù)據(jù)溢出,在內(nèi)存選擇了一段速度快且容量大的區(qū)域做為CAN數(shù)據(jù)緩沖區(qū)。
可以看到,將RAML0-3這段連續(xù)的區(qū)域合并起來使用,容量是8K×16bit當(dāng)做數(shù)據(jù)緩沖。如果app用到CLA外設(shè),會占用RAML1-L3的一部分空間,這樣會和BL的內(nèi)存分配產(chǎn)生沖突嗎?
并不會。因?yàn)閍pp被引導(dǎo)進(jìn)入自己的_c_int00后,會重新按照app本身的CMD分配方式分配RAML0-3代碼或數(shù)據(jù)的存放。
這樣就將BL的CMD編寫完成。
2)app的存儲分配
可知,app的Flash的分配與BL的Flash分配完全一致,而RAMM0-1、RAML0-3則完全不一樣,但這不影響app的正常運(yùn)行。
app程序可以先獨(dú)立開發(fā),即不通過BL下載到MCU中。app編譯完成后,用TI的XDS100V3下載器燒錄到MCU中,只要避開FlashA扇區(qū)的使用即可。
3)app的編譯輸出文件.out用什么格式傳送給MCU。
當(dāng)app編譯完成后,會輸出一個.out文件,但我們不可能用CAN傳送這種格式的文件內(nèi)容。在TI官方的BootROM中,關(guān)于串口或CAN的引導(dǎo)方式中,提到了一種app代碼文件的傳送方式。將.out文件轉(zhuǎn)換成數(shù)據(jù)流結(jié)構(gòu)。具體請參考TI的文檔SPRUGO0A.PDF的第22頁。
這里提到用hex2000.exe工具將.out轉(zhuǎn)換成流數(shù)據(jù)文件,然后再通過CAN總線傳送到MCU。MCU收到后,就按照該文件指定的方式,將app代碼寫入到指定的Flash扇區(qū)。
其實(shí)就是將.out轉(zhuǎn)換成ASCII-Hex格式
轉(zhuǎn)換完成
一共生成了5個塊.cinit、.text、codestart、ramfuncs、Cla1Prog。
這個后綴為.a00的文件可以用記事本打開。
根據(jù)SPRUGO0A.PDF這篇文檔的說明,將此文件處理了一下,方便觀察。
可以看到hex2000將.out文件轉(zhuǎn)換成幾個塊狀的數(shù)據(jù)結(jié)構(gòu),這幾個塊就是要寫入到Flash對應(yīng)扇區(qū)的數(shù)據(jù)。最上面有該app的入口地址(0x003E F293) 。還可以查看編譯后生成的對應(yīng).map文件。
入口地址確實(shí)是0x003E F293。然后再對比一下第一個段的地址和大小
從上面可以知道,只要按照生成的.a00文件的內(nèi)容,依次將收到的app代碼寫入到指定的Flash地址中,然后在BL中將程序指針指向.a00的入口地址,就能實(shí)現(xiàn)BL啟動app的目地。
4)接收app代碼的協(xié)議和Flash寫入機(jī)制
接收app代碼的協(xié)議是根據(jù)自己的習(xí)慣,自行制定的,沒有一定之規(guī)。
作者的方法是:
- 上位機(jī)先將.a00文件轉(zhuǎn)換成數(shù)據(jù)流結(jié)構(gòu),然后上位機(jī)會以.a00文件中每個塊做為一個段,以段為單位向MCU發(fā)送段的內(nèi)容。
- 先傳送此段的大小(長度)和地址。
- 當(dāng)MCU收到段長度和段地址后,會發(fā)送一個確認(rèn)信號給上位機(jī)。上位機(jī)收到確認(rèn)信號后,再傳送此段的內(nèi)容。這個段的長度、地址和內(nèi)容經(jīng)過CAN接收,全部緩存在MCU的內(nèi)存RAML0-3中,此內(nèi)存區(qū)域容量為8K×16bit,所以每個段的大小不能超過此數(shù)值,否則會出現(xiàn)溢出。目前,作者編寫的MCU程序代碼的每段大小還沒有超過此數(shù)值的。
- 一個段傳送完成后,MCU一次性將RAML0-3中的內(nèi)容寫入到段指定的Flash地址中。然后再進(jìn)行下一個段的傳送。重復(fù)2、3的步驟
- 當(dāng)讀到某個段的大小為0時,表示所有段都傳送完畢,接著傳送app的入口地址,要將這個入口地址也保存在Flash里,通過前面BL和app的CMD存儲區(qū)域的劃分,可以知道,有一個FlashB扇區(qū)既沒用于BL也沒用于aap。此區(qū)域就是用來存儲app入口地址的。
- 進(jìn)行app代碼的校驗(yàn)。校驗(yàn)成功后,通過看門狗復(fù)位,MCU重啟,BL在規(guī)定時間內(nèi)收不到PC端上位機(jī)發(fā)出的握手信號,則自動進(jìn)入app的程序空間。
5)BL是如何指向app的入口地址的
首先,上位機(jī)已經(jīng)正確將app的入口地址傳送給MCU,MCU將入口地址存放在FlashB扇區(qū)。聲明一個32bit的無符號整型變量,如下:
volatile Uint32 ProgramEntry = 0;
然后,讀出FlashB中存儲的入口地址,并聲明的變量指向入口地址。
當(dāng)BL在規(guī)定的時間內(nèi),沒有接收到上位機(jī)的握手信號,則執(zhí)行下面的語句。
(*((void(*)(void))ProgramEntry))();
-
(void(*)(void))
:這是一個類型轉(zhuǎn)換,它將ProgramEntry
轉(zhuǎn)換為一個指向返回void
類型且不接受任何參數(shù)的函數(shù)的指針。換句話說,它將ProgramEntry
視為一個函數(shù)指針,該函數(shù)不返回任何值,也不接收任何參數(shù)。 *(...)
:這是間接引用操作符,它用于調(diào)用函數(shù)指針指向的函數(shù)-
(*((void(*)(void))ProgramEntry))();
:將上述所有部分組合起來,這行代碼首先將ProgramEntry
轉(zhuǎn)換為一個函數(shù)指針,然后調(diào)用這個函數(shù)。
用上面的方法就可以將程序指針指向ProgramEntry
變量存儲的入口地址。(待續(xù))