当前位置: 首页 > news >正文

STM32-bootloader引导程序跳转机制笔记 - 教程

STM32-bootloader引导程序跳转机制笔记 - 教程

STM32F103C8T6 引导程序跳转机制笔记

适用场景:自定义 Bootloader 位于 Flash 起始 8KB( 0x08000000–0x08001FFF ),应用程序(APP)从 0x08002000 开始,芯片为 STM32F103C8T6(Flash 64KB、SRAM 20KB)。

关键内存与向量表

Memory Map 总览

如果有不懂的地方可以参考我的另一篇博客***STM32内存分配与堆栈***

地址高 ───────────────────────────────────────────────────────────
0xE000E000  System Control Space(NVIC、SCB、SysTick 等)└─ 核心控制寄存器区,软件不可搬移
0x40000000  外设寄存器空间(AHB/APB)└─ GPIO、USART、TIM、RCC、FLASH 控制器等
0x20000000  SRAM(20KB:0x20000000–0x20004FFF)├─ 堆(heap)通常自低地址向上增长└─ 栈(stack)通常自高地址向下增长(由 MSP/PSP 指向)
0x08000000  主 Flash(64KB:0x08000000–0x0800FFFF)├─ Bootloader(8KB):0x08000000–0x08001FFF└─ APP(56KB):     0x08002000–0x0800FFFF
(系统内置 Bootloader 位于“System Memory”专有区域,不在主 Flash 范围)
地址低 ───────────────────────────────────────────────────────────

点击keil的小魔术棒也可以看到如何进行内存分配
在这里插入图片描述
关注下面的ROM和RAM起始位置和区域大小

为什么要检查“栈指针必须落在 SRAM”

  • ARM Cortex-M 的固件格式规定:镜像开头的第一个 32 位值就是该固件的初始栈指针(MSP)
  • 栈需要在可读写的 RAM 中工作,因此这个值必须落在芯片的 SRAM 地址范围(F103C8 为 0x20000000–0x20004FFF )
    F103C8的RAM是20kB所以一般肯定是会指向栈顶0x20004FFF,因为栈是向低地址生长,这样也会更安全
  • 如果把 MSP 设为 0x20000000 (RAM 起点的低地址),第一次入栈就会越界到更低的地址(不在 SRAM),立刻触发错误(通常 HardFault)
  • 检查意义:快速判断指定地址上是否真的烧写了“像样的”APP
    • 合法示例: 0x20001234
    • 非法示例: 0x00000000 、 0xFFFFFFFF (通常是空闪存)或落在 Flash 的地址(不可作为栈)
      示例检查(两种写法,任选其一):
uint32_t msp = *(uint32_t*)APP_ADDRESS;
// 掩码法:判断起始段是否为 0x2000...
bool sp_ok = ((msp & 0x2FFE0000) == 0x20000000);
// 或范围法:判断落在 20KB SRAM 区间
bool sp_ok2 = (msp >= 0x20000000U && msp < 0x20005000U);

跳转前为何要做这么多“清场”操作

从 Bootloader 切换到 APP,本质上是一次“小型上下文切换”。目标是让 APP 进入时的环境与“芯片刚复位”尽可能一致,避免把 Bootloader 的状态“泄露”给 APP。

  • 关闭全局中断 __disable_irq() 防止在切换过程中,Bootloader 的中断(如 SysTick)突然触发,引发混乱。
  • 停用与清理 SysTick HAL_DeInit() 后, SysTick->CTRL/LOAD/VAL = 0 Bootloader 和 APP 都会配置各自的 SysTick;先关闭 Bootloader 的,避免互相干扰。
  • 清除 NVIC 状态(使能位与挂起位) 遍历 NVIC->ICER[i] = 0xFFFFFFFF; NVIC->ICPR[i] = 0xFFFFFFFF; 确保没有残留的中断在 APP 进入后意外触发。
  • 重定位向量表 SCB->VTOR = APP_ADDRESS 让所有中断从 APP 的向量表进入,而不是仍指向 Bootloader 的表。
  • 切换主栈指针 __set_MSP((uint32_t)APP_ADDRESS) 按 APP 向量表指定的初始栈指针重置 MSP。
  • 获取复位入口并跳转 reset = (uint32_t)(APP_ADDRESS + 4); 转为函数指针并调用。
  • 重新打开中断 __enable_irq() 让 APP 初始化后的 SysTick、中断能正常工作;这是解决 HAL_Delay() 卡住的关键。
    这些步骤缺一不可;省略任一步都可能导致不可预测的问题(如 HardFault、延时卡死、外设异常等)。

跳转代码示例

