新闻详情

新闻详情

首页 / 资讯中心 / 详情

STM32F103 IAP升级踩坑:Flash起始地址调整与三地址一致律实战

发布时间:2026/10/2 1:27:23来源:尧图网络
STM32F103 IAP升级踩坑:Flash起始地址调整与三地址一致律实战
做STM32F103的IAP在线升级时我踩过的坑里有一半都跟Flash起始地址有关。默认情况下芯片的Flash从0x08000000开始链接脚本默认写死FLASH ORIGIN 0x08000000一切都好说可一旦App要避开Bootloader挪到后面的地址事情就变成了三个层面的联动修改编译链接时的地址、内核向量表的运行时偏移、以及烧录器实际写入的物理地址。任何一个环节没改对程序的表现都相当迷惑——要么启动后凡是中断一触发就HardFault要么调试器下载完但PC停在一个诡异的地址要么干脆把辛辛苦苦写好的Bootloader给覆盖掉了。这篇文章我以STM32F103C8T6 CubeMX CLion这套组合为例把“调整Flash起始地址”这件事从原理到落地讲透。你如果能完整跟着做一遍会明白链接脚本、向量表偏移、OpenOCD烧录这些平时不太关注的东西是怎么咬合在一起的。适合正在做IAP升级、Bootloader与App分离、Flash多分区方案的开发者参考。1. 什么情况下必须动Flash起始地址1.1 IAP在线升级是最典型的需求IAPIn-Application Programming应用内编程的经典架构是Flash前段固化一个Bootloader负责开机时检查新固件、接收升级包写入Flash后段然后跳转到App执行。这个架构决定了App的起始地址不能是0x08000000必须往后挪。第一次做IAP的人容易有个误区以为只要在Bootloader里实现了“串口接收擦写Flash跳转”就等于完工。实际情况是等把App烧进去、按下复位、看到Bootloader成功跳转才发现App跑不起来。一个很典型的现象是App的main函数被执行了主循环里的LED也在闪因为主循环不靠中断但一按按键或者一开定时器程序直接HardFault。这就是Flash起始地址只改了一半的典型症状编译地址被链接脚本改到了偏移位置但中断向量表还指向原来的0x08000000。所以做IAP的人迟早会遇到“调整Flash起始地址”这个操作它不是可选项而是App工程编译前的必做步骤。1.2 除了Bootloader还有这些场景需要动地址双镜像OTAA/B分区0x08000000放Bootloader0x08008000放App A0x08010000放App B。App A升级失败时Bootloader回滚到App B。这种场景下的每个App分区起始地址都要单独规划。Flash末尾存储参数如果不想用外部EEPROM可以把用户参数放在Flash最后几页。这种场景一般不改ORIGIN但要改LENGTH以缩小可用Flash范围防止链接器把数据段塞进参数区。多固件共存Bootloader 主应用 恢复固件Recovery这种三区甚至四区方案每一段代码都要精确占据自己的地址区间。这些场景本质上是同一件事你要告诉编译器和运行时环境“我的程序不在0x08000000住而在XX地址住”。理解了这个本质后面所有修改步骤都会变得顺理成章。1.3 动手改之前必须分清三个“地址”为了让后面的实操不蒙圈先建立一个核心认知框架Flash起始地址相关的“地址”其实有三个它们各管一段缺一不可概念是什么在哪里配置改错了会怎样链接地址运行地址编译时代码段、数据段被约束到哪个起始地址运行链接脚本 MEMORY 段的 FLASH ORIGIN / LENGTH程序烧进去后跳转混乱跑飞向量表地址CPU取中断向量表异常入口地址数组的基址SCB-VTOR 寄存器或 system_stm32f1xx.c 中的 VECT_TAB_OFFSET中断触发后取到Bootloader或空白的向量直接HardFault烧录地址下载器/JTAG/OpenOCD实际往Flash里写入的物理地址下载器配置或命令行参数把App烧错位置覆盖Bootloader或烧到空洞区结果不可预测这三个地址必须一致至少在“App工作期间”要保持协调。链接地址告诉编译器向量表地址告诉CPU烧录地址告诉下载器——只有三者指向同一片区域整个App才能真正跑起来。我习惯把这叫“三地址一致律”后面所有实操都围绕它展开。2. STM32F103的启动与向量表机制为什么扯一发动全身2.1 存储映射别被“FLASH ORIGIN0x08000000”骗了STM32F103的Flash映射到地址0x08000000而CPU在复位后从0x00000000取第一条指令。这两个地址看起来不同但实际上在F103的启动模式下0x00000000是0x08000000的一个别名区——你在0x08000000放的东西CPU从0x00000000也能读到。默认场景下完全不用关心这个别名机制链接脚本直接写0x08000000一切正常。但对做BootloaderApp的人这个机制就成了陷阱。如果把App链接到0x08008000CPU在App被Bootloader跳转后取指令的PC是从0x08008000开始的这没问题但中断向量表默认在0x00000000读取也就是0x08000000的别名于是CPU在触发任何中断时都会去读Bootloader区域的数据当作中断向量结果自然是HardFault。除非你显式告诉CPU“我的向量表不在这里在偏移0x8000处”也就是设置SCB-VTOR。所以改ld之后必须同步改向量表偏移——这不是惯例而是Cortex-M3的硬件机制决定的。2.2 向量表偏移VTOR的运作细节Cortex-M3内核里有个SCB-VTOR寄存器地址0xE000ED08它定义了异常向量表的起始地址。复位后VTOR默认值是0x00000000。当程序运行在0x08008000我们要把它改成0x08008000。修改方式有两种。第一种在系统初始化代码里统一处理修改system_stm32f1xx.c中的VECT_TAB_OFFSET宏从0x00改成0x8000这样SystemInit会在main之前执行时设置VTOR。第二种在main函数开头手动执行SCB-VTOR 0x08008000;适合不用CubeMX或者需要动态修改的场景。第二种方式看起来方便但有个隐藏问题如果在SystemInit到main之间有任何中断发生或者你依赖某个驱动的初始化在main之前就用了中断那“手动设置”就太晚了。最稳妥的做法还是直接修改VECT_TAB_OFFSET宏让SystemInit在最早的启动阶段把VTOR办妥。2.3 Bootloader跳转到App时发生了什么跳转流程说白了就四步关闭全局中断__disable_irq。把App区域第4字节复位向量地址取出来作为跳转目标。把App区域首字节栈顶地址写入MSP。跳转到Reset_Handler。跳转代码里有一个非常关键的步骤设置SCB-VTOR。这一步在Bootloader里做也行在App的SystemInit里做也行。我的建议是两边都做——Bootloader设置了之后App启动初期SystemInit完成前如果触发中断向量表地址已经是对的App里再设一次是为了应对Bootloader可能没设置的情况。跳转前的栈指针校验也很重要取出来的“栈顶地址”必须在SRAM范围0x20000000~0x20004FFF否则说明App区域的Flash不是有效固件直接跳会发生灾难。这个校验虽然只有两行代码但关键时刻能救你一命——程序错乱时至少不会跑到天荒地老。3. CubeMX工程生成与Flash起始地址修改实操3.1 CubeMX生成可以被CLion直接使用的CMake工程先交代我的环境STM32CubeMX 6.12 CLion 2024.1 arm-none-eabi-gcc 13.2 OpenOCD 0.12。CubeMX 6.10之后的版本Project Manager工具链下拉框里有一个“CMake”选项。选择它并生成代码CubeMX会生成一个完整的CMake工程结构包括CMakeLists.txt、Core目录、Drivers目录CLion打开后能直接识别。关键配置项芯片选STM32F103C8T6封装LQFP48。SYS - Debug: Serial Wire打开SWD下载口。RCC - HSE: Crystal/Ceramic Resonator如果板上有8M晶振。时钟树配到72MHzHCLK 72MHzAPB1 36MHzAPB2 72MHz。Project Manager - Project - Toolchain/IDE: CMake。Project Manager - Code Generator: 勾选“Generate peripheral initialization as a pair of .c/.h files per peripheral”便于管理。生成后的目录大致是这样my_project/ ├── CMakeLists.txt ├── Core/ │ ├── Inc/ │ └── Src/ │ ├── main.c │ ├── system_stm32f1xx.c │ └── ... ├── Drivers/ │ ├── CMSIS/ │ └── STM32F1xx_HAL_Driver/ ├── startup_stm32f103xb.s └── STM32F103C8Tx_FLASH.ldCLion直接Open这个CMakeLists.txt即可。建议先在CLion里配好Toolchain——装好arm-none-eabi-gcc之后在Settings - Build, Execution, Deployment - Toolchains里把C Compiler和ASM Compiler都指向arm-none-eabi-gcc。CLion会自动识别不需要额外装插件。3.2 链接脚本(.ld)的修改改哪里、为什么这样改链接脚本是决定“程序认为自己住在哪”的核心文件。CubeMX生成的STM32F103C8Tx_FLASH.ld里MEMORY段是重点关注对象。默认内容MEMORY { FLASH (rx) : ORIGIN 0x08000000, LENGTH 64K RAM (xrw) : ORIGIN 0x20000000, LENGTH 20K }我的分区规划是Bootloader占16KBApp从0x08004000开始。修改为MEMORY { FLASH (rx) : ORIGIN 0x08004000, LENGTH 48K RAM (xrw) : ORIGIN 0x20000000, LENGTH 20K }如果你计划Bootloader占32KBApp从0x08008000开始则改为MEMORY { FLASH (rx) : ORIGIN 0x08008000, LENGTH 32K RAM (xrw) : ORIGIN 0x20000000, LENGTH 20K }这里有一个非常容易犯的错只改ORIGIN不改LENGTH。默认LENGTH64K如果你保留它意味着链接器认为从0x08004000往后有64K可用空间但实际上0x08010000就是Flash终点超出部分根本不存在。链接器不会报错它不知道芯片物理极限只会把代码放到不存在的地址上烧录后几乎必炸。正确的计算方式Flash总数 0x1000064KBC8T6 App起始 0x400016KB处 App可用长度 0x10000 - 0x4000 0xC000 48KB如果你是CBT6128KB FlashBootloader占32KBApp从0x08008000开始LENGTH 128K - 32K 96K。务必先确认自己芯片的Flash总容量再算。另外我习惯在ld文件ORIGIN修改处的注释里写清楚Bootloader占多大、App可用多大、日期和原因。这样过半年回来看不会一脸懵。改完ld后如果用的是CubeMX默认链接脚本名CMakeLists.txt里自动引用的就是这个文件无需再动CMake。但后面我会讲一种更稳的规避手段。3.3 system_stm32f1xx.c中VECT_TAB_OFFSET的修改找到Core/Src/system_stm32f1xx.c搜索VECT_TAB_OFFSET默认是#define VECT_TAB_OFFSET 0x00改成#define VECT_TAB_OFFSET 0x4000如果你用的是0x08008000方案这里就改成0x8000。这个宏最终在SystemInit函数里被使用。F103的SystemInit里有这样一段条件编译#if defined(SCB_VTOR_TBLOFF) (SCB_VTOR_TBLOFF ! 0) SCB-VTOR FLASH_BASE | VECT_TAB_OFFSET; #endif把偏移填进去后每次上电执行SystemInit就会把向量表指针指到对应的偏移地址。这样即使Bootloader忘记设置VTORApp自己也能在启动的极早阶段搞定。这里有同学会问F103的VTOR是不是都可用STM32F1家族里部分早期Cortex-M3的VTOR是只读的写了没反应。这个在F103中高密度型号上基本不会遇到CMSIS头文件也明确定义了SCB_VTOR_TBLOFFSystemInit的这段代码就会生效。万一在某些老F1上改了VECT_TAB_OFFSET没效果只能用“中断向量表重映射到SRAM”这类方案不过这是极小众情况不用过于担心。3.4 CubeMX重新生成代码的“覆盖”坑与规避办法这句话请记住CubeMX每次Generate Code都会用默认模板重新生成ld文件把你手动改的ORIGIN/LENGTH冲掉。这不是“用户代码保留区”能保护的因为ld文件的生成逻辑根本不看USER CODE注释。我踩过这个坑。一开始图省事直接改默认ld改完编译烧录全通。后来因为多勾了一个外设重新Generate Code再编译就发现程序怎么烧都不对。花了二十分钟才意识到是ld被重置回0x08000000了。规避办法最省事每次生成代码后重新检查ld文件把改动再应用一遍。缺点是容易忘。相对稳把改好的ld另存为my_linker.ld放在工程根目录或LinkerScripts/目录下然后在CMakeLists.txt里把LINKER_SCRIPT变量指向它。CubeMX重新生成时不会触碰这个新文件。一劳永逸写构建脚本在CMake Configure阶段自动用你预先写好的ld替换默认ld。实际开发里我推荐第2种最简单可靠。后面第4节会给出改法。4. CLion CMake完整配置编译、烧录、调试4.1 CubeMX生成的CMakeLists.txt结构速览CubeMX生成的CMakeLists.txt骨架大致如下不同版本略有差异核心点一样cmake_minimum_required(VERSION 3.22) project(my_project C ASM) set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) # 裸机环境 set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_PROCESSOR arm) set(CMAKE_C_COMPILER arm-none-eabi-gcc) set(CMAKE_ASM_COMPILER arm-none-eabi-gcc) add_compile_options(-mcpucortex-m3 -mthumb -Wall -fdata-sections -ffunction-sections) set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/STM32F103C8Tx_FLASH.ld) add_link_options(-T${LINKER_SCRIPT} --specsnosys.specs -Wl,--gc-sections -Wl,-Mapoutput.map) file(GLOB_RECURSE SOURCES Core/Src/*.c Core/Startup/*.s Drivers/STM32F1xx_HAL_Driver/Src/*.c Drivers/CMSIS/Device/ST/STM32F1xx/Source/Templates/*.c ) add_executable(${PROJECT_NAME}.elf ${SOURCES}) add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD COMMAND ${CMAKE_OBJCOPY} -O binary $TARGET_FILE:${PROJECT_NAME}.elf ${PROJECT_NAME}.bin COMMAND ${CMAKE_OBJCOPY} -O ihex $TARGET_FILE:${PROJECT_NAME}.elf ${PROJECT_NAME}.hex )如果你采用了3.4节的“独立ld文件”方案把set(LINKER_SCRIPT ...)一行改一下即可set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/my_linker.ld)改完在CLion里点一下Reload CMake Project新配置立即生效。注意add_compile_definitions里通常需要包含芯片型号宏CubeMX默认会加上STM32F103xB USE_HAL_DRIVER之类的定义。如果你手写CMake别漏掉这两个宏否则HAL库头文件找不到。4.2 链接脚本路径的指定与验证采用独立my_linker.ld之后还有一个细节add_executable里最好把链接脚本也作为一个“源文件”加进去这样CMake在ld文件变化时能自动重新链接不然改ld后有时候不重新编译。add_executable(${PROJECT_NAME}.elf ${SOURCES} ${LINKER_SCRIPT})验证是否真的用了你改过的ld有两种方法在CLion的CMake构建日志里搜“my_linker.ld”或“--script”能看到-T/path/to/my_linker.ld出现在链接命令里。打开生成的map文件。在链接选项里加-Wl,-Map${PROJECT_NAME}.map之后编译完用文本编辑器打开map文件第一页会有Linker script and memory map段里面会列出LOAD my_linker.ld ... .text 0x0000000008004000 0xec 0x0000000008004000 isr_vector看到这个地址是你规划的起始地址就说明链接地址对了。这一步验证很重要不要跳过因为CubeMX版本之间生成的CMakeLists可能有细微差异肉眼看不出来的时候map文件会告诉你真相。4.3 OpenOCD烧录配置地址不对全是白干CLion里烧录最典型的配置是OpenOCD。先安装官方插件Embedded Development然后在Run/Debug Configuration里新建一个OpenOCD Download Run配置核心参数OpenOCD路径填你本机的openocd路径。目标配置文件如果没有对应board配置用通用组合interface/stlink.cfgtarget/stm32f1x.cfg 多个文件用空格分隔OpenOCD会按顺序加载。关键点来了OpenOCD加载elf文件烧录时目标地址取自elf的段地址。所以只要你链接到了0x08004000烧录时不会碰到0x08000000Bootloader安全。这也是为什么我强烈建议App开发尽量用elf烧录而不是bin。但如果不得不用bin文件烧录OpenOCD命令必须带地址openocd -f interface/stlink.cfg -f target/stm32f1x.cfg \ -c program build/my_project.bin 0x08004000 verify reset exit这里的0x08004000必须与ld的ORIGIN一致。写错就是把App烧到别处或覆盖Bootloader。很多人遇到的问题“App烧进去后板子没反应再一看Bootloader没了”十有八九就是bin烧录忘了指定偏移地址。用STM32CubeProgrammer CLI时也有同样问题STM32_Programmer_CLI -c portSWD -d app.elf这个直接烧elf没问题如果烧bin命令行里需要按工具说明指定起始地址。核心原则不变烧录地址必须等于链接脚本ORIGIN。4.4 调试时的地址视角用OpenOCD map文件验证烧录成功后做两步验证防止看起来“跑起来”但实际地址不对在OpenOCD console里敲reset halt再敲reg pc。如果你正在调试AppPC应该停在0x08004000附近Reset_Handler所在位置。如果PC停在0x08000000说明你其实在跑BootloaderApp根本没被正确启动。在OpenOCD里执行x/32wx 0x08004000看一下向量表内容。前4字节应该是合法的栈顶地址形如0x20004xxx第2个4字节应该是Reset_Handler地址形如0x0800xxxx。如果读到的全是0xFFFFFFFF或乱码说明Flash没烧进去或烧错位置。这两步操作很快但能帮你确认“三地址一致律”里的链接地址和烧录地址到底对齐没有。CLion用户可以在Run面板切到OpenOCD的Console视图直接操作。map文件同样有用。比如查某个函数的实际地址arm-none-eabi-nm -n build/my_project.elf | grep main如果输出是08004xxx T main说明main被链接到了偏移后的地址段。还有一个实用技巧在CMake里加一个自定义target把烧录命令集成进去这样终端里一行命令就能烧录不必每次都依赖IDE配置。我后面给的完整CMake里就带了flash_app这个target。5. 踩坑记录改完Flash地址后最容易翻车的5个错5.1 长度少算或多算程序越界写入空洞区前面说了LENGTH不跟着ORIGIN走就会出问题但还有另一个极端——有人把LENGTH写成64K-16K48K可芯片实际型号是CBT6128K Flash莫名其妙白丢一大块空间。所以第一步永远是搞清楚自己芯片Flash总共多大用“Flash总容量 - Bootloader占用”来算App的LENGTH。C8T6是64KBCBT6是128KB别记混。还有个小众话题网上说C8T6标称64K但实际刻了128K能当128K用。我建议工程上还是按官方64K规划因为你无法保证每颗芯片都能白嫖超出的空间换一批料可能就翻车。5.2 只改ld不改VTOR主循环正常但一切中断HardFault这是我见过最多、最容易误判的故障。表现是App的main能执行、LED能闪因为主循环一直在跑但只要按一下按键、开一个定时器、来一个串口中断立刻进HardFault。很多人第一反应以为是自己中断配置写错了排查半天。实际上就是VTOR还指向0x08000000Bootloader区域。CPU一进中断去Bootloader的向量表里取Handler地址取到的是随机数据或Bootloader的中断处理函数总之不是App的程序自然就炸了。所以改了链接脚本第一件事就去改system_stm32f1xx.c的VECT_TAB_OFFSET不要等HardFault出现再回头查。5.3 烧录bin时不指定偏移Bootloader被无情覆盖用elf烧录不会出这个错因为elf自带地址。用bin烧录就非常容易翻车尤其在某些图形化烧录工具里默认从0x08000000开始烧。我不止一次看到有人把App的bin直接刷掉Bootloader然后来问“Bootloader怎么连不上了”。解决方案前面已经给过OpenOCD带地址烧bin或者干脆永远用elf烧录。如果你是通过STM32CubeProgrammer手动烧每次都要确认界面里填的起始地址。另外烧录时如果遇到“flash download failed”这类报错先查一下下载器接线和驱动再检查烧录地址是否有效——地址超出了芯片Flash范围下载器同样会报错。5.4 CLion调试器加载elf后PC位置对不上用CLion调试App时有一种情况程序能跑、能断点但reset后PC停在0x08000000而不是0x08004000。这是因为OpenOCD的reset会走一次芯片复位如果调试的App不在物理地址0x08000000处Bootloader会接管启动。如果你的调试目标是App本身想从App入口单步比较稳妥的做法是在OpenOCD配置里加-c init -c reset halt -c reg pc 0x08004000或者更简单在App的Reset_Handler处打一个断点然后用OpenOCD手动reset halt再执行reg pc 0x08004000接着continue到断点。这样能跳过Bootloader直接进入App的调试上下文。如果你要做Bootloader跳转App的联合调试那就把OpenOCD的config配置好flash bank地址加载App符号表确保断点落在0x08004000区间的代码上。5.5 升了CubeMX版本或换了芯片型号ld被重新解析除了手动Generate Code会覆盖ld还有一个隐性坑在CubeMX里从C8T6换成CBT6CubeMX会重新生成对应容量的ld文件CubeMX大版本升级后首次生成工程模板也可能变化。如果ld文件不是你手动维护的独立文件名防不胜防。所以我的最终建议是把ld文件当成“源文件”纳入工程管理不要让它成为CubeMX的“一时生成物”。独立命名在CMakeLists里显式引用就是最适合CubeMXCLion组合的方案。6. 可直接套用的完整CMake配置与Bootloader跳转参考代码6.1 完整CMakeLists.txtC8T6/CLion/OpenOCD这是一份经过实战验证的CMakeLists.txt你可以直接放在工程根目录使用。它基于CubeMX生成的版本做了精简和调整注释说清楚了每部分的作用cmake_minimum_required(VERSION 3.22) project(stm32f103_app C ASM) set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) # 裸机环境 set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_PROCESSOR arm) # 编译器由CLion Toolchain提供这里做兜底 set(CMAKE_C_COMPILER arm-none-eabi-gcc) set(CMAKE_ASM_COMPILER arm-none-eabi-gcc) # MCU编译选项Cortex-M3Thumb指令集 add_compile_options(-mcpucortex-m3 -mthumb -Wall -fdata-sections -ffunction-sections) add_compile_definitions(STM32F103xB USE_HAL_DRIVER) # 手动维护的链接脚本CubeMX重新生成不会覆盖 set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/my_linker.ld) # 链接时生成map文件方便核对地址 add_link_options(-T${LINKER_SCRIPT} --specsnosys.specs -Wl,--gc-sections -Wl,-Map${PROJECT_NAME}.map) # 收集源文件 file(GLOB_RECURSE SOURCES Core/Src/*.c Core/Startup/*.s Drivers/STM32F1xx_HAL_Driver/Src/*.c Drivers/CMSIS/Device/ST/STM32F1xx/Source/Templates/*.c ) add_executable(${PROJECT_NAME}.elf ${SOURCES} ${LINKER_SCRIPT}) # 生成bin与hex add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD COMMAND ${CMAKE_OBJCOPY} -O binary $TARGET_FILE:${PROJECT_NAME}.elf ${PROJECT_NAME}.bin COMMAND ${CMAKE_OBJCOPY} -O ihex $TARGET_FILE:${PROJECT_NAME}.elf ${PROJECT_NAME}.hex COMMAND ${CMAKE_SIZE} $TARGET_FILE:${PROJECT_NAME}.elf ) # 烧录App到0x08004000OpenOCD命令 add_custom_target(flash_app COMMAND openocd -f interface/stlink.cfg -f target/stm32f1x.cfg -c program ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bin 0x08004000 verify reset exit DEPENDS ${PROJECT_NAME}.elf ) # 用elf烧录地址来自elf自身更安全 add_custom_target(flash_elf COMMAND openocd -f interface/stlink.cfg -f target/stm32f1x.cfg -c program ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.elf verify reset exit DEPENDS ${PROJECT_NAME}.elf )其中my_linker.ld的关键段以16KB Bootloader、App从0x08004000为例MEMORY { FLASH (rx) : ORIGIN 0x08004000, LENGTH 48K RAM (xrw) : ORIGIN 0x20000000, LENGTH 20K }6.2 Bootloader跳转App的参考代码假设你的Bootloader通过串口或按键判断需要启动App跳转函数如下#include stm32f1xx_hal.h #define APP_FLASH_BASE 0x08004000u #define SRAM_BASE 0x20000000u #define SRAM_END 0x20004FFFu /* C8T6: 20KB SRAM按实际型号调整 */ void JumpToApp(void) { /* 1. 关闭全局中断DeInit用到的外设 */ __disable_irq(); HAL_UART_DeInit(huart1); /* 如有其他外设也恢复默认状态 */ /* 2. 从App向量表读出栈顶指针和复位向量 */ uint32_t app_sp *(volatile uint32_t *)APP_FLASH_BASE; uint32_t app_pc *(volatile uint32_t *)(APP_FLASH_BASE 4); /* 3. 校验栈顶指针是否落在SRAM范围防止跳到非法区域 */ if (app_sp SRAM_BASE || app_sp SRAM_END) { return; /* App无效继续留在Bootloader */ } /* 4. 设置向量表偏移 */ SCB-VTOR APP_FLASH_BASE; /* 5. 设置主栈指针并跳转 */ __set_MSP(app_sp); typedef void (*pFunction)(void); pFunction jump (pFunction)app_pc; jump(); while (1) { /* never reach here */ } }这段代码最重要的两点跳转前把Bootloader用到的外设全部复位否则App初始化时可能遇到残留状态跳转前校验栈指针范围防止误跳。很多IAP问题不是出在“跳过去了”而是出在“乱跳”。6.3 整体关系回顾再梳理一遍完整链路Bootloader编译链接在0x08000000烧录到0x08000000。App编译链接在0x08004000ld的ORIGIN向量表偏移0x4000VECT_TAB_OFFSET烧录到0x08004000OpenOCD program命令的地址或elf内嵌地址。Bootloader上电执行检查App有效性跳转前设置SCB-VTOR和MSP然后跳转。App的SystemInit根据VECT_TAB_OFFSET再次设置VTOR保证中断向量正确。至此App中的所有中断、外设、主循环都在0x08004000地址段正常运行。你只要把“三个地址”全部对齐这套方案在任何F103工程上都成立。写到最后分享一个我自己的习惯。每次调整Flash起始地址我会先在一张纸上画Flash分区图把Bootloader、App、参数区的地址范围全部标出来然后对照“三地址一致律”逐项检查链接脚本ORIGIN/LENGTH、VTOR偏移、烧录命令地址三个地方的值是否一致、长度是否算对。别小看这张图很多时候排查半天的问题回头看就是某个地址差了一个0。如果你现在正卡在“App能编译能烧录但跑不起来”建议顺着这篇文章的顺序重查一遍先看map文件确认.text段起始地址再看VTOR有没有设对最后确认烧录命令的bin偏移。大概率就是这三者之一出了问题。祝一次打通。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

