STM32實現IAP功能的學習筆記

最近因項目需求要實現STM32的在線升級即IAP功能,先將這幾天的學習體會和IAP的具體實現總結出來,分享給大家,希望對同樣實現IAP的童鞋有所幫助,文中最後會上傳名爲STM32_Update.zip的壓縮文件裏面包含了STM32_App,STM32_MyBoot_V1.0和升級軟件STM32_UpdateSoftware的源碼文件供大家參考。所有程序都經過測試,直接可以在原子哥的開發板上跑,上位機的升級軟件大家可以直接打開
STM32_Update \ STM32_UpdateSoftware \發佈\ STM32_UpdateSoftware.exe來升級,如果需要查看源碼請用VS2010打開工程文件。

最終要實現的是:
單片機每次上電會先運行啓動程序,檢查標誌位如果標誌位爲FLAG_TO_APP則直接跳轉到應用程序運行,如果標誌位爲FLAG_TO_BOOT,則運行啓動程序準備升級。在運行應用程序時,當接收到升級的指令後會在FLASH中的某處空間寫下升級的標誌位FLAG_TO_BOOT ,並且加載啓動程序,啓動程序 接受新的程序文件並且存儲在相應的FLASH空間裏,完成升級後會在標誌位的空間寫下FLAG_TO_APP,並且運行新的程序。

帖子包含如下幾個方面:
1。什麼是IAP?
2. STM32的啓動模式?
3. STM32的FLASH分佈?
4. STM32程序的運行過程?
5. BootLoader程序的編寫(如何實現程序的動態加載)?
6.程序的編寫?
7. bin文件的轉換?
8. 上位機串口升級軟件的簡介
-------------------------------------------------------------------------------------------------
1. 什麼是IAP?
IAP的知識網上的各種資料也說的比較明白,在此簡單介紹一下。IAP( In Application Programming)即在線應用編程,也就是用戶可以使用自己的程序對單片機的User Flash的某一區域(一般爲存放自己程序的區域)進行燒寫。在真正的工作中產品發佈後,可以很方便的使用預留的通信接口(串口、USB、網口、藍牙等)來完成程序的升級,從而避免了把機器拆開使用下載器燒寫程序。要實現IAP功能一般要設計兩部分代碼,一是BootLoader程序,這部分程序存儲在FLASH的某一位置,主要用來引導、升級App程序;二是App程序,這個程序纔是實現產品的功能程序。通過BootLoader來完成對App程序的更新升級,這就是IAP功能。
2. STM32的啓動模式
很多初學者對於STM32的啓動並不是很瞭解,這在《STM32的參考手冊》以及網上各種資料裏也有介紹,下面再簡單介紹一下:
STM32有三種啓動方式,主要是通過管腳BOOT0和BOOT1的連接方式來控制的,如下圖所示,因爲我們要讓程序從主存儲器啓動,因而在硬件                設計時要選擇第一種方式把BOOT0接到GND,BOOT1任意,可以拉高也可以拉低。

noteSTM32上電啓動並不是直接進入main函數,而是先進行系統初始化,這個函數的調用是在啓動文件startup_stm32f10x_hd.s(因爲我的單片機是STM32F103RCT6,大容量芯片所以是這個文件)中執行復位中斷Reset_Handler時被調用的,執行完復位中斷纔會進入main函數。
3.  STM32 FLASH的分佈
STM32每種型號單片機的FLASH大小及地址分配在芯片手冊裏都有介紹,我使用的是STM32F103RCT6的型號其FLASH爲256K屬於大容量產品其        
FLASH的分佈如下圖:主存儲塊的地址是從0x080000000x0803FFFF共256K


我們在設計程序時把FLASH分成3部分,第一部分從0x080000000x0800FFFF共64K來存放BootLoader程序,第二部分爲0x08010000        
0x0802FFFF共128K來存放App程序,第三部分從0x08030000開始到0x803FFFF共64K來存放程序運行的標誌位和其他,如下所示:

4. STM32程序的運行過程
STM32的程序運行過程在很多資料裏也都有介紹,因爲STM32F103的單片機是基於Cortex-M3核的,它的內部主要是通過中斷向量表來響應各種中斷,內部閃存的起始地址是0x08000000,中斷向量表的起始地址是0x8000004,程序啓動後,將首先從“中斷向量表”取出復位中斷向量執行復位中斷程序完成啓動,當中斷來臨時STM32 的內部硬件機制亦會自動將 PC 指針定位到“中斷向量表”處,並根據中斷源取出對應的中斷向量執行相應的中斷服務程序。

如上圖所示STM32的正常啓動流程是:
a. STM32上電後會從 0x8000004 處取出復位中斷向量的地址,並跳轉執行復位中斷服務程序,如標號1所示;
b. 復位中斷復位程序執行完成之後就會跳轉到我們的main函數如標號2所示;
c. main函數一般爲死循環,當其收到某一中斷請求之後STM32會強制把PC指針指向中斷向量表,如標號3所示;
d. 查詢中斷向量表,根據中斷源來跳轉到相應的中斷服務程序中執行響應的操作;如標號4、5所示;
e. 執行完中斷服務程序之後會再回到main函數中,如標號6所示。
以上是STM32的正常運行過程,而當加入IAP程序之後,運行流程就如下所示:

