nucleo-f411re学习记录-14,OTA 固件升级-bootloader与app分区实现

代码仓库

当设备需要升级时,若每次都需通过有线连接ST-Link烧录固件会非常不便。为解决这一问题,可引入类似手机系统升级的OTA技术。

OTA(Over-The-Air,空中下载)是通过无线网络(蓝牙/WiFi/4G等)远程更新设备软件的技术,无需拆机或接线即可完成固件升级。

由于程序运行时无法擦写自身所在的Flash区域,因此需要采用bootloader-app分区:

  • bootloader,因其位于首地址0x8000000,设备启动时首先运行,负责擦写app区域,完成擦写后跳转至app

  • app,实现所需功能,也负责接收升级数据包,然后将数据写入指定存储区域

完整的 OTA 升级流程

c 复制代码
1. App 运行中
   ↓ 收到升级包
2. App 将新固件存入外部 Flash 或预留分区
   ↓ 设置标志位,软复位
3. Bootloader 启动
   ↓ 检测到升级标志
4. Bootloader 将新固件写入 App 区域
   ↓ 校验文件,清除标志
5. Bootloader 跳转到新 App
   ↓
6. 升级完成!

已完成bootloader与app的基础功能实现:当bootloader检测到app起始地址非0xFFFFFFFF数据时,即可执行跳转操作。

bootloader实现

起始地址设置,存放第0扇区,大小16KB,目前一个扇区大小即可。第1扇区留着后续实现更多功能在打开,app起始位置在第2扇区。

跳转函数。

c 复制代码
// 函数指针类型定义
typedef void (*pFunction)(void);
// App 起始地址
#define APP_START_ADDR 0x8008000
void JumpToApp(void)
{
    // 1. 检查栈顶地址是否合法(防止跳转到空地址)
    if ((*(__IO uint32_t*)APP_START_ADDR) == 0xFFFFFFFF) {
        // 地址为空,不跳转
        return;
    }

    // 2. 关闭所有外设中断
    __disable_irq();
    
    // 3. 关闭 SysTick 中断,清零计数器
    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;

    // 4. 复位所有外设时钟,如串口之类
    // 注意:不要复位调试相关的外设,否则 ST-LINK 会断连

    // 5. 设置主堆栈指针 (MSP) 为 App 的第一个字
    __set_MSP(*(__IO uint32_t*)APP_START_ADDR);

    // 6. 获取 App 的复位向量地址(App 起始地址 + 4 字节偏移)
    uint32_t jumpAddr = *(__IO uint32_t*)(APP_START_ADDR + 4);
    pFunction JumpToApplication = (pFunction)jumpAddr;

    // 7. 重新使能中断
    __enable_irq();

    // 8. 跳转到 App
    JumpToApplication();

    // 9. 理论上不会执行到这里
    while(1);
}
int main(void)
{
    /* SysTick end of count event each 10ms */
    RCC_GetClocksFreq(&RCC_Clocks);
    SysTick_Config(RCC_Clocks.HCLK_Frequency / 100);

    /* Add your application code here */
    /* Insert 50 ms delay */
    Delay(5);
    JumpToApp();

    /* Infinite loop */
    while (1);
}

app实现

起始地址设置,存放在第2扇区,大小规划为第2+3+4扇区即16+16+64 = 128KB。

app的代码按照之前实现的即可,在main()中小小修改即可。

c 复制代码
#define APP_START_ADDR 0x8008000
int main(void)
{
    // 设置中断向量表偏移到 App 起始地址
    SCB->VTOR = APP_START_ADDR;
    // 设置优先级分组为2
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 
    // 配置蓝牙串口中断优先级
    NVIC_BT_Configuration();
    // 配置WIFI串口中断优先级
    NVIC_WIFI_Configuration();
    // 所有外设初始化
    All_Peripherals_Init();
    // 创建任务
    All_Task_Create();
    // 启动调度器
    vTaskStartScheduler();
	/* Infinite loop */
	while (1);
}

如果没有 SCB->VTOR = APP_START_ADDR; 也能跳转到app,只是会一直重启。原因就是中断发生时,CPU 仍去 0x8000000 找中断向量,而那里是 Bootloader 的向量表,导致程序跑飞重启,若是app中不使用中断,就不会重启。但是用FreeRTOS不可避免使用 #define xPortSysTickHandler SysTick_Handler ,得使用滴答定时中断。

建议使用 STM32CubeProgrammer 官方工具进行固件烧录。该工具功能强大,支持指定地址擦除和烧录操作,能够精准烧录bootloader和app固件。若将bootloader与app固件合并为单一文件,则可实现一次性烧录。
官方工具地址

根据所需下载,注意需要个账号

工具配置,使用ST-Link,SWD

点击Connect之后,就可以对扇区进行擦除,与下载了。

尝试对扇区进行擦除

读取扇区数据,可以看到已经擦除成功,存储数据全为0xFF。

1,分别对bootloader与app烧录

单下载boot
查看地址数据,与bin文件数据一样。

单下载app,因为是烧录boot之后再烧录app,发现程序已经跑起来了,哪怕没有勾选 Run after programming

串口打印数据正常,没有重启

c 复制代码
Serial communication is normal
Free heap before any task: 0 bytes
After Key_Task: 40584 bytes
After Feeddog_Task: 40352 bytes

2,若是对bootloader与app合并后下载就需要借助合并工具先合并。使用binJoin.exe工具

c 复制代码
binJoin.exe boot.bin 0x8000000 app.bin 0x8008000 boot_app.bin

之后对boot_app.bin在地址0x8000000出烧录即可。

因为没有勾选 Run after programming ,需要手动按下复位键启动。

相关推荐
袁小皮皮不皮11 小时前
1.HCIP BFD 学习笔记(优化版)
服务器·网络·笔记·网络协议·学习·智能路由器·ip
装不满的克莱因瓶11 小时前
【自动驾驶领域】学习 Cityscapes 数据集——城市街景语义理解的标准基准
人工智能·pytorch·python·深度学习·学习·机器学习·自动驾驶
清辞85312 小时前
产品经理需求推进流程
大数据·深度学习·学习·产品经理
YM52e13 小时前
鸿蒙PC ArkTS 声明合并问题深度解析与最佳实践
学习·华为·harmonyos·鸿蒙·鸿蒙系统
海兰13 小时前
【实用程序】电商销售分析仪表盘 — 从零搭建一个AI参与的全栈数据洞察系统
人工智能·学习·算法
ken223214 小时前
在 Libreoffice Calc中输入自定义表情字符时,需要保存之后,才能正常显示
学习
zwenqiyu14 小时前
P5283 [十二省联考 2019] 异或粽子题解
c++·学习·算法
编程圈子14 小时前
电机驱动开发学习2. 直流无刷电机工作原理
驱动开发·学习
MartinYeung514 小时前
[论文学习]大型语言模型(LLM)安全与隐私-基于善、恶、丑的深度分析
学习·安全·语言模型
什仙14 小时前
Mathcad Prime 的教程资料
学习·工具