AT32F415的OTA升级
- 项目简介
- IAP实现原理
- AT32中的内置FLASH分配情况
- AT32中的实现过程
- 跳转到APP的程序代码
- 删除APP区域的FLASH空间代码
- 写APP的bin数据到FLASH空间代码
项目简介
在物联网应用开发过程中,不可避免的会需要用到软件升级,一般情况下,以AT32系列芯片为主控制器的设备在出厂时就已经使用J-Link仿真器将应用代码烧录了,如果在设备使用过程中需要进行应用代码的更换、升级等操作的话,则可能需要将设备返回原厂并拆解出来再使用J-Link重新烧录代码,这就增加了很多不必要的麻烦。站在公司的角度来说,就是能远程来更换设备里边的代码程序。IAP却能很好的解决掉这个难题。
IAP实现原理
一片ARM芯片的Code(代码)区内一般只有一个用户程序。而IAP方案则是将代码区划分为两部分,两部分区域各存放一个程序,一个叫bootloader(引导加载程序),另一个较user application(用户应用程序)。bootloader在出厂时就固定下来了,在需要变更user application时只需要通过触发bootloader对userapplication的擦除和重新写入即可完成用户应用的更换。如图所示
在程序执行初始进入bootloader,在bootloader里面检测条件是否被触发(串口是否接收到特定的数据),如果有则进行对user application进行擦除和重新写入操作,如果没有则直接跳转到user application执行应用;如果有则进行擦除用户代码并重新写入新的用户代码。
AT32中的内置FLASH分配情况
AT32程序启动顺序如下图所示
AT32中的实现过程
BootLoader流程图大致应该如下:
1、初始化时钟。
2、初始化中断向量表地址。
3、初始化串口。
4、检测串口是否收到0xab,是则执行步骤5,否则执行步骤9。
5、擦除用户程序(擦除0x08004000—0x0803ffff地址空间Flash)。
6、从串口读取新的用户代码数据,把代码写入用户程序空间。
7、检测串口数据接收完毕?是则执行步骤9,否则跳回步骤7。
8、用户程序更新完毕,等待重新上电或硬件复位。
9、跳转到用户程序(强制将PC指针跳转到0x08004000+4处)。
跳转到APP的程序代码
void jump_to_app(uint32_t address)
{
uint32_t stkptr, jumpaddr;
stkptr = *(uint32_t*)address;
jumpaddr = *(uint32_t*)(address + sizeof(uint32_t));
//中断失能,串口1为程序下载口,串口2为日志打印口
nvic_irq_disable(USART1_IRQn);
nvic_irq_disable(USART2_IRQn);
__NVIC_ClearPendingIRQ(USART1_IRQn);
__NVIC_ClearPendingIRQ(USART2_IRQn);
//关闭串口外设时钟
crm_periph_clock_enable(CRM_USART1_PERIPH_CLOCK, FALSE);
crm_periph_reset(CRM_USART1_PERIPH_RESET, TRUE);
crm_periph_reset(CRM_USART1_PERIPH_RESET, FALSE);
crm_periph_clock_enable(CRM_USART2_PERIPH_CLOCK, FALSE);
crm_periph_reset(CRM_USART2_PERIPH_RESET, TRUE);
crm_periph_reset(CRM_USART2_PERIPH_RESET, FALSE);
//设置跳转地址
__set_MSP(stkptr);
//设置APP程序地址
pftarget = (void (*)(void))jumpaddr;
//执行APP程序
pftarget();
}
删除APP区域的FLASH空间代码
void DelAppCodeArea(void)
{
int page_index;
//flash记得解锁,我在调试的时候忘记解锁,导致失败
flash_unlock();
for (page_index = 0; page_index <= FLASH_APP_PAGE_COUNT; page_index++)
{
flash_sector_erase(FLASH_APP_ADDRESS + page_index * PAGE_SIZE);
wdt_counter_reload();
}
//操作完记得加锁
flash_lock();
}
写APP的bin数据到FLASH空间代码
void writeToFlash(uint8_t *pdata, uint32_t len)
{
flash_unlock();
uint32_t remainLen, i;
wdt_counter_reload();
remainLen = PAGE_SIZE - codeCacheIndex; // codeCache剩余空间
// codeCache还剩空间
if (remainLen > len)
{
memcpy(codeCache + codeCacheIndex, pdata, len);
codeCacheIndex += len;
}
else
{
memcpy(codeCache + codeCacheIndex, pdata, remainLen); // codeCache已经满了,写入flash
if (programPageSeq <= FLASH_APP_PAGE_COUNT)
{
flash_sector_erase(FLASH_APP_ADDRESS + programPageSeq * PAGE_SIZE);
for (i = 0; i < PAGE_SIZE; i += 2)
{
flash_halfword_program(FLASH_APP_ADDRESS + programPageSeq * PAGE_SIZE + i, *(uint16_t *)(codeCache + i));
wdt_counter_reload();
}
programPageSeq++;
}
codeCacheIndex = len - remainLen; // 剩余的data放入codeCache
memset(codeCache, 0xff, PAGE_SIZE);
if (codeCacheIndex)
memcpy(codeCache, pdata + remainLen, codeCacheIndex);
}
flash_lock();
}
详细工程可以直接联系我获取。