加入IAP後程序運行如下:
a. STM32復位之後還是從0x8000004處獲取中斷向量表的地址,並跳轉執行復位中斷服務程序,如標號1所示;
b. 執行完復位中斷服務程序之後回調轉到IAP的main函數中,如標號2所示;
c. IAP的過程就是通過某種選定的通信方式(如串口)來接收程序文件,並且存儲在指定的FLASH空間裏,隨後會加載新的程序,而新程序        
的復位中斷向量起始地址爲0X08000004+N+M,取出新程序的復位中斷向量的地址,並跳轉執行新程序的復位中斷服務程序,隨後跳轉
至新程序的 main 函數,如標號3、4所示;
d. 此時在STM32的FLASH裏面會有兩個中斷向量表,在新程序 main 函數執行的過程中,當中斷來臨時PC指針仍會回跳轉至地址爲
0x8000004 中斷向量表處,而並不是新程序的中斷向量表,這是由STM32的硬件機制決定的,如標號5所示;
e. 查詢中斷向量表,根據中斷源來跳轉到新的中斷服務程序中執行響應的操作,如標號6所示;
f. 執行完中斷服務程序之後會再回到main函數中,如標號7、8所示。
note:
由上可知新的程序在FLASH中必須放在IAP程序之後的某個地址裏,這裏我的程序中設置的是0x08010000 即偏移量爲0x10000,而且新程序
的中斷向量表也要做相應的偏移,偏移量也爲0x10000 (地址的設置可以通過編譯軟件來實現,下文會有介紹)。

5. BootLoader程序的編寫
   BootLoader程序主要的功能是接收新的程序並把它存儲在FLASH的特定位置,然後加載新的程序運行。單片機每次上電都會先讀取一個
標誌位,根據此標誌位來決定是運行APP程序還是來運行自己來升級。
flag = STMFLASH_ReadHalfWord(FLASH_ADDR_UPDATE_FLAG); (FLASH_ADDR_UPDATE_FLAG 是0x08030000的地址
當flag = FLAG_TO_APP 則加載App程序,否則執行升級程序。
在我的程序中通過串口來完成程序bin文件的傳輸,爲了通信安全制定通信協議,串口接收分爲兩種:
a. 指令的接收,長度爲16個字節,協議示例爲
test[16] = {55, aa, 01, 指令長度,命令碼,00,00,...00, 和校驗位}
和校驗位 = 0 - 前15字節的和,
b. 程序文件的接收,每包數據爲(2048 + 6)個字節,示例爲:
test[2054] = {55, aa, 01, 包號,命令碼,數據文件2048字節,和校驗位}
       之所以設置以上的通信協議就是爲了保證數據傳輸的正確性。
  • Boot程序的主函數
Boot程序的main函數裏主要是讀取標誌位flag根據flag的值來決定是加載現有的App程序還是運行自身的升級程序,在自身運行時會定時給上位機軟件發送BOOT準備完成的指令,告訴上位機我準備好了,並運行ReceiveUsartData();根據串口中斷裏的標誌信息來完成對指令和程序文件的接收。
int main(void)

  int flag = 3;
  int nCount = 0;
  delay_init();  
  uart_init(115200);
  LED_Init();
  TIM3_Init(99, 719);  //10ms定時
  flag = STMFLASH_ReadHalfWord(FLASH_ADDR_UPDATE_FLAG);  //讀取標誌位
  while(1)
  {        
    //FLASH_EraseAllPages();  //僅在擦除所有FLASH時打開
    if(flag == FLAG_TO_APP)
    {
        Iap_Load_App(FLASH_ADDR_APP);
    }        
    else
    {      
        ReceiveUsartData();   //串口接收
        if(Flag10MS == 1)  
        {          
            Flag10MS = 0; 
            nCount++;
            if(nCount == 10)  //100ms
            {
                nCount = 0;
                USARTxSendRespondToServer(USART1, SERIAL_CODE_STM32_UPDATE_PREPAR_BOOT_OK); //不能發送過快否則會有髒數據
                LED0 = !LED0;
            }                            
        }
    }
  }    
}
  • 串口初始化程序
  使用STM32的USART1,設置波特率爲115200、8位數據長度、1個停止位、無校驗位,        
  具體實現見源碼的uart_init()函數。
  • 串口中斷服務程序 void USART1_IRQHandler(void)
