利用bootloader代碼能夠實現遠程代碼更新。
要清楚的認識bootloader,我們就要先了解stm32正常程序運行流程。
在stm32中將所有的中斷做成了一張中斷向量表(其實就像是一張表格),由上圖可知由棧頂地址向下,我們可以大致分爲三個部分。
1、中斷向量表
2、各個中斷程序入口。
3、main函數入口。
整個單片機上電運行的流程爲:
上電------復位(從中斷向量表中找到復位中斷向量)-----執行復位中斷服務程序-----執行main函數程序-----main函數中發生中斷內請求----PC指針強制跳轉到中斷向量表處找到相應的中斷向量-------執行對應的中斷服務程序------返回mian函數
以上就是整個stm32上電運行main函數的流程。
當我們加入bootloader程序之後,在我們單片機的flush裏面實際有兩個或者多個程序(程序個數取決於flush大小),每個程序有着自己的mian函數,我們的多個程序分別放在flush的不同位置,我們的bootloader放在0X08000000(即是首地址處),其餘的程序(app)緊隨bootloader之後。
加入bootloader之後的啓動流程如下圖:
由圖我們可以看出每個程序都有着自己的中斷向量表等等。。。多個程序的驅動流程和單個的大致相似。。只是在app中發生中斷時PC指針指向的是0X08000004這個位置的中斷向量表,而非app本身的中斷向量表處,,這裏的注意,,在執行相應的中斷程序時是跳轉到app自己的中斷程序表位置,,,這裏的注意。。。
由此,我們利用bootloader實現代碼的更新無非就是在bootloader裏面將接受到的代碼下載到指定的flush區域替換掉原先的代碼,然後將PC指針強制指向app棧頂地址處即可實現代碼的跟新。。。。。
同理我也嘗試過在app中強制的將pc指針指向另外的app也能實現兩個應用程序之間的切換。。。。
bootloader中主要代碼爲跳轉程序:
//跳轉到應用程序段
//appxaddr:用戶代碼起始地址.
void iap_load_app(u32 appxaddr)
{
if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //檢查棧頂地址是否合法.
{
jump2app=(iapfun)*(vu32*)(appxaddr+4); //用戶代碼區第二個字爲程序開始地址(復位地址)
MSR_MSP(*(vu32*)appxaddr); //初始化APP堆棧指針(用戶代碼區的第一個字用於存放棧頂地址)
jump2app(); //跳轉到APP.
}
}
注意:
if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) :判斷棧頂值是否合法,若沒有這一句的話,即使沒有下載程序也會進入而導致跑飛。
其中appxaddr表示棧頂地址中的值是棧頂地址中的值而非地址。 棧頂合法值爲0x20000000--0x20020000
如何查看一個程序的棧頂值,可以在工程中查看map文件至於怎麼查看map文件自行百度。。。。
這是我們app項目生成的map文件可以看到我們的棧頂值爲0x2000b360,是一個合法的棧頂值。
jump2app=(iapfun)*(vu32*)(appxaddr+4); //用戶代碼區第二個字爲程序開始地址(復位地址)
appxaddr:棧頂地址+4表示的是指向中斷向量表位置(也是復位地址)處。
其中iapfun,jump2app都是一個函數指針
typedef void (*iapfun)(void); //定義一個函數類型的參數.
彙編 初始化堆棧指針 MSR_MSP(*(vu32*)appxaddr); //初始化APP堆棧指針(用戶代碼區的第一個字用於存放棧頂地址)
樓主實力有限。。。不懂彙編所以這裏也不做解釋了。。。。照搬
jump2app():把用戶代碼的復位地址付給PC指針,我看到jump2app()這句代碼debug的時候對應的彙編代碼是
LDR r0,[pc,#12] ;相對PC的數據加載到函數指針的地址
LDR r0,[r0,#00] ;R0做索引,無偏移,數據裝載到R0,這個內容就是函數指針指向的內容,也就是函數的地址了,用戶程序的起始地址;
BLX r0 ;這個不解釋,說了是跳轉
到此整個bootloader的開發流程大致清楚,就可以利用bootloader實現app更新等一系列項目開發。。。
除了使用按鍵跟新程序,,我們也可以通過標誌位等其他方式更新app。。。