STM32 BOOT 未引出场景下的串口升级方案
解决"BOOT0/BOOT1 引脚未引出、但需要通过串口升级固件"这类问题的技术方案。
以 STM32F407 + USART1 + HAL 库为实例,已实测验证可行。
1. 适用场景
- BOOT0(甚至 BOOT1)引脚未引出到板边,无法通过硬件拨码/跳线进入系统 bootloader;
- 需要现场通过串口(或 USB DFU 等)升级固件;
- 希望在应用运行中通过一条指令/命令触发升级;
- 应用里启用了独立看门狗(IWDG),需要兼顾升级过程不被看门狗打断。
方案思路:软件跳转 + 标志 + 复位 + 上电早期跳转,不依赖 BOOT0 引脚。
2. 背景与原理
2.1 什么是系统 bootloader(System Bootloader)
STM32 出厂时在芯片内部固化了一段 ROM 引导程序(System Bootloader),存放在 System Memory(系统存储器) 中。它不占用用户的 Flash,可以擦写整个 Flash(包括用户自己的 bootloader 区)。
2.2 BOOT 引脚与启动模式,以及为什么能软件跳转
复位时,硬件根据 BOOT0 / BOOT1 的组合决定从哪块存储启动:
| BOOT1 | BOOT0 | 启动模式 | 启动空间 |
|---|---|---|---|
| x | 0 | 主 Flash | 0x08000000(别名到 0x00000000) |
| 0 | 1 | System Memory(系统 bootloader) | 0x1FFF0000 |
| 1 | 1 | 内嵌 SRAM | 0x20000000 |
注:BOOT0 是物理引脚;BOOT1 的来源因系列而异------STM32F1 是独立引脚(PB2),STM32F4 则由选项字节
nBOOT1位决定(不再占用引脚)。
BOOT0 / BOOT1 只在复位瞬间被采样。运行中的程序可以直接跳转到 System Memory 的执行入口,等效于复位后从系统 bootloader 启动,从而绕开 BOOT0 引脚。这一点在 AN2606 中明确说明。
2.3 System Memory 地址(关键)
不同系列地址不同,务必以 AN2606 为准。常见速查:
| 系列 | System Memory 基址 | 备注 |
|---|---|---|
| STM32F0 | 0x1FFFEC00 / 0x1FFFD800 等 | 分具体型号 |
| STM32F1 | 0x1FFFF000 | F10x |
| STM32F2 / F4 | 0x1FFF0000 | 本文所用 |
| STM32F3 | 0x1FFFD800 | |
| STM32F7 | 0x1FF00000 | |
| STM32G0 / G4 | 0x1FFF0000 | |
| STM32L0 / L1 | 0x1FF00000 | |
| STM32L4 | 0x1FFF0000 | |
| STM32H7 | 0x1FF09800 | 注意:入口 ≠ 基址 0x1FFF0000 |
2.4 串口协议要点(AN3155)
- 数据帧:8 数据位 + 偶校验(even parity) + 1 停止位;
- 自动波特率检测:bootloader 用 SysTick 测量首个
0x7F同步字节的帧长来推算波特率; - 波特率范围 1200 ~ 115200,115200 是上限(最敏感),9600 更稳定;
- 实际波特率偏差需 < 2.5%;
- 握手机制:host 发
0x7F→ bootloader 回0x79(ACK);命令字节 + 其按位取反(XOR 校验),错误回0x1F(NACK)。
3. 方案设计
3.1 为什么不能"直接在运行中跳转"
在运行中的应用里直接跳转会有三个问题:
- IWDG 一旦启动,软件无法关闭(只能靠复位停止)。跳转后 bootloader 运行期间没人喂狗,约数秒后(取决于超时配置)芯片被复位回主 Flash,升级被打断。
- 跳转前外设状态不干净:DMA、中断、PLL、USART 等都还在运行,bootloader 期望的是复位后的默认状态,脏状态可能导致自动波特率检测异常。
- 跳转前时钟、Flash 延迟等配置残留,可能影响 bootloader 内部的 SysTick 波特率测量。
3.2 正确流程:标志 + 复位 + 早期跳转
#mermaid-svg-VUJpf2xbFq7rwqat{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VUJpf2xbFq7rwqat .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VUJpf2xbFq7rwqat .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VUJpf2xbFq7rwqat .error-icon{fill:#552222;}#mermaid-svg-VUJpf2xbFq7rwqat .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VUJpf2xbFq7rwqat .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VUJpf2xbFq7rwqat .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VUJpf2xbFq7rwqat .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VUJpf2xbFq7rwqat .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VUJpf2xbFq7rwqat .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VUJpf2xbFq7rwqat .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VUJpf2xbFq7rwqat .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VUJpf2xbFq7rwqat .marker.cross{stroke:#333333;}#mermaid-svg-VUJpf2xbFq7rwqat svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VUJpf2xbFq7rwqat p{margin:0;}#mermaid-svg-VUJpf2xbFq7rwqat .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VUJpf2xbFq7rwqat .cluster-label text{fill:#333;}#mermaid-svg-VUJpf2xbFq7rwqat .cluster-label span{color:#333;}#mermaid-svg-VUJpf2xbFq7rwqat .cluster-label span p{background-color:transparent;}#mermaid-svg-VUJpf2xbFq7rwqat .label text,#mermaid-svg-VUJpf2xbFq7rwqat span{fill:#333;color:#333;}#mermaid-svg-VUJpf2xbFq7rwqat .node rect,#mermaid-svg-VUJpf2xbFq7rwqat .node circle,#mermaid-svg-VUJpf2xbFq7rwqat .node ellipse,#mermaid-svg-VUJpf2xbFq7rwqat .node polygon,#mermaid-svg-VUJpf2xbFq7rwqat .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VUJpf2xbFq7rwqat .rough-node .label text,#mermaid-svg-VUJpf2xbFq7rwqat .node .label text,#mermaid-svg-VUJpf2xbFq7rwqat .image-shape .label,#mermaid-svg-VUJpf2xbFq7rwqat .icon-shape .label{text-anchor:middle;}#mermaid-svg-VUJpf2xbFq7rwqat .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VUJpf2xbFq7rwqat .rough-node .label,#mermaid-svg-VUJpf2xbFq7rwqat .node .label,#mermaid-svg-VUJpf2xbFq7rwqat .image-shape .label,#mermaid-svg-VUJpf2xbFq7rwqat .icon-shape .label{text-align:center;}#mermaid-svg-VUJpf2xbFq7rwqat .node.clickable{cursor:pointer;}#mermaid-svg-VUJpf2xbFq7rwqat .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VUJpf2xbFq7rwqat .arrowheadPath{fill:#333333;}#mermaid-svg-VUJpf2xbFq7rwqat .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VUJpf2xbFq7rwqat .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VUJpf2xbFq7rwqat .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VUJpf2xbFq7rwqat .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VUJpf2xbFq7rwqat .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VUJpf2xbFq7rwqat .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VUJpf2xbFq7rwqat .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VUJpf2xbFq7rwqat .cluster text{fill:#333;}#mermaid-svg-VUJpf2xbFq7rwqat .cluster span{color:#333;}#mermaid-svg-VUJpf2xbFq7rwqat div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VUJpf2xbFq7rwqat .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VUJpf2xbFq7rwqat rect.text{fill:none;stroke-width:0;}#mermaid-svg-VUJpf2xbFq7rwqat .icon-shape,#mermaid-svg-VUJpf2xbFq7rwqat .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VUJpf2xbFq7rwqat .icon-shape p,#mermaid-svg-VUJpf2xbFq7rwqat .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VUJpf2xbFq7rwqat .icon-shape .label rect,#mermaid-svg-VUJpf2xbFq7rwqat .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VUJpf2xbFq7rwqat .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VUJpf2xbFq7rwqat .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VUJpf2xbFq7rwqat :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 收到升级触发命令
标志匹配
无标志
正常运行 APP
IWDG 正常喂狗
① 写请求标志
RTC 备份寄存器 BKP0R
② 软件复位
NVIC_SystemReset()
③ main() 第一条语句
检测标志
④ 清除标志
跳转到系统 bootloader
0x1FFF0000
系统 bootloader 运行
串口升级稳定执行
正常启动 APP
跳转必须发生在 HAL_Init() / SystemClock_Config() / MX_IWDG_Init() / 任何外设初始化之前。
3.3 为什么用 RTC 备份寄存器存标志
标志必须在软件复位后依然存在,否则复位后无法得知要进入 bootloader。可选方案对比:
| 方案 | 跨软复位保持 | 实现难度 | 说明 |
|---|---|---|---|
| 普通全局变量 | ❌ 复位清零 | 简单 | 不可用 |
| no-init RAM 变量 | ✅ | 较复杂 | 需在散文件/linker 单独声明 no-init 段,不同工具链(Keil/GCC)配置各异,易出错 |
| RTC 备份寄存器 | ✅ | 最简单 | 硬件原生支持,无需改 linker |
| 内部 Flash 固定位置 | ✅ | 复杂 | 需擦写、速度慢、有磨损寿命 |
选 RTC 备份寄存器(RTC_BKP_DRx)的理由:
- 跨软复位保持 :备份寄存器位于独立备份域,
NVIC_SystemReset()这类软复位不会清零,正好满足"跨复位传递标志"的需求; - 掉电才丢失、窗口极短:只要 VDD 供电(或 VBAT 接电池)就保持,仅 VDD 掉电且无 VBAT 时才丢失。而本方案流程是"设标志 → 立即复位 → 检测到即清除",窗口极短,不影响正常使用;
- 零 linker 改动 :读写只需三步(使能 PWR 时钟 → 解除备份域写保护
HAL_PWR_EnableBkUpAccess()→ 读写寄存器),不用动散文件/链接脚本,最省事、也不容易出错。
注:STM32F407 有 20 个备份寄存器(
RTC_BKP_DR0~RTC_BKP_DR19),任选一个即可(本例用BKP0R),注意别与工程里已有的 RTC 用途冲突。
3.4 完整交互时序
系统 Bootloader RTC 备份寄存器 应用 APP 上位机 / 串口工具 系统 Bootloader RTC 备份寄存器 应用 APP 上位机 / 串口工具 #mermaid-svg-b3nzLWCXmfS3P2XZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-b3nzLWCXmfS3P2XZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-b3nzLWCXmfS3P2XZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-b3nzLWCXmfS3P2XZ .error-icon{fill:#552222;}#mermaid-svg-b3nzLWCXmfS3P2XZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-b3nzLWCXmfS3P2XZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-b3nzLWCXmfS3P2XZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-b3nzLWCXmfS3P2XZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-b3nzLWCXmfS3P2XZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-b3nzLWCXmfS3P2XZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-b3nzLWCXmfS3P2XZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-b3nzLWCXmfS3P2XZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-b3nzLWCXmfS3P2XZ .marker.cross{stroke:#333333;}#mermaid-svg-b3nzLWCXmfS3P2XZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-b3nzLWCXmfS3P2XZ p{margin:0;}#mermaid-svg-b3nzLWCXmfS3P2XZ .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-b3nzLWCXmfS3P2XZ text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-b3nzLWCXmfS3P2XZ .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-b3nzLWCXmfS3P2XZ .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-b3nzLWCXmfS3P2XZ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-b3nzLWCXmfS3P2XZ .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-b3nzLWCXmfS3P2XZ #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-b3nzLWCXmfS3P2XZ .sequenceNumber{fill:white;}#mermaid-svg-b3nzLWCXmfS3P2XZ #sequencenumber{fill:#333;}#mermaid-svg-b3nzLWCXmfS3P2XZ #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-b3nzLWCXmfS3P2XZ .messageText{fill:#333;stroke:none;}#mermaid-svg-b3nzLWCXmfS3P2XZ .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-b3nzLWCXmfS3P2XZ .labelText,#mermaid-svg-b3nzLWCXmfS3P2XZ .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-b3nzLWCXmfS3P2XZ .loopText,#mermaid-svg-b3nzLWCXmfS3P2XZ .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-b3nzLWCXmfS3P2XZ .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-b3nzLWCXmfS3P2XZ .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-b3nzLWCXmfS3P2XZ .noteText,#mermaid-svg-b3nzLWCXmfS3P2XZ .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-b3nzLWCXmfS3P2XZ .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-b3nzLWCXmfS3P2XZ .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-b3nzLWCXmfS3P2XZ .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-b3nzLWCXmfS3P2XZ .actorPopupMenu{position:absolute;}#mermaid-svg-b3nzLWCXmfS3P2XZ .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-b3nzLWCXmfS3P2XZ .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-b3nzLWCXmfS3P2XZ .actor-man circle,#mermaid-svg-b3nzLWCXmfS3P2XZ line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-b3nzLWCXmfS3P2XZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 复位:IWDG 停止、外设复位、时钟回默认态 发送升级触发命令 1 写标志 BKP0R = MAGIC 2 NVIC_SystemReset() 3 main() 第一条语句读标志 4 匹配 MAGIC 5 清除标志 6 jump_to_system_bootloader() 7 发送 0x7F 同步帧 8 0x79 ACK 9 升级命令 + 数据(XOR 校验) 10 ACK / NACK 11
4. 核心实现(完整可复制)
4.1 头文件
c
void jump_to_system_bootloader(void);
void request_system_bootloader(void);
uint8_t bootloader_requested(void);
void clear_bootloader_request(void);
4.2 实现
c
#include "stm32f4xx_hal.h"
#define BOOTLOADER_MAGIC 0x5AA5C33C /* 任意唯一魔数 */
/**
* 跳转到系统 bootloader。注意:此函数不会返回。
*/
void jump_to_system_bootloader(void)
{
void (*sysmem_boot_jump)(void);
volatile uint32_t addr = 0x1FFF0000; /* F407 System Memory 基址 */
/* 1. 时钟回到复位默认态(HSI) */
HAL_RCC_DeInit();
/* 2. 关闭 SysTick */
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
/* 3. 关闭全局中断 */
__disable_irq();
/* 4. 将 System Memory 重映射到 0x0000 0000,保证向量表正确 */
__HAL_RCC_SYSCFG_CLK_ENABLE();
SYSCFG->MEMRMP = 0x01;
/* 5. 关键顺序:先取跳转地址,再设 MSP(设 MSP 后不能再访问栈) */
sysmem_boot_jump = (void (*)(void))(*(volatile uint32_t *)(addr + 4));
__set_MSP(*(volatile uint32_t *)addr);
/* 6. 跳转 */
sysmem_boot_jump();
/* 不应到达 */
while (1);
}
/**
* 请求进入系统 bootloader:写备份寄存器标志 + 软件复位。
* 供运行中的命令(AT 指令、按键、上位机指令等)调用。
*/
void request_system_bootloader(void)
{
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWR_EnableBkUpAccess();
RTC->BKP0R = BOOTLOADER_MAGIC;
HAL_NVIC_SystemReset();
}
/**
* 复位后检查是否有进入 bootloader 的请求(备份寄存器跨软复位保持)。
*/
uint8_t bootloader_requested(void)
{
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWR_EnableBkUpAccess();
return (RTC->BKP0R == BOOTLOADER_MAGIC);
}
/**
* 清除请求标志。
*/
void clear_bootloader_request(void)
{
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWR_EnableBkUpAccess();
RTC->BKP0R = 0;
}
4.3 入口检查 main.c(放在 main() 第一条)
c
int main(void)
{
/* 复位后优先检查是否请求进入系统 bootloader */
if (bootloader_requested())
{
clear_bootloader_request();
jump_to_system_bootloader(); /* 不会返回 */
}
/* ...... 之后的正常初始化:HAL_Init / SystemClock_Config / MX_IWDG_Init / ... ...... */
}
5. 踩坑记录
坑 1:__set_MSP() 的顺序(会导致跳转后 HardFault)
错误写法(先设 MSP、后取跳转地址):
c
__set_MSP(*(volatile uint32_t *)addr); /* 先切栈 */
sysmem_boot_jump = ...(addr + 4); /* 再读 addr(volatile,从栈读)→ 垃圾值 */
__set_MSP() 会切换栈指针,之后不能再访问任何局部变量 。addr 是 volatile,编译器每次从栈上重读,切栈后读到的是垃圾,跳到错误地址直接 HardFault。
正确写法:先取跳转地址,最后再设 MSP。
坑 2:IWDG 复位打断升级
跳转后 bootloader 运行期间无人喂狗,IWDG 超时复位回主 Flash。表现为:连接能握手、能读到 Chip ID,但后续命令 timeout/NACK 交替。
解决 :用"标志 + 复位 + 早期跳转",让跳转发生在 MX_IWDG_Init() 之前(复位已停掉 IWDG)。
坑 3:自动波特率敏感,GETID 被 NACK
0x7F 同步能过(Activating OK),但 GETID 命令 NACK/timeout 交替。原因:
- 115200 接近 bootloader 波特率上限,量化误差大;
- 部分 USB-TTL 适配器(尤其 CH340 类)晶振精度不足。
解决:
- 降波特率到 9600 测试;
- 换 PL2303GT / FT232 等更可靠的适配器。
坑 4:跳转前状态不干净
在运行中直接跳转(外设/DMA/中断/PLL 未复位)会导致 bootloader 行为异常。ST 官方建议"复位后、初始化前跳转"(即本方案)。
6. 移植到其他 STM32 系列
- 改地址 :替换
jump_to_system_bootloader()中的addr(见 2.3 表,以 AN2606 为准); - 改引脚:确认目标系列系统 bootloader 使用的 USART 引脚(F407 的 USART1 为 PA9/PA10;H7/F7 等需查 AN2606 对应表格);
- 改重映射寄存器 :
- F4:
SYSCFG->MEMRMP - F0:
SYSCFG->CFGR1 - 其他系列查对应参考手册的 SYSCFG/内存重映射章节;
- F4:
- 标志存储:若目标芯片无 RTC 备份寄存器,可改用 no-init RAM 变量(需在 linker/散文件里单独声明 no-init 段),或用内部 Flash 固定位置。
7. 参考资料
- AN2606:STM32 microcontroller system memory boot mode(系统 bootloader 地址、引脚、时序)
- AN3155:USART protocol used in the STM32 bootloader(串口协议、校验、波特率)
- RM0090:STM32F405/415、407/417 参考手册(IWDG、SYSCFG、时钟)