在串口中斷服務程序中主要是接收上位機升級軟件發過來的數據,當UpdateFlag置1時爲接收bin程序文件的數據,當UsartRxCodeCount        
  的計數等於每包傳輸的總字節數USART_RECEIVE_CODE_DATA_SIZE時,置接收完成標誌位UsartReceiveFlag = 1並且置NextPageFlag = 1
  跳出中斷去ReceiveUsartData()處理,把接收到的數據存儲在FLASH的指定位置,不斷循環直到文件全部接收完成。升級指令的接收方法
  相同,詳見代碼。
  (note:在中斷服務函數裏,儘量不要做其他的操作,只設定標誌位,具體的操作去外面的函數執行。
  • 重新加載代碼的程序
爲了實現Boot和App程序之間跳轉,則必須在升級完成之後重新加載新的程序文件,其中涉及到在C語言裏內嵌彙編語言,代碼如下:
void MSR_MSP(u32 addr) 
{
    //asm("MSR MSP, r0");  //使用Keil內嵌彙編時使用這兩句
    //asm("BX r14");
  __ASM("msr msp, r0");  //set Main Stack value 將主堆棧地址保存到MSP寄存器(R13)中
  __ASM("bx lr"); //跳轉到lr中存放的地址處。bx是強制跳轉指令 lr是連接寄存器,是STM32單片機的R14
}

typedef  void (*IapFun)(void);                                //定義一個函數類型的參數
IapFun JumpToApp; 

//跳轉到應用程序 AppAddr:用戶代碼起始地址.
void Iap_Load_App(u32 AppAddr)
{
        if(((*(vu32*)AppAddr)&0x2FFE0000)==0x20000000)        //檢查棧頂地址是否合法.
        { 
                JumpToApp = (IapFun)*(vu32*)(AppAddr+4); //用戶代碼區第二個字爲程序開始地址(新程序復位地址)                
                MSR_MSP(*(vu32*)AppAddr);                 //初始化APP堆棧指針(用戶代碼區的第一個字用於存放棧頂地址)
                JumpToApp();        //設置PC指針爲新程序復位中斷函數的地址,往下執行
        }
}
首先 if(((*(vu32*)AppAddr)&0x2FFE0000)==0x20000000) 的作用是檢查棧頂地址是否合法,(*(vu32*)AppAddr)是去除用戶程序首地
址裏面的數據,而這個數據就是用戶代碼的堆棧地址,而堆棧地址指向RAM,RAM的起始地址是0x20000000,因此可以用上免得語句判斷用
戶的堆棧地址是否合法。
當判斷棧頂地址合法之後取出新的復位中斷函數的地址即(vu32*)(AppAddr+4),並把它賦值給函數指針JumpToApp,然後調用
MSR_MSP()函數把主堆棧指針賦值給MSP寄存器,最後調用JumpToApp();來執行新的程序。
   (這裏涉及到函數指針的知識,一定要理解函數名本身就是該函數的入口地址,它的實質是一個地址。)
上面涉及到嵌入彙編的知識,可能講解不是很透徹感興趣的朋友可以參考《Cortex-M3 權威指南》獲取更多的瞭解。
  • 中斷向量表的設置和起始地址的設置(IAR軟件)
  在IAR軟件中設置程序的中斷向量表和程序的入口地址的方法如下:
1. 打開工程,在工程名STM32_BOOT_v1.0上右鍵--Options

2. 選擇Linker--Edit.

3. 設置中斷向量表的地址Vector Table 和 Memory Regions的值


6. App程序的編寫
App程序相對簡單,它主要包含兩部分,一是程序要實現的主體功能(比如點亮LED),主要是你想讓App做什麼就實現什麼;二是通過串口來查詢升級指令,當收到升級的命令後要在FLASH_ADDR_UPDATE_FLAG 的地址處寫入FLAG_TO_BOOT的標誌位,並且調用Iap_Load_App()l加載運行BootLoader的程序來完成升級,詳細請看源碼。
對於App程序的要設置其中斷向量表的偏移通過語句 SCB->VTOR = FLASH_BASE | FLASH_VTOR_OFFSET;來實現,FLASH_VTOR_OFFSET這個變量在程序中是 #define FLASH_VTOR_OFFSET  ((uint32_t)0x10000) 因爲我們的App程序存儲地址是0x08010000相對於0x08000000來說偏移量即爲0x10000,而且在程序編譯時要設置Vector Table 和 Memory Regions的值爲0x08010000

7. bin文件的轉換
升級程序時編譯出的程序文件最好選用bin格式的文件,因爲bin文件比hex文件要小的多從而佔用的FLASH更小,這是比較主觀的優點,使用IAR軟件編譯時可以通過對軟件的設置來輸出bin格式的可執行文件,設置如下:
a. 打開工程的Options選項卡選擇選擇Output Converter


    b. 在Output format選項中選擇 binary格式,同時把Override default輸出文件的後綴改爲.bin,這樣在相應的工程目錄下(我的是 
       STM32_App\Project\EWARM5\Debug\Exe) 路徑下就可以找到編譯輸出的bin格式的可執行文件了。
8. 上位機升級軟件的簡介
   我的上位機升級軟件是使用C++寫的,具體編碼不做介紹了,想了解的朋友可以參考源碼。對話框界面如下:

首先設置端口號波特率,然後連接串口,連接成功之後,點擊“選擇要升級的文件”來實現升級。

升級完成之後會提示“升級完成”。

     到此我的IAP實現介紹完成,如果大家有什麼問題或者我的程序中大家發現了什麼bug可以提出來一起探討,希望以上內容會對大家學習STM32有所幫助。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章