目录
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区域是项目的逻辑实现和功能实现。
STM32 IAP (In-Application Programming) 介绍STM32 IAP(In-Application Programming)是一种允许应用程序在运行时更新或编程内部Flash存储器的技术。通过IAP,您可以在不使用外部编程器的情况下,直接从应用程序代码中对STM32的内部Flash进行读取、写入和擦除操作。这对于实现固件升级、数据存储等功能非常有用。
- IAP 的应用场景
•固件升级:通过IAP,您可以实现OTA(Over-The-Air)固件升级,允许设备在运行时从网络或其他来源下载并安装新的固件版本。
•数据存储:IAP可以用于将用户数据、配置参数等存储到Flash中,确保数据在断电后不会丢失。
•多应用管理:某些应用可能需要在同一片Flash中存储多个应用程序,并根据需要切换运行不同的应用程序。 - IAP 的工作原理IAP的核心是通过调用STM32 HAL库或标准外设库中的Flash编程函数来操作Flash存储器。STM32的Flash存储器分为多个扇区(Sector),每个扇区可以独立进行擦除和写入操作。IAP通常包括以下几个步骤:
•解锁Flash:在对Flash进行任何操作之前,必须先解锁Flash以允许编程操作。
•擦除Flash:在写入新数据之前,必须先擦除目标扇区。注意,擦除操作是以扇区为单位的,不能只擦除单个字节或字。
•写入数据:将新数据写入指定的Flash地址。写入操作是以半字(16位)或字(32位)为单位进行的。
•锁定Flash:完成所有操作后,重新锁定Flash以防止意外修改。 - IAP 的实现方式IAP可以通过以下几种方式实现:3.1 Bootloader + Application 结构这是最常见的IAP实现方式。系统启动时,首先运行Bootloader程序,Bootloader负责检查是否有新的固件需要更新。如果有,则将新固件写入应用程序区域,然后跳转到应用程序执行。如果没有新固件,则直接跳转到应用程序。
•优点•
•Bootloader和应用程序分离,互不影响。
•可以实现安全的固件升级,避免损坏整个系统。
•支持多种升级方式(如UART、USB、SPI、Ethernet等)。
•缺点•
•需要额外的空间来存储Bootloader。
•Bootloader和应用程序之间的通信需要设计良好的协议。
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;
<