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