1. 程式人生 > >STM32實現IAP功能的學習筆記

STM32實現IAP功能的學習筆記

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

最終要實現的是:
微控制器每次上電會先執行Boot程式,檢查標誌位如果標誌位為FLAG_TO_APP則直接跳轉到App程式執行,如果標誌位為FLAG_TO_BOOT,則執行Boot程式準備升級。在執行App程式時,當接收到升級的指令後會在FLASH中的某處空間寫下升級的標誌位FLAG_TO_BOOT,並且載入Boot程式,Boot程式會接受新的程式檔案並且儲存在相應的FLASH空間裡,完成升級後會在標誌位的空間寫下FLAG_TO_APP,並且執行新的程式。

帖子包含如下幾個方面:


1. 什麼是IAP?
2. STM32的啟動模式?
3. STM32的FLASH分佈?
4. STM32程式的執行過程?
5. BootLoader程式的編寫(如何實現程式的動態載入)?
6. App程式的編寫?
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(因為我的微控制器是
STM32F1
03RCT6,大容量晶片所以是這個檔案)中執行復位中斷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有所幫助。


 STM32_Update.zip (17.52 MB, 下載次數: 35934)