运行环境
1.Windows10 2. Keil5(MDK5) Version 5.27.0.0 3. MCU GD32E230C8T6
简介
本例程主要分析在线升级(OTA)的实现过程, 主要是针对IAP OTA从原理分析, 分区划分, 到代码编写和实验验证等过程阐述这一过程. 和大家以前理解OTA的原理. 作者也是通过已有的网上DEMO来理解编写,所以只是提供个人的理解思路。有需要优化的地方望大家指教。
程序的起始地址
正常情况下, 我们写的程序都是放在GD32片内Flash中(暂不考虑外扩Flash). 我们写的代码最终会变成二进制文件, 放进Flash中 。起始地址在0x08000000。
进行分区
64KBFlash MCU分区方案 Flash 空间划分出 4 个区域:Bootloader、FLAG、APP 分区、APPBAK 分区。
总体流程图
参考此流程图(可更具实际情况自己更改) 先执行BootLoader程序, 先去检查FLAG区有没有升级标志, 如果有就将APPBAK区(备份区)的程序拷贝到APP区, 然后再跳转去执行APP的程序. 然后执行APP程序, 因为BootLoader和APP这两个程序的向量表不一样, 所以跳转到APP之后第一步是先去更改程序的向量表. 然后再去执行其他的应用程序. 在应用程序里面会加入程序升级的部分, 这部分主要工作是拿到升级程序, 然后将他们放到APPBAK区(备份区), 以便下次启动的时候通过BootLoader更新APP的程序. 流程图如下图所示: 此图出自https://gitee.com/leafguo
Bootloader 程序
Bootloader 的主要职能是在有升级任务的时候将 APPBAK 分区里面的固件拷贝到 APP 区域。当然,这期间需要做很多的工作,比如升级失败的容错等等。具体的流程可以参考图示。需要注意的是,在校验 MD5 正确后开始搬运固件数据期间,MCU 出现故障(包括突然断电),MCU 应发生复位操作(FLAG 区域数据未破坏),复位后重新开始执行 Bootloader,从而避免 MCU 刷成板砖。(可以根据难度进行裁剪)
void execute_user_code(void)
{
uint32_t JumpAddress;
JumpAddress = *(__IO uint32_t*) (APP_CODE_OFFSET+ 4);
usart_disable(USART0);
__set_MSP(*(__IO uint32_t*) APP_CODE_OFFSET);
(*( void (*)( ) )JumpAddress) ();
}
在需要跳转的地方执行这个函数就可以了execute_user_code();
#define BOOTLOADER (0x08000000)
#define UPDATE_FLAG (0x08002C00)
#define UPDATE_FLAG_MAX_SIZE (0x0400)
#define APP_CODE_OFFSET (0x08003000)
#define APP_CODE_SIZE (0x6800)
#define UPDATE_CODE_OFFSET (0x08009800)
#define UPDATE_CODE_SIZE (0x6800)
#define FLASH_SECTOR_SIZE (0x400)
Bootloader 编译设置
BootLoader的代码默认是最开始的所以不需要特别设置代码的下载位置 按照下图, 修改擦除方式为Erase Sectors, 大小限制在0X2C00(11KB)
APP 分区部分
做好 Bootloader 工作后,我们开始写 APP 分区的代码。APP 分区固件的编写要注意硬件版本号和软件版本号,软件版号作为升级迭代很重要的标志。APP 分区代码我们只需要增加从网络接口接收到新固件到APPBAK区(备份区)功能,并给更新标志区写入相应数据。需要注意的是,中断向量地址偏移的定义,我们 APP 分区实际的偏移是 0x3000。如果不修改,APP 分区也可以正常加载运行,但是不会相应中断。所以,我们需要根据实际 APP 分区下载的起始地址,对中断向量地址偏移做定义。按照协议规定,具体如下:
- 修改中断向量表
nvic_vector_table_set (BOOTLOADER,0x3000);
APP 编译设置
因为硬件 FLASH 空间限定,我们需要对 APP 分区的固件大小做严格的限制。本例程可允许的最大固件为 26KB。需要升级的新固件同样最大可支持 26KB。
重点步骤
- 程序的跳转
- APP中的修改中断向量表
- Flash内存的操作(重点)
int main(void)
{
systick_config();
usart_gpio();
usart_init();
uint32_t temp;
while(1){
printf("current version:"BOOTLOAD_VERSION"\n");
temp=option_byte_value_get(UPDATE_FLAG);
if(43690 == temp){
printf("Enter the firmware upgrade process\n");
copy_updata();
fmc_unlock();
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGERR);
fmc_page_erase(UPDATE_FLAG);
fmc_lock();
usart_disable(USART0);
execute_user_code();
}
else{
printf("Firmware upgrade failed, jump to app\n");
execute_user_code();
}
}
}
中间的Flash操作各MCU大体相同 故参考相关例程 此文章仅个人的思路及理解,如有需要优化的地方大家提出,共同优化。
|