欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

IAP的原理和stm8的IAP

程序员文章站 2022-03-16 18:51:28
...

一、引出(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,当执行上面的语句后,发生中断时就会到FLASH_BASE | 0x4000地址去查询中断服务函数地址。

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

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

 

相关标签: MCU