IAP(In Application Program)在線應用編程

要實現在線升級,MCU代碼須分爲 : bootloader和 用戶代碼App 兩個部分。

STM8下IAP程序的存儲方式

圖1.STM8下IAP程序的存儲方式

用戶啓動區域(UBC): (可理解爲用戶自定義的bootloader的存放區域)
包含有復位和中斷向量表,它可用於存儲IAP及通訊程序。UBC有一個兩級保護結構可保護用戶代碼及數據在IAP編程中免於無意的擦除或修改。這意味着該區域總是寫保護的,而且寫保護不能通過使用MASS密鑰來解鎖。它的大小可通過配置option bytes 設置。


1. 中斷向量表

STM8的中斷向量表的地址是固定的,位於0x8000~0x8080,即發生中斷時,程序將會跳轉至0x8000~0x8080的中斷向量表中尋找中斷入口地址,繼而跳轉至對應的中斷服務函數中執行中斷程序。
若是要實現IAP,而0x8000~0x8080的中斷向量表將會位於UBC區域,被bootloader所佔用。如此一來,UBC區域中的bootloader程序與MAIN PROGRAM區域中的App發生中斷時,程序同樣都是跳轉至UBC區域中的中斷向量表中尋找中斷入口地址。所以,若App要執行App自己的中斷服務函數,則須在App中建立自己的中斷向量表,並對bootloader中的中斷向量表進行重定向,將程序跳轉至App的中斷向量表。如圖2、圖3所示。

中斷跳轉示意圖

圖2.中斷跳轉示意圖

中斷跳轉流程圖
圖3.中斷跳轉流程圖

中斷向量表的重定向:(以STVD環境下的官方例程AN2659爲例)
以下重定向即可使App程序能夠找到自己對應的中斷服務函數,但是bootloader的中斷將不可使用

struct interrupt_vector const _vectab[] = {  
       {0x82, (interrupt_handler_t)_stext}, /* reset */  
       {0x82, NonHandledInterrupt}, /* trap  */  
       {0x82, NonHandledInterrupt}, /* irq0  */  
       {0x82, NonHandledInterrupt}, /* irq1  */  
       {0x82, NonHandledInterrupt}, /* irq2  */  
       {0x82, NonHandledInterrupt}, /* irq3  */  
       {0x82, NonHandledInterrupt}, /* irq4  */  
       {0x82, NonHandledInterrupt}, /* irq5  */  
       {0x82, NonHandledInterrupt}, /* irq6  */  
       {0x82, NonHandledInterrupt}, /* irq7  */  
       {0x82, NonHandledInterrupt}, /* irq8  */  
       {0x82, NonHandledInterrupt}, /* irq9  */  
       {0x82, NonHandledInterrupt}, /* irq10 */  
       {0x82, NonHandledInterrupt}, /* irq11 */  
       {0x82, NonHandledInterrupt}, /* irq12 */  
       {0x82, NonHandledInterrupt}, /* irq13 */  
       {0x82, NonHandledInterrupt}, /* irq14 */  
       {0x82, NonHandledInterrupt}, /* irq15 */  
       {0x82, NonHandledInterrupt}, /* irq16 */  
       {0x82, NonHandledInterrupt}, /* irq17 */  
       {0x82, NonHandledInterrupt}, /* irq18 */  
       {0x82, NonHandledInterrupt}, /* irq19 */  
       {0x82, NonHandledInterrupt}, /* irq20 */  
       {0x82, NonHandledInterrupt}, /* irq21 */  
       {0x82, NonHandledInterrupt}, /* irq22 */  
       {0x82, NonHandledInterrupt}, /* irq23 */  
       {0x82, NonHandledInterrupt}, /* irq24 */  
       {0x82, NonHandledInterrupt}, /* irq25 */  
       {0x82, NonHandledInterrupt}, /* irq26 */  
       {0x82, NonHandledInterrupt}, /* irq27 */  
       {0x82, NonHandledInterrupt}, /* irq28 */  
       {0x82, NonHandledInterrupt}, /* irq29 */  
   }; 

改爲