黑马点评商品类型Redis缓存实战:从Key设计到穿透击穿防御 2026/10/2 3:07:50

黑马点评商品类型Redis缓存实战:从Key设计到穿透击穿防御

最近在整理黑马点评项目的课后练习,其中一道题是给商品类型列表加上Redis缓存。这道题看起来很小,真做起来却能带出一串问题:缓存key怎么设计、商品类型用哪种数据结构、缓存穿透要不要防、RedisTemplate序列化为什么全是乱码、连接池超时怎么…

阅读更多 →
大模型压测中TTFT指标获取全流程:概念、工具与排障实践 2026/10/2 3:07:50

大模型压测中TTFT指标获取全流程:概念、工具与排障实践

前两天在群里看到有人问:压测大模型的时候,TTFT这个指标到底怎么拿?底下回答挺多,但大部分是概念性的,真到了动手阶段,很多人还是不知道该从哪里下手。我自己第一次搭vLLM服务做压测时也踩过类似的坑&#…

阅读更多 →
Unity CharacterController重力实现原理与角色移动最佳实践 2026/10/2 3:07:50

Unity CharacterController重力实现原理与角色移动最佳实践

1. 项目概述:为什么用CharacterController而不是Rigidbody做角色移动?在Unity里做第三人称或第一人称角色控制,新手常踩的第一个坑就是:一上来就给角色挂Rigidbody,以为“有物理才真实”。结果呢?卡顿、穿模…

