STM32中使用GCC移动isr_vector后程序无法启动的问题排查
我需要移动isr_vector以腾出空间存储两个变量,已尝试如下操作:
- 修改链接脚本:
/* Specify the memory areas */ MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 92K RAM2 (xrw) : ORIGIN = 0x10000000, LENGTH = 32K FLASH0 (rx) : ORIGIN = 0x08000000, LENGTH = 4K FLASH (rx) : ORIGIN = 0x08001000, LENGTH = 1020K my_ram (xrw) : ORIGIN = 0x20017000, LENGTH = 4K } /* Define output sections */ SECTIONS { /* CUSOTM SECTION */ .myBufBlock(NOLOAD) : { KEEP(*(.myRAM)) /* keep my variable even if not referenced */ } > my_ram .crcBlock : { . = ALIGN(8); KEEP(*(.myCrcSection)) /* Startup code */ . = ALIGN(8); } >FLASH0 /* The startup code goes first into FLASH */ .isr_vector : { . = ALIGN(8); KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(8); } >FLASH }
- 设置VTOR偏移为0x1000:
SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET; VECT_TAB_OFFSET = 0x1000; VECT_TAB_BASE_ADDRESS = 0x08000000UL;
但操作后STM32程序无法启动,请问哪里操作错误?
1. VTOR赋值顺序完全颠倒
你先执行了SCB->VTOR = ...赋值,再给VECT_TAB_OFFSET和VECT_TAB_BASE_ADDRESS变量赋值,这会导致寄存器写入的是变量的初始垃圾值,而非预期的0x08001000。正确顺序必须先给变量赋值,再写入寄存器:
VECT_TAB_OFFSET = 0x1000; VECT_TAB_BASE_ADDRESS = 0x08000000UL; SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET;
2. 向量表对齐不符合硬件要求
STM32的中断向量表要求必须对齐到向量表总长度的整数倍(比如128个中断对应0x400字节长度,需对齐到0x400;256个中断对应0x800字节长度,需对齐到0x800)。你当前仅设置ALIGN(8),远达不到硬件要求,会导致向量表读取异常。修改链接脚本中的对齐规则:
.isr_vector : { . = ALIGN(0x400); // 需根据MCU实际中断数量调整,比如0x400或0x800 KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(8); } >FLASH
注:你设置的FLASH起始地址0x08001000本身是0x400的整数倍,满足对齐前提。
3. VTOR设置时机太晚
VTOR必须在初始化堆栈指针之后、调用main函数之前设置,也就是要放在启动文件(如startup_stm32xxxx.s)的Reset_Handler中,而非main函数内。因为复位后CPU会优先从默认向量表地址(0x08000000)读取堆栈指针和复位向量,若VTOR设置太晚,CPU会执行错误地址的代码直接崩溃。
4. FLASH0区域占用导致复位向量异常
你在0x08000000-0x08000FFF(FLASH0区域)放置了.crcBlock,但复位后CPU会首先读取0x08000000处的堆栈指针、0x08000004处的复位向量地址。如果.crcBlock覆盖了这两个地址,CPU会获取非法的堆栈指针或复位地址,直接导致启动失败。
解决方式三选一:
- 把
.crcBlock移到原向量表位置之后的区域; - 在0x08000000处放置一段极简引导代码,先设置VTOR,再跳转到新向量表的复位入口;
- 若MCU支持,通过BOOT引脚配置直接从0x08001000启动(仅部分STM32型号支持)。
内容的提问来源于stack exchange,提问作者Anton Morian