typedef void (* pFunction)(void);
void JumptoApplicaitoin(void){
uint32_t app_sp = *(__IO uint32_t*)APP_ADDRESS;
if((app_sp & 0x2FFE0000) == 0x20000000){
//关闭现有中断
__disable_irq();
//停用 HAL 和系统节拍
HAL_DeInit();
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL  = 0;
//清除 NVIC 状态
for (uint32_t i = 0; i < 8; i++) {
NVIC->ICER[i] = 0xFFFFFFFF;
NVIC->ICPR[i] = 0xFFFFFFFF;
}
//重定位中断向量
SCB->VTOR = APP_ADDRESS;
//取 APP 的复位入口
pFunction app_reset_handler = (pFunction)(*(__IO uint32_t*)(APP_ADDRESS + 4));
//设置主栈指针: __set_MSP(*(uint32_t*)APP_ADDRESS) ,按 APP 的向量表重置栈。
__set_MSP(app_sp);
//再打开中断
__enable_irq();
//调用复位入口
app_reset_handler();
}
}

要点:

  • APP_ADDRESS 对齐到 0x200 (如 0x08002000 )
  • __enable_irq() 放在跳转前,保证 APP 的 SysTick/中断可用

APP 工程链接设置(Keil/MDK 示例)

  • IROM1 Start: 0x08002000

  • IROM1 Size: 0x0000E000 (= 64KB - 8KB)

  • IRAM1 Start: 0x20000000

  • IRAM1 Size: 0x00005000 (20KB)
    校验方法(强烈建议):

  • 查看 .map 文件或 .htm / .axf 导出信息,确认

    • 向量表地址: 0x08002000
    • [0] MSP 在 0x20000000–0x20004FFF
    • [1] 复位入口在 0x08002000 之后的 Thumb 地址(最低位为 1)

常见坑点与后果

烧录与启动流程推荐

  1. 使用调试器烧录 Bootloader 到 0x08000000 (8KB 内)。
  2. 将 APP 烧录到 0x08002000 (通过 Keil 的 IROM1 设置或自定义 scatter)。
  3. 复位芯片:Bootloader 自检后直接跳到 APP 运行;无 APP 则闪灯。

调试建议

IAP(在线升级)扩展思路

  • 层次化设计:Bootloader 负责通信与写 Flash,APP 专注业务逻辑
  • 只擦/写 APP 区(1KB 页擦除),保护 Bootloader 区
  • 在 Bootloader 中做固件校验(CRC/签名)与回滚策略
  • 合理的接口与协议(UART/CAN/USB/SD 等),避免断电砖机

结论

应用程序app的分区跳转和升级app可以为你的小创意或者毕设增加亮点,想要stm32f103c8t6的bootloader模板工程可以点赞加收藏下方评论。

http://www.jsqmd.com/news/71425/

相关文章:

  • 深入解析:心率变异性计算算法(HRV)
  • Unlock Super Remote Diagnosis on Launch Smartlink C with 1-Time Activation Card
  • .gitignore 的匹配规则
  • 无法在Debian13 VSCode中使用fcitx5输入中文
  • python基础
  • Debian 13安装rime中文输入法
  • 中介者模式
  • [LabVIEW随笔-10] -基础知识3
  • 0_C++的基础语法(上)
  • Codeforces Round 1069 (Div. 2)
  • Old-Java类集框架随笔
  • linux进线程
  • 第51天(中等题 数据结构)
  • 对《AI元人文构想:从“伦理规范”向“技术合标”的范式扩展》的评价与展望(深化版)
  • Claude使用体验
  • 适合 Linux 强者的方式 freertos学习
  • 网络故障具体排错
  • Git 中文文件名显示为转义码(乱码)的解决方案
  • 真相:大量硬件场景 根本跑不了 Linux
  • Windows-GameBar-ErrorLog
  • S7-PLCSIM Advanced V6.0同网段不同电脑之间网络配置
  • 记录一些波波的话
  • 从“能用”到“卓越”:7个实战技巧提升C#代码质量
  • 2025最新结构胶品牌推荐!国内优质结构胶权威榜单发布,资质服务双优助力高品质建筑山东结构胶服务公司推荐 - 全局中转站
  • 2025最新玻璃胶推荐!国内优质玻璃胶品牌权威榜单发布,资质与性能双优助力高效密封 - 全局中转站
  • 2025最新免钉胶推荐!国内优质免钉胶品牌权威榜单发布,环保性能与粘结强度双优助力高效装修 - 全局中转站
  • 2025 最新美缝剂品牌 / 厂家 TOP5 评测!环保品质 + 技术创新权威榜单发布,匠心赋能家居装饰新体验 - 全局中转站
  • AI元人文构想:从“伦理规范”向“技术合标”的范式扩展
  • containerd base_runtime_spec
  • Luogu P9165 「INOH」Round 1 - 意外