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 ,需要手动按下复位键启动。

相关推荐
Bechamz5 小时前
大数据开发学习Day24
大数据·学习
在下_诸葛5 小时前
langgraph学习笔记
笔记·python·学习·langgraph教程
charlie1145141915 小时前
现代Qt开发教程(新手篇)1.10——进程
开发语言·c++·qt·学习
绿豆人6 小时前
Cache缓存项目学习2
学习·缓存
山楂树の6 小时前
H.265 (HEVC) 视频解码转逐帧图像 完整实现方案
学习·音视频·h.265
星幻元宇VR6 小时前
VR观景台推动安全科普走向沉浸体验
科技·学习·安全·vr·虚拟现实
十安_数学好题速析6 小时前
【多选】成比之道:巧解三角形中比例综合
笔记·学习·高考
嵌入式小企鹅6 小时前
RISC-V车规专委会成立、AI模型集中开源、半导体产能加速爬坡
人工智能·学习·ai·程序员·算力·risc-v·半导体
我想我不够好。6 小时前
消防监控学习 4.30 1.5hour
学习