资讯动态

STM32H5 BootLoader升级方案实战:分区规划与跳转避坑指南

发布时间:2026/10/24 3:01:36 来源:尧图企业网站定制
STM32H5 的升级方案我踩过的坑比我写过的代码还多。前几年做过一个车载数据采集项目主控从旧平台换到 STM32H5 之后原来的那套“BootLoader 串口 Ymodem”直接带不动了问题一个接一个跳转后任务卡死、上电无法唤醒 App、Flash 擦写把中断向量表搞乱……折腾了差不多一周才把整套流程理顺。这篇文章不是原理手册是我把实际做过的 STM32H5 BootLoader 升级方案重新梳理了一遍包括分区规划、跳转细节、校验策略、A/B 备份和现场翻车记录。如果你正在给 STM32H5 设计远程升级通道或者公司准备把老代码从 F1/F4 迁到 H5这篇会比官方应用笔记多一层“人话版”的现场经验。1. 为什么升级方案要单独设计而不是直接“写完烧进去就完事”很多人一听说 BootLoader 就觉得是标配功能照着老平台的代码改个地址就能用。其实这里有个容易被忽略的问题STM32H5 的启动方式、Flash 特性和安全模型跟老的 F1/F4 完全不是一个套路。如果你的出厂固件永远不升级那 BootLoader 确实没必要但只要你有过“设备已经装到客户现场还得开壳接 SWD 刷固件”的体验就知道痛在哪。1.1 现场升级的三条死路设备一旦发出去你手里的升级手段基本只剩三条串口、CAN/USB、或者网络如果设备有通信模块。但这三条路都不像 SWD 那样被芯片厂商“惯着”它们全都要靠在应用代码里提前埋好升级逻辑。我见过最惨的情况是设备里跑的是旧固件压根没有预留升级接口。客户反馈功能不对工程师只能寄回来拆壳接 ST-Link重刷再寄回去。一来一回两周没了。做产品时间久了你会发现升级通道不是功能是保险。保险不能等出事再买。STM32H5 这个平台更有意思它原生带了一套叫“Secure Boot”的机制也支持 TrustZone 安全分区。听起来很高级但实际项目里如果没吃透反而会埋雷。比如你把 App 放在未受保护的非安全区把 BootLoader 放在安全区跳转的时候一不小心就会触发 NMI 或 HardFault。1.2 有 BootLoader 和没 BootLoader差异不止“能升级”表面上BootLoader 就是一段最先执行的代码检查有没有升级请求没有就跳转跑 App。但从系统设计的角度有 BootLoader 让很多事情变成了可选固件校验上电先算一遍 App 区的 CRC校验失败就原地等升级而不是让变砖的设备瞎跑。A/B 备份保留两个 App 区升级失败还能回滚旧版本这个后面细讲。设备产线烧录出厂不用烧完整固件只烧个 BootLoader后面整包升级都走通信口产线效率高很多。1.3 什么时候你“真的需要”精心设计升级方案这里给个判断标准只要你的设备可能被升级超过一次且升级路径包含通信链路串口/USB/CAN/无线那就必须认真对待升级方案。如果只是开发阶段自己用 SWD 刷不用折腾。我个人的项目经验是嵌入式升级方案的复杂度不是由“能烧进去”决定的而是由“烧到一半失败后怎么办”决定的。断了电、掉了包、校验不对、Flash 擦写超时……这些才是设计重点。所以本文后面大部分篇幅会围绕“失败之后怎么办”展开。2. 动手设计前先搞懂 STM32H5 的“脾气”STM32H5 用的内核是 Cortex-M33主频能跑到 250MHz还带 DSP 指令和浮点单元。听起来性能强了但升级方案里性能强反而是个坑外设多、中断多、Cache 多跳转时一个没清理干净App 跑起来就是各种诡异问题。2.1 内核与主频对升级流程的实际影响Cortex-M33 和老的 Cortex-M4 相比多了一些系统控制逻辑比如 TrustZone 相关的 SAU/NSBA还有可选的指令 Cache 和数据 Cache。这意味着跳转前要处理的东西变多了。举一个具体的例子。之前有一套代码从 F4 平台迁到 H5BootLoader 跳转 App 后App 里的串口中断死活不触发。查了半天发现是跳转前没有做SCB_InvalidateICache()导致 App 首部几次取指命中了解高速缓存里的旧数据。这个坑在 F4 上从没遇到过因为老平台压根没有 I-Cache。所以从老平台升级上来的朋友这里一定要重点关照。2.2 Flash 的 Bank 结构与擦写约束STM32H5 的 Flash 总体还是带双 Bank 的架构但不同型号的具体大小和 Bank 划分有差异。以双 Bank 的型号为例比如带 2MB Flash 的型号你可以把 App 分成 Bank A 和 Bank B一个跑一个备。升级时擦写 Bank B跑着 Bank A就算擦到一半停电Bank A 还是好的上电自动回滚这事在单 Bank 设计里就做不到。实际规划分区之前要去看对应型号的 Flash 大小、扇区大小和 Bank 基地址。某国产厂商的开发板资料里给的例子就很有代表性利用双 Bank 进行现场固件升级文档里直接是在执行 Bank A 的同时擦写 Bank BDownLoad 完成之后通过设置 Flag 跳转整个过程的示意图就是两个 Bank 来回切。如果项目用到 TrustZone比如要把 BootLoader 放在安全区App 放非安全区那还要额外注意 Flash 安全属性配置。比如FLASH_CR寄存器里的安全位、FLASH_NS寄存器等等。安全区里的代码没法随意被非安全代码访问跳转边界处理不好CPU 很快就 HardFault。2.3 硬件安全等级RDP 与 HDP 怎么影响升级STM32H5 的读保护等级不止老平台的 Level 0/1/2对保密性要求高的设备还可能开 HDP隐藏保护把固件读取通道完全关死。听起来更安全了但如果你开着 RDP Level 2同时没有预留好升级通道那以后每次升级都得先把保护降级降级过程本身又可能触发全片擦除。设计阶段就要想清楚开发期建议用 Level 0 或 Level 1方便反复烧录调试。量产期如果开了 Level 2一定要保证 BootLoader 本身就支持 Signed 固件升级不然就把自己锁死在门外。提示HDP 保护下的 Flash 区域即使通过 SWD 也读不出来。升级固件的签名校验逻辑如果留在 App 里而不是 BootLoader 里一旦 App 崩了你可能连“关保护”的入口都找不到。安全特性是把双刃剑别等出事才后悔。2.4 一个值得推荐的捷径直接用官方的加密启动方案ST 对 H5 系列是有自己的 Secure Boot 方案的。不建议没有安全基础的团队从零造轮子除非你的业务有特殊定制需求。官方方案把“固件真实性校验、签名、加密、防回滚”这些都有现成参考。但我自己实际用下来官方方案有个不算缺点的缺点太重了。如果你的产品对安全等级要求没那么高只是需要一个可靠的升级通道完全可以把官方方案砍到只剩“系统启动检查 升级引导”这一小块。这样代码量小、可控性强也避免被官方库的抽象层绕晕。3. 分区与链接脚本设计把地图画明白后面才不迷路做 BootLoader 方案第一步不是写代码而是画存储地图。Flash 地址空间就那么大前面放什么、中间放什么、末尾留什么都要明确。我见过不少人上来就写代码最后发现 BootLoader 占太大、App 放不下或者升级缓存区跟 App 区重叠了都是因为动手前没有把地图画清楚。3.1 一份实用的分区规划实例假设 MCU 是带 2MB Flash、双 Bank 的型号我从一个实际项目中提炼了下面这种分区区域起始地址大小内容BootLoader0x0800 0000128KB启动引导 升级逻辑App A当前运行0x0802 0000832KB主应用参数存储区0x080F 000016KB升级标志、版本号、CRC 结果App B备份区0x0810 0000896KB待升级固件存储或另一份 App最后一段保留0x081E 0000128KB升级临时缓存/出厂信息以上地址是示意具体型号请以你自己的 Flash 大小和 Bank 边界为准。这里只强调一个原则BootLoader 区尽量独立不要让它和 App 共享一个物理 Bank。好处是升级 App 时擦写操作永远不会碰到 BootLoader 自己即使断电也不会把 BootLoader 擦坏。3.2 中断向量表的搬移不是改个宏就完事App 的启动地址在 0x08020000这意味着 APP 的起始区域必须先放一张中断向量表。正常情况下芯片上电从 0x08000000 读向量表你跳转到 0x08020000 后CPU 仍然认为向量表在 0x08000000所以必须显式修改SCB-VTOR。#define APP_FLASH_BASE 0x08020000U void app_vector_table_init(void) { SCB-VTOR APP_FLASH_BASE; }这句代码是所有跳转方案的核心之一。看起来简单但有几个细节要注意必须在App 执行任何中断相关代码之前设置 VTOR最好放在 Reset_Handler 的最开始。向量表地址要求对齐Cortex-M33 要求 VTOR 对齐到 0x80 的整数倍设计分区时最好直接对齐到 256 字节省得麻烦。如果开了双 Bank 映射地址要按 Bank 的实际基地址算不是你想写哪就写哪。还有一个经常被忽略的点用 BootLoader 跳转时MSP主栈指针要从 App 向量表的第 1 个字取而PC程序计数器从第 2 个字取。很多人直接写死SCB-VTOR但跳转却用固定地址算 PC导致 App 跑飞。3.3 链接脚本改动ORIGIN 和 LENGTH 是命根子你现在需要两个工程BootLoader 和 App。BootLoader 的链接脚本不用改太多主要是把起始地址设为 0x08000000。App 工程的分散加载文件则是重头戏/* 以 App 为例的 MEMORY 配置示意 */ MEMORY { FLASH (rx) : ORIGIN 0x08020000, LENGTH 832K RAM (xrw) : ORIGIN 0x20000000, LENGTH 640K }App 的 ORIGIN 改成 0x08020000 之后.isr_vector 段会被自动链接到这里生成的 bin 文件就直接是“从 App 基地址开始的完整镜像”。如果要生成 hexIDE 会自动填地址bin 文件则不带地址信息下载时要手动指定偏移。注意App 工程里如果用了FLASH_APP1_ADDR这类宏做常量偏移计算不要只改链接脚本宏定义也要一起改。这种“宏改了但链接脚本没改或反过来”的错位问题是排查跳转异常时最浪费时间的。3.4 参数区设计用魔法数字识别“有没有 App”分区里我特意留了一块“参数存储区”主要解决一个问题BootLoader 上电后怎么知道自己该跳 App 还是等升级最稳的做法是在参数区固定偏移写一个特定结构体typedef struct { uint32_t magic; // 固定为 0xA5A5A5A5 uint32_t app_version; uint32_t app_crc; uint32_t reserve; } upgrade_info_t;BootLoader 上电流程可以简化成读参数区的 magic。magic 等于预设值 → 认为 App 有效跳转。magic 不等于预设值 → 进入等待升级模式。升级完成或校验成功后再把这个结构的 magic 写回去。注意写参数区的时候要先擦后写Flash 擦除是按扇区/页来的所以参数区最好单独占一个扇区别和别的数据共用。4. 升级协议与流程状态机每一步校验都是在现场救你的命升级协议看起来就是“收包、写 Flash、校验”但实际通信链路上出问题最多的就是这三个环节。STM32H5 主频高、Flash 快但协议设计如果有一处没考虑到 ACK/NACK、超时重传、半包处理升级稳定性就会很拉胯。4.1 一条升级帧带什么字段我在多个项目里验证过的简单帧格式字段长度说明帧头2 字节固定 0xAA55帧类型1 字节0x01 握手 / 0x02 数据 / 0x03 结束包序号2 字节0 到 65535数据长度2 字节最大 1024数据域N 字节固件内容CRC324 字节对帧头到数据域做 CRC帧尾2 字节固定 0x0D0A帧头帧尾是为了接收端字节对齐方便CRC32 是为了保证每一包内容都完整。不要只用累加和或者 CRC8现场干扰多的时候CRC32 的容错率明显高一个量级。STM32H5 内部有硬件 CRC 外设支持 CRC32多项式 0x04C11DB7直接用硬件算速度比软件快很多代码也简单。// 初始化硬件 CRC 外设示意 void crc_init(void) { RCC-AHBENR | RCC_AHBENR_CRCEN; CRC-CR CRC_CR_RESET; } uint32_t crc32_calc(uint8_t *data, uint32_t len) { crc_init(); for (uint32_t i 0; i len; i) { CRC-DR data[i]; } return CRC-DR; }4.2 升级状态机不要把流程写成 if-else 屎山升级这件事天然是一个状态机空闲 → 握手 → 接收 → 校验 → 跳转。如果你把整个过程全部写在 while(1) 里用一堆 if 硬堆后面加超时重传、加断点续传时就会痛苦到想重写。用状态机的好处是每个状态都在明确地处理“当前这一步失败了怎么办”。比如空闲状态收到错误帧 → 丢弃重启接收框。握手状态超时 → 返回等待不退出。数据接收状态 CRC 错误 → 回复 NACK等待重发。接收完成但整体 CRC 错误 → 放弃本次升级跳到旧 App。接收完成且整体 CRC 正确 → 写 App 标志位复位跳转。我一般用枚举定义状态再用 switch 维护状态跳转每个 case 里只做一件事最后统一一个错误出口。typedef enum { UPGRADE_IDLE 0, UPGRADE_HANDSHAKE, UPGRADE_RECEIVING, UPGRADE_VERIFYING, UPGRADE_DONE, UPGRADE_ERROR } upgrade_state_t;4.3 超时与重传没有重传机制的升级只能在实验室玩串口/无线类的不稳定链路偶尔丢一个字节太正常了。如果不做超时重传固件传一半卡住整个设备就停在升级状态业界俗称“砖”。所以我对每一包都设置了超时计数器超过比如 500ms 没收到下一包就发一个 NACK 请求重发当前序号。如果是无线链路建议把单包数据长度控制在 256 字节以内太长的话一个 bit 翻转会导致整包丢弃重传效率反而更低。断点续传我一般这样处理主机发送“握手包”时带上源固件总长度、版本号、目标地址从机收到后回传当前已写入的扇区/页位置。这样断线重连后主机可以跳过已经写好的部分只传剩余部分。注意做断点续传时必须对已写入部分重新做 CRC否则识别不好容易漏坏块。4.4 整体校验最后一关的防线分帧 CRC 只能保证传输过程中单包没坏不能保证整个固件拼接后就是完整正确的。我习惯的做法是升级前先把整包固件长度和全局 CRC32 放在握手包或文件尾带过来。从机收完所有分帧后对整个 App 区的数据再算一遍 CRC32。计算值和主机声明的全局值一致 → 写 App 有效标志 → 复位跳转。不一致 → 清有效标志 → 保持旧 App 可启动 → 返回失败。提示计算全局 CRC 时不要把 BootLoader 自己占用区域的 Flash 算进去。有人整片 Flash 从头算到尾结果把启动代码区域也算进 CRC 值App 一升级就把自己干成不匹配回滚还找不到原因。5. 跳转 App 不是“指哪打哪”里面有四道坎跳转是 BootLoader 方案里最容易出问题、也最容易被低估的一步。很多老工程师第一版跳转代码就三行关中断、取地址、跳转。结果在 H5 上翻车因为少了 Cache、外设复位等清理动作。5.1 第一道坎从 Flash 里正确取出栈指针和复位向量App 的启动文件里第一个 4 字节是初始 MSP第二个 4 字节是 Reset_Handler 地址。跳转函数要直接读 Flash 对应地址不能用编译期常量替代typedef void (*pFunction)(void); void jump_to_app(uint32_t app_addr) { if (app_addr 0) { return; } uint32_t app_stack_addr *(volatile uint32_t *)app_addr; uint32_t app_reset_addr *(volatile uint32_t *)(app_addr 4); // 栈顶指针合法性防御RAM 地址范围 if ((app_stack_addr RAM_BASE) || (app_stack_addr RAM_BASE RAM_SIZE)) { return; } // 复位向量合法性防御必须在 Flash 地址范围 if ((app_reset_addr FLASH_BASE) || (app_reset_addr FLASH_BASE FLASH_SIZE)) { return; } // 关全局中断 __disable_irq(); // 复位所有外设到初始状态 Reset_All_Peripherals(); // 关闭并清除 I-Cache / D-Cache SCB_DisableICache(); SCB_DisableDCache(); SCB_InvalidateICache(); SCB_InvalidateDCache(); // 设置主栈指针并跳转 __set_MSP(app_stack_addr); pFunction app_entry (pFunction)app_reset_addr; app_entry(); }注意这里做了两层防御栈顶指针必须在 RAM 区间复位向量必须在 Flash 区间。这能避免因为 Flash 内容被意外改写而跳到一个非法地址。实测下来加了防御后即使升级数据损坏也会老老实实停在 BootLoader 等待重试而不是当场 HardFault。5.2 第二道坎外设状态必须复位干净跳转前如果不复位外设App 上电初始化时可能发现某个外设处于“半配置”状态导致初始化失败或行为异常。典型情况是 DMA、定时器、UART 这些在 BootLoader 里用过的外设。最稳妥的办法是写一个Reset_All_Peripherals()把用过的外设时钟重新打开再关闭或者把外设寄存器复位值写回去。另外一个通用做法是调用NVIC_SystemReset()做软复位利用系统上电复位电路把所有外设全部初始化干净然后在启动代码最前面判断是“冷启动还是热启动”从而决定要不要再次进入 BootLoader。这个技巧在实际项目里特别香先软复位再跳 App既省去了手动清理外设的麻烦又保证 App 的初始化环境跟冷上电一致。5.3 第三道坎I-Cache 和 D-Cache 不失效实测会跑飞这个坑我在 2.1 里提过一次这里展开说。BootLoader 本身跑在 Flash 里执行时经过 I-Cache跳转后App 所在的 Flash 区域可能已经和 Cache 里的旧内容重叠。如果你只是关中断然后跳App 前几条指令很可能还是旧的 Cache 内容现场表现就是“跳转成功但立刻卡死或跑飞”。所以跳转前必须SCB_DisableICache(); SCB_DisableDCache(); SCB_InvalidateICache(); SCB_InvalidateDCache();如果用了 DMA 搬运 Flash 数据D-Cache 还要做 Clean 操作保证 RAM 里的最新数据写出去后再搬运。很多人在 STM32H7/H5 上遇到的“下载的固件校验不对”其实是 D-Cache 写回问题导致的不是真的 Flash 内容坏了。5.4 第四道坎中断清理与优先级分组跳转前需要关中断但如果跳转后的 App 在启动代码里重新使能中断那中断标志位里可能残留着 BootLoader 里产生的 pending 中断。这个残留中断会在 App 开中断之后立刻触发如果你的 App 在启动早期还没初始化到对应外设就直接进 HardFault。我习惯的做法是跳转前把所有外设中断都清一遍逐个判断挂起位同时把 NVIC 的优先级分组复位成默认值保证 App 自己设置的优先级分组生效。优先级分组不一致会让所有中断优先级概念混乱调试起来非常痛苦。6. 容错与回滚一套可靠的升级方案必须能体面地失败真正拉开工程师水平的不是“成功升级”这条快乐路径而是“升级失败之后设备怎么办”。这一节全是“体面失败”的设计心得。6.1 A/B 双 Bank 的无缝回滚如果 MCU 支持双 Bank我最推荐的方案就是 A/B 分区App 运行在 Bank A新固件升级到 Bank B校验完成后修改 flag软复位跳转新的 Bank B或者继续跑 Bank A下次启动再切换。一旦发现新固件起不来BootLoader 检测标志位和 CRC 失败自动切回 Bank A用户几乎无感知。具体策略分为两个层面升级前把要跑的 App 标志位指向当前稳定区绝不预先指向新区。只有等新固件整体校验通过才把“下次启动优先级”指向新区。升级后首启看门狗新固件首次运行如果不主动喂狗BootLoader 认为它卡死自动回滚。这个策略在 IOT 设备上非常实用能自动识别“假启动成功”的场景。双 Bank 唯一的代价是 Flash 占用翻倍。对于 Flash 小的型号如果容量紧张可以退化为“单 Bank 外部缓存”方案新固件先全部缓存在外部存储比如 SD 卡或外挂 Flash校验 OK 后再整片搬到内部 Flash。6.2 升级标志与“三次机会”策略只有 A/B 还不够因为如果新固件在运行时才崩比如跑了三天才异常标志位早就被更新成“新的好用”了。所以我在参数区还存了一个“使用计数”字段每次启动新固件计数值加 1。如果系统在短时间内连续重启超过阈值比如 3 次判定新固件异常BootLoader 强制回滚到旧版本并把计数清零。这套逻辑很像路由器固件里的“启动失败次数检测”。代码量不大但对远程升级的可靠性提升是质变。现场无人值守的设备越需要这种“自动做决定”的能力。6.3 BootLoader 自身的容灾BootLoader 一旦损坏整台设备就是真正的砖。所以做升级方案时对 BootLoader 区域要尽量保证BootLoader 升级功能本身要极简逻辑越简单出 bug 概率越低。升级 BootLoader 的通道要单独隔离最好只能通过 SWD 或者一个特殊安全指令触发不能让普通的 App 升级包随便覆盖 BootLoader 区。编译时把 BootLoader 区固定为只读除非执行专属的 Boot 擦除动作否则任何普通 Flash 写操作都不能跨越这个边界。有人会问那 BootLoader 自己要不要 A/B一般不必因为只要 BootLoader 足够简单经过充分测试变砖概率非常低。真不放心可以在保留区塞一个“BootLoader 备份”但没必要为所有产品都加。6.4 升级记录与日志我还习惯把每次升级的相关信息记录在参数区时间戳、源版本号、目标版本号、结果、当前温度如果设备在户外。这样设备出问题返修回来先读参数区基本能还原它“临死前”干了什么。日志区不要太大16KB 循环存就行存满覆盖最旧记录。7. 实测翻车记录与最终经验总结这一节是纯实战内容。我把自己做 STM32H5 BootLoader 升级时真正踩过的坑列一遍并按“现象、原因、解决办法”整理成表格方便你对照排查。7.1 我踩过的三个典型坑现象根因解决办法跳转后 App 立刻 HardFault调试器显示 PC 指向 0xFFFFFFFE跳转前未正确处理栈指针或者提取的复位向量地址非法增加地址合法性校验__set_MSP必须在取 PC 之后执行串口升级到 90% 后断电重新上电设备无法启动新固件已经把参数区 Cleared 掉合理标志丢失升级过程中不先改参数区全部校验通过后才写标志App 串口中断偶发不工作跳转前没有清理 NVIC 挂起中断跳转前逐个清挂起位关闭所有用过的外设中断第一个坑最有戏剧性。当时是同事写了一个跳转函数直接Jump_Address (*(__IO uint32_t*) (APP_ADDR 4))然后__set_MSP和Jump_Address()都写了但忘了校验。结果 App 区没烧东西读回来全是 0xFFFFFFFF跳过去直接 HardFault。加了地址合法性判断后最多停在 BootLoader 等待重试再也不会秒崩。7.2 关于外设时钟和串口缓冲区的小事BootLoader 里用了串口跳转前要把串口复位。我经历过一次是在测试升级时App 串口初始化后收的第一个字节总是 0xFF排查到是 BootLoader 的发送寄存器里残留了最后一个字节。后来在跳转前加了__HAL_UART_DEINIT()问题消失。还有个容易被忽略的问题如果 BootLoader 的日志通过串口输出波特率配置和 App 不一致很容易让人误以为跳转失败。调试时建议 BootLoader 和 App 的日志串口波特率保持一致或者至少把跳转前的最后一条日志打清楚比如“Jump to App 0x08020000”方便现场判断卡在哪一步。7.3 优化方向加密与签名如果产品对安全有要求在上面这套方案基础上应该加一层“签名固件”机制。具体可以这样设计固件包 头部版本、长度、签名值 固件内容可能加密。BootLoader 内置公钥验签通过才允许写入 Flash。回滚保护比较版本号禁止新固件版本低于当前版本防止有人把旧固件刷回来攻击。STM32H5 支持硬件加解密加速烧 Boot 时把公钥放进安全区App 侧只留私钥签名的固件包在产线和现场都跑得通。7.4 代码组织的建议BootLoader 和 App 最好作为两个独立工程维护不要塞在一个工程里用宏区分。两个工程共用一个“裸机启动逻辑”升级协议代码只放 BootLoaderApp 只负责“是否进入升级模式”的判定和触发。另外建议在工程里加一个版本头文件例如#define APP_VERSION_MAJOR 1 #define APP_VERSION_MINOR 2 #define APP_VERSION_PATCH 0固件包头的版本号和这个要保持同步。版本不一致导致的“升级后功能不变”或“无法升级”是售后最常遇到的低级问题。8. 根据我个人的体会最后再分享三点第一升级方案设计一定要先画分区图再写代码。我见过不少工程师拿到板子就开始敲代码结果扇区大小不够、地址重叠、Cache 清理遗漏每个问题都要花大半天排查。其实做设计时静下来把这些都想清楚后面能省十倍时间。第二尽量利用官方现有的加密启动和安全策略不要为了显得专业而过度设计。STM32H5 的 Secure Boot 方案虽然重但它能帮你解决“在安全世界里处理升级”这类非常容易出错的问题。安全领域真的不适合从零造轮子除非你有一个专门做安全的团队在背后撑腰。第三升级方案做完成之后强烈建议做一次完整的故障注入测试分别在握手阶段掉电、传输 10% 掉电、传输 80% 掉电、校验失败、跳转后首启看门狗超时这些场景下反复测试。我自己的经验是把断电测试做成自动化脚本跑一个晚上第二天看日志你会对本方案有多可靠比任何人都有数。升级对嵌入式产品来说通常是最后一道关也是最见功力的一道关。希望这篇基于 STM32H5 实际的 BootLoader 升级方案能帮你绕过我踩过的坑留出时间做更有价值的事情。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