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