对于ota大家应该都因该很熟悉,比如说智能语音音响手机升级等。看千差万别实际有些内容大同小异。本次我们针对stm32进行讲解
对于ota来说一般来说分为两个部分boot+app,app比较复杂,下一篇文章再说,本次们就得先把最底层的“看门人”——Bootloader(简称 Boot)给整明白。
很多人觉得 Boot 只是个引导程序,几行代码就完事了,但实际上,它是整个固件更新过程中最稳的一道防线。如果 App 升级挂了,我们还能靠 Boot 重来;如果 Boot 挂了,那就只能严重客诉了😂😂😂——J-Link 拆机或者返厂了。
一、 Bootloader 的核心逻辑:
在 STM32 中,Bootloader 本质上是一个独立于 App 的小工程(也可以理解为是运行其他功能的平台),它驻留在 Flash 的起始地址(通常是 0x08000000)。它的核心任务只有三个:
-
判断:是直接跳到旧的 App 运行,还是开始更新固件?或者是升级报错。
-
搬运:把存储在外部(如 SD卡or SPI Flash)或暂存在备份区的固件搬到 App 运行区。
-
跳转:交出 CPU 的控制权,跳转执行 App。
二、 内存布局(Memory Layout)
做 OTA 之前,你必须做一份关于Flash资源分布的内存图。以 STM32F103 为例,我们通常把内部 Flash 划分为以下几个区域:
区域名称
作用
典型起始地址
Bootloader
存放升级逻辑,永不更新
0x08000000
Flag Param
存放升级标志位(比如:是否有新固件)
0x08004000
Application
正常的业务代码运行区
0x08005000 (示例)
三、 Boot 升级的核心三步走
1. 标志位检查
Boot 启动后,第一时间不是跑 App,而是去读在参数区的标志位。
-
如果标志位显示
UPDATE_FLG = 1,说明有新固件来了,乖乖开始搬运。 -
如果标志位是
0,且 App 区代码校验通过,直接跳转。
2. 固件搬运与校验
本次工作主要是需要把加密或压缩的固件“解密/解压”并写到 App 区。
注意事项:在写 Flash 之前,一定要进行 app区全部擦除。STM32 的 Flash 只能把 1 变成 0,不擦除直接写,你的代码就会出现乱码和其他异常情况。
3. 跳转指令
这是最能体现嵌入式功底的地方,也是很多人跳转app失败的原因。跳转前需要完成四件事:
-
屏蔽全局中断:
__disable_irq(); -
清除 Pending 中断:防止有还没处理完的中断标志位影响 App。
-
关闭 Systick 定时器:这是很多人忽略的,Systick 是内核定时器,跳转后如果不关,它会继续跑。
-
设置栈指针:
__set_MSP(*(uint32_t *)APP_ADDR);/* 1. 声明函数指针 */ typedef void (*pFunction)(void); pFunction Jump_To_Application;
/* 2. 检查 App 地址的首地址(通常是栈顶地址,应在 RAM 范围内) / if ((((__IO uint32_t*)APP_ADDR) & 0x2FFE0000 ) == 0x20000000) { /* 3. 找到 App 的 Reset_Handler 地址 (App 起始地址 + 4) */ uint32_t JumpAddress = (__IO uint32_t) (APP_ADDR + 4); Jump_To_Application = (pFunction) JumpAddress;
/* 4. 这里的收尾工作至关重要 */ HAL_RCC_DeInit(); // 恢复时钟到默认状态(HSI) HAL_DeInit(); // 关闭外设 SysTick->CTRL = 0; // 关闭 Systick __disable_irq(); // 关中断 /* 5. 重新映射中断向量表(也可以在 App 的 SystemInit 里做) */ SCB->VTOR = APP_ADDR; /* 6. 设置栈指针并跳转 */ __set_MSP(*(__IO uint32_t*) APP_ADDR); Jump_To_Application();}
四、 开发注意事项
一个完整的产品的OTA Bootloader 必须具备极强的**容错性,**下面两项一定要注意:
-
断电保护:如果搬运过程中突然断电,Boot 必须能识别出“固件不完整”,并停留在升级模式等待重传,而不是尝试运行一个“残废”的 App。
-
版本回滚:如果新 App 运行崩溃,Boot 最好能自动检测并跳回备份区执行旧版本。