stm32f030 中断向量表 重定位/重映射(Cortex-M0无VTOR)与bootloader原理浅析

张开发
2026/4/21 8:29:37 15 分钟阅读

分享文章

stm32f030 中断向量表 重定位/重映射(Cortex-M0无VTOR)与bootloader原理浅析
文章目录问题背景为什么跳转后 HAL_Delay() 卡死解决方案中断向量表拷贝 内存重映射参考代码实现stm32 bootloader原理回顾复位后 CPU 的取址流程boot 启动模式与地址映射机制Cortex-M3/M4的向量表重定位机制最近使用了一款芯片是STM32F030F4Cortex-M0内核在编写把ootloader → pp 跳转代码时发现它中断向量表的重映射方法与常见的F10x4Cortex-M3内核/F40x4Cortex-M4内核不同遇到了新的现象记录一下结论Cortex-M0 没有 VTOR 寄存器不能像 M3/M4 那样用 SCB-VTOR 改中断向量基址。想让放在非 0x08000000 的 App 正常跳转中断一种做法是把 App 的向量表拷贝到 SRAM 起始0x20000000然后用 SYSCFG 的内存重映射把 SRAM 映射到 0x00000000再开中断。这个动作可在 Boot 完成也可在 App 代码中的初始化过程中完成问题背景为什么跳转后 HAL_Delay() 卡死为了测试bootloader的代码能否顺利跳转到app写了最简单的一段测试代码如下while(1){uartSendData((uint8_t*)“hello”,5);HAL_Delay(200);}正常现象是串口每间隔200ms打印一句hello但实际现象是bootloader 能正确跳转到app串口刚开始能输出一帧数据之后整个程序卡死在 HAL_Delay() 里原因分析HAL_Delay() 依赖SysTick 中断来递增全局tick计数debug调试发现SysTick_Handler 根本没有被调用Cortex-M0在响应中断时会去地址0x00000000读取向量表入口由于app的向量表实际位于Flash偏移区如 0x08003000如果没有做重映射CPU 找不到 SysTick_Handler于是 tick 不加 → HAL_Delay 永远等待于是记起来要重定位向量表基地址通过SCB-VTOR但是在写代码过程中发现STM32F030F4并没有SCB-VTOR这个寄存器typedefstruct{__IMuint32_tCPUID;/*! Offset: 0x000 (R/ ) CPUID Base Register */__IOMuint32_tICSR;/*! Offset: 0x004 (R/W) Interrupt Control and State Register */uint32_tRESERVED0;__IOMuint32_tAIRCR;/*! Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */__IOMuint32_tSCR;/*! Offset: 0x010 (R/W) System Control Register */__IOMuint32_tCCR;/*! Offset: 0x014 (R/W) Configuration Control Register */uint32_tRESERVED1;__IOMuint32_tSHP[2U];/*! Offset: 0x01C (R/W) System Handlers Priority Registers. [0] is RESERVED */__IOMuint32_tSHCSR;/*! Offset: 0x024 (R/W) System Handler Control and State Register */}SCB_Type;问题关键为什么不能用 VTOR在Cortex-M3/M4 中可以通过SCB-VTOR 来修改中断向量表基地址直接指向App的Flash起始地址但是在 Cortex-M0 中没有 VTOR 寄存器向量表只能固定从 0x00000000 取因此如果app不位 0x08000000CPU在异常进入时仍会错误地去0x00000000取ISR地址导致中断失效当app不在 0x08000000时就必须使用其他方法解决方案中断向量表拷贝 内存重映射文档明确说M0 CPU不支持向量表重定位解决办法通过软件拷贝向量表到SRAM然后重映射SRAM到0x00000000具体步骤拷贝向量表将app的向量表在Flash偏移区比如0x08003000拷贝到SRAM起始地址 0x20000000重映射内存通过 SYSCFG_CFGR.MEM_MODE把SRAM重映射到0x00000000这样CPU访问0x00000000 时其实是在访问0x20000000也就是我们拷贝进去的向量表中断发生时CPU取向量表入口 → 从 RAM 找到正确的中断入口地址 → 跳转到 Flash 中的 ISR → SysTick_Handler正常运行注意可以在app中实现这一步也可以在bootloader要保证在bootloader关总中断后app打开中断之前在app工程中保留一块SRAM起始空间应用程序需要在链接脚本中保证SRAM起始地址0x20000000留有一块足够大的空间中断向量表大小我的为0xC0字节keil通过魔术棒页面修改RAM的起始地址和大小修改stm32cubeide通过.ld文件修改即可VECTOR SIZE的大小可以通过.s文件每一个DCD都代表一个中断向量 中断服务程序的入口地址 每一个4字节统计数量即可也可查看st的f0系列cube软件包里面的bootloader例程写明了是0xC0字节参考代码实现#defineAPP_VECTOR_TABLE_ADDR0x08001000#defineVECTOR_SIZE_WORDS(48)// 根据实际中断数量确定voidrelocateVectorTableToSRAM(void){uint32_t*src(uint32_t*)APP_VECTOR_TABLE_ADDR;uint32_t*dst(uint32_t*)0x20000000;// 1. 拷贝向量表for(inti0;iVECTOR_SIZE_WORDS;i){dst[i]src[i];}// 2. 重映射 SRAM 到 0x00000000LL_SYSCFG_SetRemapMemory(LL_SYSCFG_REMAP_SRAM);}或使用memcpymemcpy((void*)0x20000000,(void*)0x08001000,VECTOR_SIZE);LL_SYSCFG_SetRemapMemory(LL_SYSCFG_REMAP_SRAM);在 main() 初始化早期调用该函数intmain(void){relocateVectorTableToSRAM();// 先做向量表重定位HAL_Init();// 再初始化 HALSystemClock_Config();...}// LL_SYSCFG_SetRemapMemory 对应寄存器操作SYSCFG-CFGR1|SYSCFG_CFGR1_MEM_MODE_0|SYSCFG_CFGR1_MEM_MODE_1;// MEM_MODE 0b11重新开启中断向量表重映射完成后再使能中断系统即可正常运行。此后CPU 访问0x00000000实际上会去访问 0x20000000即拷贝进去的向量表bootloader 跳转流程Bootloader 关闭中断清除中断挂起位读取App起始地址AppBase 4设置为 PCReset_Handler设置MSP *(AppBase)栈指针跳转到 App Reset_Handler。stm32 bootloader原理回顾bootloader 的职责并不只是“跳一下函数指针”。在 Cortex-M 架构里启动方式、地址、向量表、外设/中断状态都会影响你能否切到app复位后 CPU 的取址流程在Cortex-M内核中上电复位或发生异常时CPU会从固定地址0x00000000处读取中断向量表取址规则如下0x00000000 → 初始主栈指针值MSP0x00000004 → Reset_Handler 的入口地址0x00000008 … → 其他异常/外设中断服务程序入口地址CPU始终只在地址 0x00000000 查找中断向量表boot 启动模式与地址映射机制0x00000000这一块并非某一段固定的物理存储而是一个别名地址。具体这段别名窗口映射到哪片物理存储由芯片的系统配置寄存器决定。以 STM32F0 系列Cortex-M0为例主Flash0x08000000 起SRAM0x20000000 起也就是说CPU 在取向量时访问的始终是 0x00000000 地址但这块区域可以通过配置被映射为主 Flash 的起始段系统存储器System Boot ROMSRAM在STM32中“启动”指的是芯片复位后CPU 从哪里开始取第一条指令这一点由BOOT引脚决定当系统时钟启动后在SYSCLK第4个上升沿时芯片会锁存 BOOT0/BOOT1引脚的状态从而进入不同的启动模式STM32 常见的启动目标有三类主Flash存储器Main Flash这是我们最常用的启动方式程序通过SWD/JTAG下载到内部Flash起始地址0x08000000复位后CPU就会从Flash的向量表启动配置方式BOOT00, BOOT1X这是典型的应用程序运行模式大部分产品都是这种模式例如常见的最小系统板原理图设计系统存储器System Memory这块存储器中出厂时已经由ST固化了一段ROM Bootloader用户无法修改主要作用提供ISP在系统编程功能典型应用当 Flash 被擦坏、调试接口被禁用时可以通过 BOOT 引脚启动系统 Bootloader使用 UART或 I2C/USB具体看芯片型号把新程序下载进Flash进入方式设置 BOOT01, BOOT10复位CPU 会进入 ST 提供的 ROM Bootloader通过串口工具刷写固件烧写完成后再切回BOOT00从 Flash 启动这种模式在量产或解锁“刷死”的芯片时特别有用。缺点是需要手动切换 BOOT 引脚不太方便所以很多工程会用外部电路或自动下载电路来解决内部 SRAM顾名思义就是从片上 SRAM 启动特点SRAM 是易失存储器掉电后数据丢失启动前必须先通过调试器或工具把代码加载到SRAM一般只在开发阶段用于调试代码比如如果频繁修改小段程序烧写Flash又很慢可以先放到SRAM 测试有些高安全场景会利用SRAM运行临时固件掉电即消失进入方式设置 BOOT01, BOOT11复位后 CPU 从 0x20000000SRAM起始取向量表Cortex-M3/M4的向量表重定位机制Cortex-M3/M4 内核提供了 SCB-VTORVector Table Offset Register寄存器可以在运行时指定中断向量表的基地址默认情况下VTOR 0x00000000开发者可以在系统初始化阶段修改 SCB-VTOR指向任意对齐的地址通常是Flash 中应用程序的基地址或SRAM中的一份拷贝示例代码Cortex-M3/M4// 将中断向量表重定位到应用程序起始地址APP_BASE_ADDRESS为app代码的Flash起始地址SCB-VTORAPP_BASE_ADDRESS;应用场景bootloader 跳转到appbootloader保持中断关闭app复位后自己修改VTOR指向自身向量表开启中断多固件切换多个 App只需动态修改VTOR即可因此M3/M4 系列在多应用 Bootloader 中切换中断向量表十分简单不需要额外的内存重映射操作

更多文章