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 》

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值