IAP实验笔记
ISP和IAP
ISP:在系統(tǒng)編程。通過芯片上已經(jīng)固化好的bootloader,從串口將代碼寫入內(nèi)部的flash。這塊bootloader位于系統(tǒng)存儲區(qū),置位boot引腳來選擇系統(tǒng)存儲器為啟動區(qū)域。
IAP:在應(yīng)用編程。用戶根據(jù)自己需要將flash分成兩個區(qū)域,一塊用作bootloader,一塊是我們正常運(yùn)行的程序(app),而bootloader的作用和上面ISP的一樣,將app代碼引導(dǎo)至flash中。
IAP過程
stm32正常運(yùn)行流程
從圖中可以看到,在0x08000000處存放著程序的中斷向量表,它由棧頂?shù)刂泛透髦袛嗟娜肟诘刂方M成。在上電復(fù)位后,芯片會由硬件控制回到0x08000000處,先將0x08000000地址開始的4字節(jié)棧頂指針?biāo)徒osp寄存器,然后將0x08000003地址開始存放的復(fù)位中斷的入口地址給程序計數(shù)器pc,從此開始程序跳轉(zhuǎn)到復(fù)位中斷服務(wù)函數(shù)。
跳轉(zhuǎn)到復(fù)位中斷后,它做的只有兩件事,一是系統(tǒng)時鐘初始化,二是跳轉(zhuǎn)到我們編寫的main函數(shù)
當(dāng)我們進(jìn)入到main函數(shù)時,程序會按照我們編寫的邏輯運(yùn)行。當(dāng)有中斷請求時,芯片會從中斷向量表中獲取相應(yīng)的中斷服務(wù)函數(shù)入口地址,跳轉(zhuǎn)到相應(yīng)的中斷程序。并且中斷向量表的位置是可以重定義的,它由SCB->VTOR寄存器控制。
有IAP的stm32運(yùn)行流程
從圖中可以看到,與stm32正常運(yùn)行流程相比,IAP相當(dāng)于加入了一個bootloader,通過它將我們的app代碼引導(dǎo)至flash中。首先,和上面的流程一樣,復(fù)位后,芯片配置堆棧指針和pc指針,初始化系統(tǒng)時鐘,然后進(jìn)入我們的main函數(shù),main函數(shù)里面是我們編寫的bootlaoder程序,按照我們的需要引導(dǎo)app程序至flash或者跳轉(zhuǎn)到app程序。
(上面的代碼是一個簡單的bootloader的測試。通過按鍵控制,從串口接收app程序,并跳轉(zhuǎn)到app程序。我們可以根據(jù)自己需求編寫,功能都大同小異)
自此,bootloader部分暫時結(jié)束。程序會跳轉(zhuǎn)到我們的app。從圖中可以看到,它其實是又重復(fù)了一遍正常的運(yùn)行流程,只不過相對于以前的0x08000000的起始地址有了一個偏移。值得注意的是,進(jìn)入我們app的main函數(shù)之后,如果有中斷請求,我們需要它進(jìn)入的是app的中斷,而不是bootloader的中斷,這時就需要對SCB->VTOR寄存器進(jìn)行重設(shè)。
IAP代碼
這里主要是對正點原子提供的bootloader程序的部分代碼分析。
bootloader
bootloader主要包括兩個功能:引導(dǎo)app和跳轉(zhuǎn)到app。
引導(dǎo)app到flash這個功能的實現(xiàn)很簡單,利用的是flash的擦除和讀寫,在此不做贅述。
跳轉(zhuǎn)到app
//跳轉(zhuǎn)到應(yīng)用程序段 //appxaddr:用戶代碼起始地址. void iap_load_app(u32 appxaddr) {jump2app=(iapfun)*(vu32*)(appxaddr+4); //用戶代碼區(qū)第二個字為程序開始地址(復(fù)位地址) MSR_MSP(*(vu32*)appxaddr); //初始化APP堆棧指針(用戶代碼區(qū)的第一個字用于存放棧頂?shù)刂?jump2app(); //跳轉(zhuǎn)到APP. }(appxaddr是我們定義的app存放在flash中的地址,注意它不能和bootloader程序占用的空間沖突)
如前面分析所示,要跳轉(zhuǎn)到app程序執(zhí)行,就是把a(bǔ)pp程序起始地址給程序計數(shù)器pc,同時初始化app的棧頂指針。變化是以前是硬件完成,現(xiàn)在由軟件完成。
首先是pc指針賦值工作。我們無法對程序計數(shù)器pc直接操作,所以我們使用空函數(shù)跳轉(zhuǎn)的辦法。我們獲取appxaddr+4的內(nèi)容,得到復(fù)位中斷的入口地址,然后跳轉(zhuǎn)到它即可。這里的代碼先是取出了32位的函數(shù)地址,然后將其強(qiáng)制轉(zhuǎn)化成函數(shù)指針的類型賦給jump2app,最后執(zhí)行jump2app這一空函數(shù)實現(xiàn)跳轉(zhuǎn)。
以下代碼是iapfun和jump2app的定義,涉及函數(shù)指針和typedef別名定義,可以參考這兩篇文章,很有學(xué)習(xí)價值:
typedef void (*iapfun)(void); //定義一個函數(shù)指針 iapfun jump2app;typedef用法總結(jié)
函數(shù)指針和指針函數(shù)
接下來是初始化棧頂指針。
//設(shè)置棧頂?shù)刂?//addr:棧頂?shù)刂?__asm void MSR_MSP(u32 addr) {MSR MSP, r0 BX r14 }這里使用的是兩條匯編指令。函數(shù)的參數(shù)會自動傳給寄存器r0,利用指令MSR將r0的值存儲到特殊寄存器sp中,實現(xiàn)棧頂指針的重設(shè)。之后執(zhí)行BX指令,r14會保存執(zhí)行函數(shù)前的pc值,這樣就跳轉(zhuǎn)到原始位置執(zhí)行jump2app。
app
通過前面的分析,可以知道app程序要滿足兩個要求:
- 起始地址偏移
- 中斷向量表偏移
地址偏移的修改如下圖所示,根據(jù)bootloader的大小決定起始地址偏移量。比如我之前使用的bootloader是6k字節(jié),那么我就可以把0x08001800后的空間留給app使用(在這里使用0x08010000為起始地址),之后再根據(jù)flash容量確定大小即可。
對比偏移后生成的二進(jìn)制文件,就能發(fā)現(xiàn)其實是中斷向量表存放的中斷向量發(fā)生了一個整體偏移,對運(yùn)行的代碼沒有影響。
起始地址偏移修改完后,在沒有中斷發(fā)生的情況下,程序是可以正常跑起來的;在有中斷請求時,芯片會根據(jù)中斷向量表起始地址(VTOR寄存器存儲)和中斷類型號找到對應(yīng)中斷程序,因為中斷向量表起始地址還是默認(rèn)的0x08000000,所以它進(jìn)入的中斷還屬于bootloader。在這里修改中斷向量表起始地址,讓它定位到app。具體操作如下:
到這里,app的部分就配置好了,接下來就該生成對應(yīng)的二進(jìn)制文件讓bootloader引導(dǎo)進(jìn)flash。具體操作如下圖所示。
找到我們的MDK目錄下的fromelf程序,使用以下命令,讓它生成二進(jìn)制文件。
生成的二進(jìn)制文件就是我們編譯完成后的最純粹的數(shù)據(jù)代碼,cpu按照這個代碼順序執(zhí)行就能實現(xiàn)相應(yīng)功能。至于為什么不使用十六進(jìn)制文件,是因為他還有很多附加信息,比如數(shù)據(jù)存儲的起始地址,cpu執(zhí)行過程中并不需要它們。具體可參考以下文章。
hex和bin的區(qū)別
總結(jié)
- 上一篇: c语言中int sel是什么意思,SEL
- 下一篇: 以讹传讹的小故事大道理