STM32:Bootloader(AB备份,自动回滚)
目录
1.STM32的升级方式
1、ICP:In Circuit Programing,简单说就是在单片机开发时使用烧录器升级程序,比如使用J-Link烧录单片机程序。
2、ISP:In System Programing,在单片机内部实现了基于通信接口(如串口、I2C、SPI等等)的FLASH引导程序,配合厂家提供的烧录软件工具或自行开发的软件实现程序烧录。
3、IAP:In applicating Programing,是指单片机程序开发好之后在运行过程中由外部用户发起的在线升级,这种升级方式一般由用户自行设计升级方案,方案灵活性和自由度较高,在智能家居、汽车电子、物联网设备中常用的OTA(Over The Air)即空中下载技术原理也与之类似。
2.IAP升级
ICP和ISP需要连接才能更新固件,实际的交付客户的产品肯定不行,需要用到IAP,STM32的IAP升级,需要用到Bootloader,需要两段代码一段boot,一段app,boot区域负责更新app区域的代码,而app区域是项目的逻辑实现和功能实现。
3.代码逻辑展示
3.1 划分存储区域
把下载的固件存储到一个固定位置,另外还需要把原先APP区的代码进行备份,避免升级过程中出现异常导致生成砖
//1M start
#define USER_FLASH_FIRST_PAGE_ADDRESS 0x000000 //
#define USER_FLASH_LAST_PAGE_ADDRESS 0x0FFFFF //
#define USER_FLASH_END_ADDRESS 0x0FFFFF
//1M end
//3M start
#define APP_FLASH_BACKUP_FIRST_PAGE_ADDRESS 0x200000 //
#define APP_FLASH_BACKUP_LAST_PAGE_ADDRESS 0x27FFFF //
#define APP_FLASH_BACKUP_END_ADDRESS 0x27FFFF
//3M end
3.2 IAP的状态
//IAP STATUS
typedef enum iap_status_code{
IAP_NO = 0,
IAP_START = 1,
IAP_COPY_APP_TO_BACKUP, //拷贝APP区域代码到备份区域
IAP_COPY_APP_TO_BACKUP_SUCCESS_STATUS, //拷贝APP区域代码 成功
IAP_COPY_APP_TO_BACKUP_FAIL_STATUS, //拷贝APP区域代码 失败
IAP_COPY_APP_TO_BACKUP_CHECK_MD5_FAIL_STATUS, //拷贝APP区域代码 MD5校验失败
IAP_COPY_NEWBIN_TO_APP, //拷贝新的固件到APP区
IAP_COPY_NEWBIN_TO_APP_NO_DATA_STATUS,//新固件存储区域没有数据
IAP_COPY_NEWBIN_TO_APP_SUCCESS_STATUS,//成功
IAP_COPY_NEWBIN_TO_APP_FAIL_STATUS,//失败
IAP_COPY_NEWBIN_TO_APP_CHECK_MD5_FAIL_STATUS,//MD5校验失败
IAP_BACK_TO_BACKUP_OLDBIN, //备份区域的旧固件恢复到APP区域
IAP_BACK_TO_BACKUP_OLDBIN_NO_DATA_STATUS,//备份区域没有数据
IAP_BACK_TO_BACKUP_OLDBIN_SUCCESS_STATUS,//成功
IAP_BACK_TO_BACKUP_OLDBIN_FAIL_STATUS,//失败
IAP_BACK_TO_BACKUP_OLDBIN_CHECK_MD5_FAIL_STATUS,//MD5校验失败
IAP_ERROR_TO_APP, //IAP 升级失败 跳转到APP区运行
IAP_END_TO_APP, //IAP 升级成功 跳转到APP区运行
}IAP_STATUS;
#define IAP_COPY_APP_SIZE 409600 //(400*1024) (448*1024)
为了避免升级过程中出现异常,需要对下载的文件进行MD5校验,此外未预防OTA文件有问题,添加多少次没有进入APP区域,代码自动回滚到上一个版本
4.源码分析
4.1 记录IAP的状态
记录上一次IAP的状态,避免中途断电,断网等异常导致OTA流程被打断后无法正常OTA
//读取OTA过程中上一次的iap状态
BSP_W25Qx_Read(&last_iap_status_flag,IAP_STATUS_FLAG_ADDRESS,1);
if(last_iap_status_flag == 0xFF || last_iap_status_flag > 20)
{
last_iap_status_flag = 0;
BSP_GD25QXX_Write(&last_iap_status_flag,IAP_STATUS_FLAG_ADDRESS,1);
}
4.2 APP代码异常判断
Boot区和APP区进入次数进行比较,差异超过设定的次数,表示APP区域代码有问题,需要回滚到上一版本的代码
in_boot_times_count();
get_in_app_times();
if(boot_app_times_compare() != 0)
{
iap_status = IAP_BACK_TO_BACKUP_OLDBIN;
last_iap_status_flag = IAP_BACK_TO_BACKUP_OLDBIN;
LOG_INFO("in_boot_times.value - in_app_times.value >30 iap_status = IAP_BACK_TO_BACKUP_OLDBIN...\r\n");
}
4.3 IAP状态的切换以及异常的处理逻辑
switch(iap_status)
{
//不需要IAP
case IAP_NO:
LOG_INFO("IAP_NO...jump to app --->\r\n");
HAL_Delay(1000);
load_jump_to_app();
break;
//开始IAP的升级流程
case IAP_START:
LOG_INFO("IAP_START 1 : last_iap_status_flag = %d iap_status = %d\r\n",last_iap_status_flag,iap_status);
if(last_iap_status_flag > IAP_START && last_iap_status_flag < IAP_ERROR_TO_APP)
{
iap_status = last_iap_status_flag;
}
else
{
iap_status = IAP_COPY_APP_TO_BACKUP;
}
LOG_INFO("IAP_START 2 : last_iap_status_flag = %d iap_status = %d\r\n",last_iap_status_flag,iap_status);
break;
//拷贝app区域的内容到外部flash的备份区域
case IAP_COPY_APP_TO_BACKUP:
{
//把外部flash的备份区域的内容清空
if(Erase_back_flash_Sector_to_backup_app_area() != 0)
{
LOG_INFO("IAP_COPY_APP_TO_BACKUP Erase_back_flash_Sector_to_backup_app_area fail......\r\n");
iap_status = IAP_COPY_APP_TO_BACKUP_FAIL_STATUS;
}
else
{
LOG_INFO("IAP_COPY_APP_TO_BACKUP Erase_back_flash_Sector_to_backup_app_area success......\r\n");
error_code = copy_app_to_back_area(IAP_COPY_APP_SIZE);
//LOG_INFO("copy_app_to_back_area error_code = %d\r\n",error_code);
if(error_code == 0)
{
calculate_app_area_md5(now_app_md5_value,IAP_COPY_APP_SIZE);
LOG_INFO("IAP_COPY_APP_TO_BACKUP now_app_md5_value MD5: ");
for(uint16_t i =0;i<16;i++)
{
LOG_INFO("%02x",now_app_md5_value[i]);
}
LOG_INFO("\r\n");
calculate_back_area_md5(back_md5_value,IAP_COPY_APP_SIZE);
LOG_INFO("IAP_COPY_APP_TO_BACKUP back_md5_value MD5: ");
for(uint16_t i =0;i<16;i++)
{
LOG_INFO("%02x",back_md5_value[i]);
}
LOG_INFO("\r\n");
if(!compare_md5_2buff(now_app_md5_value,back_md5_value,16))
{
LOG_INFO("IAP_COPY_APP_TO_BACKUP_SUCCESS_STATUS......\r\n");
BSP_GD25QXX_Write(back_md5_value,APP_FLASH_BACKUP_MD5_VALUE_ADDRESS,16);
iap_status = IAP_COPY_APP_TO_BACKUP_SUCCESS_STATUS;
//BSP_GD25QXX_Write(&iap_status,IAP_STATUS_FLAG_ADDRESS,1);
}
else
{
LOG_INFO("IAP_COPY_APP_TO_BACKUP copy_app_to_back_area check is fail ---> BSP_Reset_system\r\n");
HAL_Delay(100);
iap_status = IAP_COPY_APP_TO_BACKUP_CHECK_MD5_FAIL_STATUS;
}
}
else
{
LOG_INFO("IAP_COPY_APP_TO_BACKUP copy_app_to_back_area copy is fail ---> BSP_Reset_system\r\n");
HAL_Delay(100);
iap_status = IAP_COPY_APP_TO_BACKUP_FAIL_STATUS;
}
}
}
break;
case IAP_COPY_APP_TO_BACKUP_SUCCESS_STATUS:
{
iap_status = IAP_COPY_APP_TO_BACKUP_SUCCESS_STATUS;
BSP_GD25QXX_Write(&iap_status,IAP_STATUS_FLAG_ADDRESS,1);
iap_status = IAP_COPY_NEWBIN_TO_APP;
}
break;
case IAP_COPY_APP_TO_BACKUP_FAIL_STATUS:
iap_copy_app_to_backup_error_cnt++;
if(iap_copy_app_to_backup_error_cnt >= 10)
{
iap_status = IAP_COPY_APP_TO_BACKUP_FAIL_STATUS;
BSP_GD25QXX_Write(&iap_status,IAP_STATUS_FLAG_ADDRESS,1);
iap_status = IAP_ERROR_TO_APP;
}
else
{
iap_status = IAP_COPY_APP_TO_BACKUP;
}
break;
case IAP_COPY_APP_TO_BACKUP_CHECK_MD5_FAIL_STATUS:
iap_copy_app_to_backup_error_cnt++;
if(iap_copy_app_to_backup_error_cnt >= 10)
{
iap_status = IAP_COPY_APP_TO_BACKUP_CHECK_MD5_FAIL_STATUS;
BSP_GD25QXX_Write(&iap_status,IAP_STATUS_FLAG_ADDRESS,1);
iap_status = IAP_ERROR_TO_APP;
}
else
{
iap_status = IAP_COPY_APP_TO_BACKUP;
}
break;
//把下载的bin文件写入到app的空间区域
case IAP_COPY_NEWBIN_TO_APP:
LOG_INFO("IAP_COPY_NEWBIN_TO_APP start\r\n");
//拷贝前先查看备份区域的数据的完整性
memset(back_download_area_md5_value,0,16
原文地址:https://blog.csdn.net/weixin_43996145/article/details/144214911
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!