阅读更多 →
网络欺诈检测NLP实战:从TF-IDF到BERT微调的完整指南 2026/10/2 3:07:50

网络欺诈检测NLP实战:从TF-IDF到BERT微调的完整指南

简介:网络欺诈检测数据集提供8,564个中英双语欺诈案例,覆盖钓鱼、虚假招聘、冒充等类型,面向自然语言处理与网络安全研究者,用于训练和评估多类别欺诈文本识别模型,可帮助开发者快速构建和迭代检测方案。资源共4个JSON…

阅读更多 →
PyTorch CNN遥感滑坡识别:小样本、多光谱与距离场监督 2026/10/2 3:07:50

PyTorch CNN遥感滑坡识别:小样本、多光谱与距离场监督

简介:本资源是一套基于PyTorch实现的遥感图像滑坡识别系统,面向地理信息科学、遥感技术及人工智能交叉领域的高校学生与科研初学者,解决地质灾害智能解译中的关键识别问题。压缩包共15个文件,含7个核心Python脚本(涵盖…

阅读更多 →
RAP Side Effects 实战:从行为定义根治 Fiori 局部刷新难题 2026/10/2 3:07:37

RAP Side Effects 实战:从行为定义根治 Fiori 局部刷新难题

做 Fiori 开发的同行,应该都有过这种经历:用户在详情页改了一个下拉框,页面上另一个关键字段死活不更新,业务顾问一口咬定“系统坏了”,你跑到前端 Fiori 调试器里翻 controller,找到一处手写的oModel.refr…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