struct interrupt_vector const UserISR_IRQ[32] @ MAIN_USER_RESET_ADDR; //MAIN_USER_RESET_ADDR爲主程序區的首地址 的宏定義  
  //redirected interrupt table  
   struct interrupt_vector const _vectab[] = {  
       {0x82, (interrupt_handler_t)_stext}, /* reset */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+ 1)}, /* trap  */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+ 2)}, /* irq0  */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+ 3)}, /* irq1  */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+ 4)}, /* irq2  */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+ 5)}, /* irq3  */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+ 6)}, /* irq4  */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+ 7)}, /* irq5  */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+ 8)}, /* irq6  */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+ 9)}, /* irq7  */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+10)}, /* irq8  */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+11)}, /* irq9  */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+12)}, /* irq10 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+13)}, /* irq11 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+14)}, /* irq12 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+15)}, /* irq13 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+16)}, /* irq14 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+17)}, /* irq15 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+18)}, /* irq16 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+19)}, /* irq17 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+20)}, /* irq18 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+21)}, /* irq19 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+22)}, /* irq20 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+23)}, /* irq21 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+24)}, /* irq22 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+25)}, /* irq23 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+26)}, /* irq24 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+27)}, /* irq25 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+28)}, /* irq26 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+29)}, /* irq27 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+30)}, /* irq28 */  
       {0x82, (interrupt_handler_t)(UserISR_IRQ+31)}, /* irq29 */  
   }; 

其中,

typedef void @far (*interrupt_handler_t)(void);  //定義中斷函數地址類型

struct interrupt_vector {
  unsigned char            interrupt_instruction;    //指令,其中0x82爲跳轉指令
  interrupt_handler_t      interrupt_handler;    //函數地址
};

2. FLASH塊編程程序的存放

對於stm8的塊編程,代碼必須在Ram中運行,因爲在塊編程時Flash程序將會停止運行。因此,存儲在Flash中的代碼(與Flash編程相關的代碼)必須拷貝至Ram中進行編譯、鏈接、運行。這樣,程序將在Ram中執行塊編程,塊編程時Flash的狀態也不會影響到Ram中的程序繼續運行。
方法如下:

1. 代碼的編寫

將Flash編程相關的代碼存放至FLASH_CODE段:

#pragma section (FLASH_CODE)    //將代碼存放至RAM  
void Write_Flash(void)  
{  
    //....  
}  
void Erase_Flash(void)  
{  
    //....  
}  
#pragma section ()    //代碼放置至默認段 

注意,在調用這些函數之前,必須把這些代碼拷貝至RAM中, STVD的cosmic編譯器可以使用內置函數

int  _fctcpy(char name)實現此功能。 
int  _fctcpy(char name)    //name爲定義的段名首字母 

如本例中Flash塊編程所在的段名爲FLASH_CODE,其首字母爲 ‘F’ 故在調用這些函數之前須執行
_fctcpy('F');

2. STVD的設置

STVD--Settings--Linker選項卡下,選擇目錄Category下的Input,在Ram下新建SectionOption-ic,表示可移至RAM

這裏寫圖片描述

3. 程序的跳轉

若bootloader程序要跳轉到app,須將程序跳轉至主程序區的首地址。
可使用匯編指令完成:

{   
    //reset stack pointer (lower byte - because compiler decreases SP with some bytes)   
    _asm("LDW X, SP ");   
    _asm("LD A, $FF");   
    _asm("LD XL, A ");   
    _asm("LDW SP, X ");   

    //跳轉至App程序 :       
    _asm("JPF $A000");//這裏假設app首地址爲0x00A000。  
    /***或者可以***/  
    _asm("JPF [_MainUserApplication]");//跳轉至app程序首地址,其中_MainUserApplication的定義在下面有所解釋  
}   

其中:

//typedef @far void (*)(void) TFunction;  
typedef @far void (*TFunction)(void);  

//main application code (user reset) - init user code start - to interrupt table reset jump  
const TFunction MainUserApplication = (TFunction)MAIN_USER_RESET_ADDR;  

4. 參考資料

官方資料(STVD開發環境):例程AN2659,《 AN2659 Application Note.pdf 》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章