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