STM8的IAP在线升级

IAP(In Application Program)在线应用编程

官方资料(STVD开发环境):例程AN2659

                                   《 AN2659 Application Note.pdf 》

要实现在线升级,MCU代码须分为 : bootloader和 用户代码App 两个部分。

STM8的IAP在线升级_第1张图片

                               图1.STM8下IAP程序的存储方式

用户启动区域(UBC): (可理解为用户自定义的bootloader的存放区域)

   包含有复位和中断向量表,它可用于存储IAP及通讯程序。UBC有一个两级保护结构可保护用户代码及数据在IAP编程中免于无意的擦除或修改。这意味着该区域总是写保护的,而且写保护不能通过使用MASS密钥来解锁。它的大小可通过配置option bytes 设置。


一、中断向量表:

  STM8的中断向量表的地址是固定的,位于0x8000~0x8080,即发生中断时,程序将会跳转至0x8000~0x8080的中断向量表中寻找中断入口地址,继而跳转至对应的中断服务函数中执行中断程序。

  若是要实现IAP,而0x8000~0x8080的中断向量表将会位于UBC区域,被bootloader所占用。如此一来,UBC区域中的bootloader程序与MAIN PROGRAM区域中的App发生中断时,程序同样都是跳转至UBC区域中的中断向量表中寻找中断入口地址。所以,若App要执行App自己的中断服务函数,则须在App中建立自己的中断向量表,并对bootloader中的中断向量表进行重定向,将程序跳转至App的中断向量表。如图2、图3所示。

    STM8的IAP在线升级_第2张图片    STM8的IAP在线升级_第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;    //函数地址

};

二、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下新建Section,Option填-ic,表示可移至RAM。

STM8的IAP在线升级_第4张图片

三、程序的跳转

      若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;




 


你可能感兴趣的:(stm8)