最近做了IAP U盘升级模块开发,总结下IAP基本开发流程,不深入讨论原理。
详细原理参考
首先需要知道我们需要把之前的APP区域拆一块出来做BOOT升级程序区域。
以STM32F103为例,0x08000000到0x0807FFFF为FLASH空间,即上图代码区域。那么可以以此划分APP区域和BOOT区域
而具体怎么划分,可以用JLINK读取当前芯片BOOT程序占用了多少空间,比如占用了0x8960字节,后面都是FF 即 空。
那么我们可以给BOOT划分0x08000000~0x0800C000的地址空间(方便举例,可以不用余这么多)
这里好像需要注意得是4的整数倍,
部分芯片如LPC177X会涉及到扇区与地址对应,但思路是一样的,参考这篇
这里用了1K即0x400做校验地址空间(0X0800C000~0X0800C3FF),但这不是必须的,也可以不要
#define APP_START_ADDR 0x0800C400UL //业务程序起始地址
#define APP_END_ADDR 0x0807FFFFUL //业务程序结束地址
#define CRC_SAVE_START_ADD 0X0800C000UL //CRC程序校验码存放开始地址
#define CRC_SAVE_END_ADD 0X0800C3FFL //CRC程序校验码存放结束地址,共1k
现在0x08000000~0x0800C000放boot,
0X0800C000~0X0800C3FF放校验,
那么剩下的都可以做APP空间啦
0X0800C400~0X0807FFFF爽用,注意ROM右边是SIZE不是截止地址,这个做个十六进制减法就好了。
那么地址配置现在做完了,接下来就是跳转。
由BOOT跳转到APP区域有以下两个即可,jumpToApp
是在main里跳,如果用了Freertos在任务中跳就用jumpToAppInTask
void jumpToApp(uint32_t appBaseAddr)
{
void (*firmwareFunc)(void);
uint32_t fwStackVal = *((uint32_t *)(appBaseAddr)); /* the first word is for the stack pointer. */
uint32_t fwEntryVal = *((uint32_t *)(appBaseAddr+4U)); /* the second works is for the boot function. */
firmwareFunc = (void (*)(void))fwEntryVal;
SCB->VTOR = appBaseAddr; /* The stack address is also the start address of vector. */
__set_MSP(fwStackVal);
__set_PSP(fwStackVal);
firmwareFunc();
}
void jumpToAppInTask(uint32_t appBaseAddr)
{
void (*firmwareFunc)(void);
SysTick->CTRL = 0X00;//禁止SysTick
SysTick->LOAD = 0;
SysTick->VAL = 0;
__disable_irq();
uint32_t fwStackVal = *((uint32_t *)(appBaseAddr)); /* the first word is for the stack pointer. */
uint32_t fwEntryVal = *((uint32_t *)(appBaseAddr+4U)); /* the second works is for the boot function. */
firmwareFunc = (void (*)(void))fwEntryVal;
SCB->VTOR = appBaseAddr; /* The stack address is also the start address of vector. */
__set_MSP(fwStackVal);
__set_PSP(fwStackVal);
firmwareFunc();
}
需要注意在APP main开头加上SCB->VTOR = APP_START_ADDR;
以保证正确跳转
APP跳BOOT只需要NVIC_SystemReset();
即可
跳转逻辑可以参考
地址配置,跳转都完成后就是升级数据接收,Flash擦除与写入了
主要逻辑是在BOOT程序中擦除APP空间的FLASH,然后再可以1K 1K的写入,当然擦1K写1K也可以
具体接收逻辑就自己定义就可以了,比如收一包回一包,加序号都可以的