IAP的原理和stm8的IAP

179 篇文章 193 订阅

一、引出(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的中断就用不了了。

二、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); 
}

 

        改写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窗口上看可能并未改变,但实际上已经被改写了。(本人被坑了半天)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

【ql君】qlexcel

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值