打印
[STM8]

IAP的原理和stm8的IAP

[复制链接]
418|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
wowu|  楼主 | 2021-6-4 15:18 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
一、引出(IAP的原理和stm8上实现IAP的问题)

        具有IAP功能的单片机,程序可以分为两部分:IAP和APP。APP是用来实现真正功能的程序,而IAP是用来远程重新编程APP的程序。单片机上电时会先执行IAP程序,在IAP中判断APP是否正常,然后再跳转到APP中执行。

        这样就会有一个问题,那就是中断向量表的问题。当发生中断时,单片机会去中断向量表中查询中断服务函数的地址,然后才能按照地址跳转到中断服务函数去执行。中断向量表一般都在程序的开头,当把编译好的IAP下载到单片机里,单片机上电执行IAP程序,在执行IAP时发生中断,单片机就会到IAP的开头去查询中断服务函数地址,然后当从IAP跳转到APP后,开始执行APP程序,这个时候发生中断,单片机仍然会到IAP的开头去查询中断服务函数地址,这里就出现问题了。单片机在执行APP时,应该到APP的开头去查询中断服务函数地址才对。

        因此需要某种方法通知单片机,当我们执行APP时,要在APP的开头去查询中断服务函数地址。在stm32单片机中,只需要在执行APP程序前加一句:

SCB->VTOR = FLASH_BASE | 0x4000;   //0x4000是APP的开始地址
因为stm32有地址偏移寄存器SCB->VTOR,当发生中断时,stm32会去SCB->VTOR读取中断向量表的地址。执行上面的语句后,发生中断时就会到FLASH_BASE | 0x4000地址去查询中断服务函数地址。

        而stm8没有地址偏移寄存器,所以我们需要其他的办法来解决。中断向量表实际上就是:跳转指令+地址。当发生中断单片机会去执行中断向量表,也就是执行跳转指令,跳转到目标地址。明白了这个,那我们的方法也就出来了:改写IAP的中断向量表,把它的地址改为APP的中断向量表的地址。这样,当在APP中发生中断,单片机会跳转到IAP的开头去执行中断向量表,然后跳转到APP的中断向量表,最后才跳转到APP的中断服务函数。当然,这样一来IAP的中断就用不了了。



使用特权

评论回复
沙发
wowu|  楼主 | 2021-6-4 15:19 | 只看该作者
二、stm8的IAP

        stm8的Flash是字节编程的,而且不需要先擦除再写,可以直接写。因此stm8的IAP处理流程为:进入IAP---解锁Flash---接收APP数据写Flash---改写IAP的中断向量表---Flash上锁----跳转到APP。

        读函数、解锁、上锁、写函数为:

//读出一个32位数
uint32_t FLASH_Read(uint32_t Address)  
{
    return(*(PointerAttr uint32_t *) (uint16_t)Address);      
}

//解锁Flash,在写Flash前,只需要在IAP中调用一次
void FLASH_Unlock(FLASH_MemType_TypeDef FLASH_MemType)  
{
    FLASH->PUKR = FLASH_RASS_KEY1;
    FLASH->PUKR = FLASH_RASS_KEY2;
}

//Flash上锁,写完Flash后,调用一次
void FLASH_Lock(FLASH_MemType_TypeDef FLASH_MemType)
{
    FLASH->IAPSR &= (uint8_t)FLASH_MemType;
}

//往Flash中写入一个8位数
void FLASH_ProgramByte(uint32_t Address, uint8_t Data)
{
    *(PointerAttr uint8_t*) (uint16_t)Address = Data;
}

//往Flash中写入一个32位数
void FLASH_ProgramWord(uint32_t Address, uint32_t Data)
{
    /* Enable Word Write Once */
    FLASH->CR2 |= FLASH_CR2_WPRG;
    FLASH->NCR2 &= (uint8_t)(~FLASH_NCR2_NWPRG);

    /* Write one byte - from lowest address*/
    *((PointerAttr uint8_t*)(uint16_t)Address) = 0x00;
    *((PointerAttr uint8_t*)(uint16_t)Address) = *((uint8_t*)(&Data));
    /* Write one byte*/
    *(((PointerAttr uint8_t*)(uint16_t)Address) + 1) = 0x00;
    *(((PointerAttr uint8_t*)(uint16_t)Address) + 1) = *((uint8_t*)(&Data)+1);
    /* Write one byte*/   
    *(((PointerAttr uint8_t*)(uint16_t)Address) + 2) = 0x00;
    *(((PointerAttr uint8_t*)(uint16_t)Address) + 2) = *((uint8_t*)(&Data)+2);
    /* Write one byte - from higher address*/
    *(((PointerAttr uint8_t*)(uint16_t)Address) + 3) = 0x00;
    *(((PointerAttr uint8_t*)(uint16_t)Address) + 3) = *((uint8_t*)(&Data)+3);
}


使用特权

评论回复
板凳
wowu|  楼主 | 2021-6-4 15:19 | 只看该作者
改写IAP向量表的函数为:

//重新初始化STM8的中断向量表  把它重新定义到APP的中断向量中
void STM8_HanderIqr_Init(void)
{
    uint8_t Index;

    disableInterrupts();   //关闭中断          
    FLASH_Unlock(FLASH_MEMTYPE_PROG);
    for(Index = 1; Index < 32;Index++)
    {
        FLASH_ProgramWord(0x8000+4*Index,0x82000000+APPLICATION_ADDRESS+Index*4);
    }
    FLASH_Lock(FLASH_MEMTYPE_PROG);
}


        跳转到APP的函数为:

void JPMainProgram(void)
{
    //跳转至APP
    asm("LDW X,  SP ");
    asm("LD  A,  $FF");
    asm("LD  XL, A  ");
    asm("LDW SP, X  ");
    asm("JPF $9000");     //0x9000是APP的地址,根据自己的情况来改
}


最后:

stm8的Flash很小,建议使用寄存器操作,不要用库函数,否则不好控制代码大小。

IAR的SWIM仿真对于Memory的支持不是很好,当调用上面的函数改写Flash后,从Memory窗口上看可能并未改变,但实际上已经被改写了。


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

91

主题

4082

帖子

1

粉丝