1. 烧录地址不是“随便填的数字”,而是芯片启动逻辑的物理指纹
你第一次在Keil里点“Download”时,烧录器弹出窗口让你填“Start Address”,手一抖输了个0,程序居然跑起来了;第二次换了个STM32F103C8T6,烧录工具默认显示0x08000000,你照填,也正常;第三次调试一个ESP32-WROOM-32模块,串口下载工具却要求你填0x6000——你盯着这三个数字发呆:它们到底代表什么?是芯片厂商拍脑袋定的?还是烧录工具bug?抑或自己配置错了?
不是。这三个地址背后,是三套完全不同的硬件启动机制、存储器映射架构和引导流程设计逻辑。把它们当成“可选参数”去试错,轻则程序不启动、串口没反应,重则擦除错误扇区导致芯片变砖。我干这行十年,亲手救过二十多块因烧录地址填错而锁死的开发板,最典型的一次,是把0x08000000错填成0x08000001,结果Bootloader跳转到非法指令地址,MCU直接卡死在复位向量处,连SWD都连不上。
烧录地址的本质,是告诉编程器:“请把我的固件二进制代码,从哪个物理位置开始,写进芯片的非易失性存储器里”。这个“物理位置”,必须同时满足三个硬性条件:
第一,它必须落在芯片实际存在的Flash存储器地址空间内;
第二,它必须对齐芯片Flash页/扇区的擦除边界(比如STM32F103的最小擦除单位是1KB扇区,地址必须是0x400的整数倍);
第三,它必须与芯片上电后执行的第一条指令地址(即复位向量入口)严格一致——否则CPU一上电就去读一堆乱码,直接死机。
所以你看,0、0x08000000、0x6000,根本不是“有时是A,有时是B”的随机现象,而是不同芯片家族在硅片级设计时,就固化下来的启动地址规则。就像汽车钥匙必须匹配特定齿形才能启动引擎,烧录地址就是那把“数字钥匙”,错一个bit,引擎就不转。接下来,我会用真实芯片手册+实测波形+反汇编截图,一层层剥开这三类地址背后的硬件真相,告诉你怎么一眼看穿任何新芯片的正确烧录地址。
1.1 地址0:裸机时代的“原点哲学”,只属于最简化的8位单片机
地址0,是所有嵌入式工程师最早接触的烧录地址,也是最容易被误解的“万能起点”。它常见于STC89C52、AT89C51这类经典8051内核单片机,以及部分早期ARM Cortex-M0芯片(如NXP LPC824)。但它的存在,绝非因为“简单好记”,而是源于一种极致精简的硬件设计哲学:将Flash存储器的起始物理地址,直接映射到CPU复位后的程序计数器(PC)初始值。
我们以STC89C52为例。查阅其数据手册第12页“Memory Organization”章节,明确写着:
“After reset, the program counter (PC) is loaded with 0x0000. The CPU fetches the first instruction from address 0x0000 in the internal program memory.”
这句话翻译过来就是:复位后,CPU的PC寄存器被硬件强制加载为0x0000,然后立刻从地址0x0000处取第一条指令执行。而STC89C52的内部Flash,物理地址范围正是0x0000 ~ 0x7FFF(32KB)。因此,烧录地址填0,意味着把你的main函数入口、中断向量表、所有代码段,全部塞进Flash的最开头——CPU上电后自然就能找到并执行。
但这里有个致命陷阱:地址0只适用于“无Bootloader”的纯裸机环境。一旦你给STC单片机加了串口ISP功能,情况就变了。STC官方ISP工具(如STC-ISP)会在Flash末尾预留一段空间存放Bootloader代码,而用户程序则从0x0000开始烧录。此时,烧录地址仍是0,但整个Flash布局被重新划分:前0x7C00字节是用户代码,后0x400字节是Bootloader。如果你用第三方烧录工具(比如CH341A编程器)强行把固件烧到0x0000,而没预留Bootloader空间,上电后Bootloader就被覆盖,ISP功能永久失效。
我去年帮一个客户修复一块无法ISP的STC12C5A60S2,用逻辑分析仪抓取复位时的Flash读取波形,发现CPU确实在0x0000地址读取,但读到的是全0xFF(空白Flash),说明用户代码根本没烧进去。最后查到是客户用Keil生成的hex文件包含了扩展线性地址记录(Extended Linear Address Record),而老版本STC-ISP解析时会忽略该记录,导致实际烧录偏移了0x1000。这就是为什么“填0”看似简单,却需要你彻底理解目标芯片的启动流程和烧录工具的行为逻辑。
提示:当你看到烧录地址为0时,请立即确认三点:
- 芯片是否内置Flash且起始地址为0(查手册Memory Map章节);
- 是否使用原厂ISP工具(非Keil/J-Link等通用工具);
- 固件是否包含完整的中断向量表(尤其Vector Table Offset Register是否被正确设置)。
1.2 地址0x08000000:STM32的“黄金标准”,源于Cortex-M内核的存储器映射规范
0x08000000这个地址,几乎成了STM32系列的代名词。无论你是用ST-Link烧STM32F103,还是用J-Link烧STM32H743,烧录界面默认显示的起始地址,十有八九都是0x08000000。它不像地址0那样“凭空出现”,而是ARM Cortex-M内核架构强制规定的主Flash存储器起始地址,写死在硅片里,任何兼容Cortex-M的芯片都必须遵守。
ARM官方文档《ARMv7-M Architecture Reference Manual》第4.2.1节明确指出:
“The main Flash memory region is mapped to address 0x0000_0000–0x1FFF_FFFF. However, for exception handling, the vector table must be located at address 0x0000_0000 on reset. To resolve this conflict, the NVIC supports a vector table offset register (VTOR) that allows the vector table to be relocated.”
这段话揭示了关键矛盾:Cortex-M规定复位后PC从0x00000000取指令,但又要求中断向量表(Vector Table)必须放在0x00000000。而Flash物理地址不可能从0开始(因为0地址通常留给SRAM或外设),怎么办?ARM的设计是:让芯片厂商在复位时,通过硬件将0x00000000这个地址,动态映射(Remap)到真实的Flash起始地址。对于STM32F103,这个真实Flash起始地址,就是0x08000000。
我们用STM32CubeMX生成一个最小工程,打开生成的startup_stm32f103xb.s文件,看Reset_Handler之后的向量表:
__Vectors DCD __initial_sp ; Top of Stack DCD Reset_Handler ; Reset Handler DCD NMI_Handler ; NMI Handler DCD HardFault_Handler ; Hard Fault Handler ...这个向量表,在链接脚本(STM32F103CBTx_FLASH.ld)中被定位到.isr_vector段,其起始地址由_estack = 0x20005000;和_Min_Stack_Size = 0x400;计算得出,但最关键的是:
MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K } SECTIONS { .isr_vector : { . = ALIGN(4); KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(4); } > FLASH }这里ORIGIN = 0x08000000,就是告诉链接器:把所有代码和向量表,统统塞进Flash的0x08000000起始区域。烧录器拿到这个bin/hex文件,自然就知道该从0x08000000开始写入。
但问题来了:为什么不是0x08000000 + 偏移?比如有些项目要求Bootloader放在0x08000000,App程序从0x08004000开始烧录?这就引出了“地址偏移”的核心逻辑。STM32的Flash擦除是以扇区(Sector)为单位的,F103的Sector0大小是1KB(0x08000000 ~ 0x080003FF),Sector1是1KB(0x08000400 ~ 0x080007FF),以此类推。如果你的Bootloader只有2KB,它就必须占据Sector0和Sector1,那么App程序的烧录地址,就必须是0x08000800(Sector2起始)。我曾在一个工业PLC项目中,因App烧录地址错填成0x08000400(Sector1中间),导致擦除Sector1时把Bootloader的后半部分擦掉了,设备再也无法升级。
注意:STM32的0x08000000是“物理地址”,但你在调试时看到的PC值,可能是0x00000000(因为NVIC VTOR被设置为0x08000000,CPU访问0x00000000时,硬件自动转发到0x08000000)。这是ARM MMU/MPU机制的体现,不是烧录器的bug。
1.3 地址0x6000:ESP32的“分段加载艺术”,源于Xtensa架构的二级引导机制
0x6000这个地址,是ESP32系列(包括ESP32-S2、ESP32-C3)烧录时最常遇到的“异类”。它既不是0,也不是0x08000000,看起来毫无规律。但如果你翻开乐鑫官方《ESP32 Technical Reference Manual》第6章“Boot and Startup”,就会发现:0x6000根本不是Flash的起始地址,而是eFuse中存储的“应用程序镜像偏移量”。ESP32的启动过程,是一场精密的“三级接力赛”。
第一级:ROM Bootloader(固化在芯片ROM中,不可修改)。上电后,ROM Bootloader首先运行,它会读取eFuse中的FLASH_CRYPT_CNT(Flash加密使能)、CHIP_VER(芯片版本)等配置,并根据SPI_PIN引脚状态,确定SPI Flash的接线方式(QIO/QOUT/DIO/DOU)。
第二级:Secondary Bootloader(烧录在Flash的0x1000地址)。ROM Bootloader会从Flash的0x1000地址加载并执行Secondary Bootloader。这个Secondary Bootloader才是乐鑫SDK(ESP-IDF)真正可控的部分,它负责初始化SPI Flash控制器、解密(如果启用)、校验(CRC32)。
第三级:Application Image(应用镜像)。Secondary Bootloader会读取Flash中一个特殊结构体——image_header_t,其定义在esp_image_format.h中:
typedef struct { uint8_t magic; uint8_t legacy_crc; uint8_t reserved[10]; uint32_t flash_mode; // SPI mode uint32_t flash_speed; // SPI speed uint32_t flash_size; // Flash size } image_header_t;而这个image_header_t的起始地址,就是烧录工具(esptool.py)要求你填的--flash_mode dio --flash_freq 40m --flash_size detect 0x6000中的0x6000。它表示:应用镜像的二进制数据,从Flash的0x6000偏移处开始存放。
为什么是0x6000?因为前面的空间被Reserved了:
- 0x0000 ~ 0x0FFF:ROM Bootloader的参数区(eFuse映射)
- 0x1000 ~ 0x1FFF:Secondary Bootloader(约4KB)
- 0x2000 ~ 0x5FFF:OTA分区表(Partition Table)、Factory App、OTA App等元数据(约16KB)
- 0x6000 ~ :第一个Factory App镜像的起始位置
我在调试一个ESP32-WROVER-B模块时,客户反馈OTA升级失败。用esptool.py read_flash 0x6000 0x1000 app.bin读出镜像,反汇编发现入口地址是0x400D0000(IRAM),但实际烧录时,esptool.py会自动在镜像头部插入一个esp_image_header_t结构,并将entry_point字段设为0x400D0000。如果手动用dd命令把bin文件写到0x6000,而没加header,Secondary Bootloader就会因校验失败而跳过该镜像,直接尝试下一个分区。
关键提醒:ESP32的0x6000是“镜像起始偏移”,不是“CPU执行起始地址”。CPU最终执行的是镜像中指定的entry point(通常是0x400D0000,指向IRAM中的代码),而0x6000只是Flash上的存放位置。混淆这两者,是ESP32新手最常见的错误。
2. 看懂芯片手册里的“Memory Map”,比背公式更重要
很多工程师面对烧录地址困惑,第一反应是百度、问群、翻论坛,却忘了最权威的源头——芯片数据手册(Datasheet)和参考手册(Reference Manual)。手册里那个叫“Memory Map”的表格,就是你的终极答案库。但问题在于,90%的人只会扫一眼“Flash: 0x08000000 - 0x0801FFFF”,却忽略了旁边一行小字:“Note: This address is remapped to 0x00000000 on reset”。这一行小字,就是解开所有谜题的钥匙。
我以STM32F407VGT6为例,打开《STM32F407xx Reference Manual》第2.3.2节“Memory map overview”。表格清晰列出:
| Memory Area | Start Address | End Address | Size | Description |
|---|---|---|---|---|
| System memory | 0x1FFF0000 | 0x1FFF77FF | 30KB | Bootloader (USART, USB, CAN, etc.) |
| SRAM | 0x20000000 | 0x2001FFFF | 128KB | Main SRAM |
| CCM SRAM | 0x10000000 | 0x1000FFFF | 64KB | Core Coupled Memory |
| Flash | 0x08000000 | 0x080FFFFF | 1MB | Main Flash memory |
| Peripheral block | 0x40000000 | 0x5FFFFFFF | 512MB | All peripherals |
注意,Flash行的“Start Address”是0x08000000,但手册紧接着在2.3.3节“Memory remap”中解释:
“At reset, the boot pins are sampled and the boot source is selected. The boot memory is then mapped at address 0x00000000. The mapping depends on the boot source:
- Main Flash memory: 0x00000000 → 0x08000000
- System memory: 0x00000000 → 0x1FFF0000
- Embedded SRAM: 0x00000000 → 0x20000000”
这段话的意思是:复位时,芯片根据BOOT0/BOOT1引脚电平,决定从哪里启动。如果BOOT0=0,BOOT1=x,则选择Main Flash,此时硬件会把0x00000000这个地址,动态映射(Remap)到物理Flash的0x08000000。所以,烧录地址填0x08000000,是因为这是Flash的物理起始地址;而CPU执行时PC=0x00000000,是因为硬件做了地址转换。
再看ESP32的手册,《ESP32 Technical Reference Manual》第6.2节“Boot process”给出一张流程图:
- Power-on → ROM Bootloader runs
- ROM reads eFuse → determines SPI config
- ROM loads Secondary Bootloader from0x1000
- Secondary Bootloader reads Partition Table from0x8000
- Secondary Bootloader loads Factory App from0x6000(or OTA App from 0x10000)
这里0x1000、0x8000、0x6000,全是Flash上的绝对偏移地址,由乐鑫预定义,写死在Secondary Bootloader源码里(components/bootloader_support/src/esp_image_format.c)。你无法更改,只能遵守。
而STC89C52的手册,《STC89C52RC Datasheet》第3.2节“Memory Structure”则直白得多:
“The internal program memory (Flash) is organized as 4K bytes per sector. The first sector starts at address 0x0000.”
没有Remap,没有Bootloader,没有复杂映射,就是简单的线性地址。所以烧录地址就是0。
这三份手册的对比,揭示了一个铁律:烧录地址 = 芯片手册中“Memory Map”表格里,你所选启动介质(Flash/SRAM/System Memory)的“Start Address”。剩下的工作,就是确认这个Start Address是否被Remap,以及你的固件是否适配了Remap后的向量表位置。
2.1 实战技巧:三步速查法,5分钟锁定任意新芯片的烧录地址
当你拿到一块从未用过的芯片(比如刚发布的GD32E503、或是国产RISC-V芯片CH32V307),如何快速确定烧录地址?我总结了一套“三步速查法”,已在十几个项目中验证有效:
第一步:查芯片型号后缀,锁定Flash容量与起始地址
芯片型号往往暗藏玄机。例如:
- STM32F103C8T6:C表示Flash容量为64KB,查STM32F103xx datasheet,64KB Flash起始地址为0x08000000;
- GD32F303RCT6:R表示Flash为256KB,GD32F303xx手册Table 1明确写出“Flash memory: 0x08000000 - 0x0803FFFF”;
- ESP32-WROOM-32:WROOM表示内置4MB Flash,乐鑫文档规定Factory App固定从0x6000开始。
如果型号不明确,直接搜索“[芯片型号] datasheet pdf”,用Ctrl+F搜“memory map”或“flash start address”。
第二步:看开发板原理图,确认BOOT引脚配置
烧录地址还取决于你如何启动芯片。同一颗STM32F407,如果BOOT0=1,BOOT1=0,它会从System Memory启动(地址0x1FFF0000),此时烧录地址就得填0x1FFF0000,而不是0x08000000。所以,务必打开你的开发板原理图,找到BOOT0和BOOT1电阻的连接方式。常见配置:
- BOOT0接地(GND):从Main Flash启动 → 烧录地址 = Flash Start Address
- BOOT0接VCC:从System Memory启动 → 烧录地址 = System Memory Start Address
- BOOT0悬空(通过10K电阻上拉):需看具体芯片,有些默认Flash,有些默认SRAM
我曾在一个客户项目中,因开发板BOOT0通过0Ω电阻焊接到VCC,导致烧录到0x08000000后设备不启动,反复排查半小时才发现是启动模式错了。
第三步:用烧录工具的“Auto Detect”功能,交叉验证
主流烧录工具(ST-Link Utility、J-Flash、esptool.py)都有自动识别功能。例如:
- ST-Link Utility:连接芯片后,点击“Target → Connect”,它会自动读取芯片ID,并在“Program Download”页显示正确的Flash起始地址和大小;
- esptool.py:运行
esptool.py chip_id确认芯片型号,再esptool.py flash_id读取Flash ID,工具会自动匹配正确的烧录参数。
如果工具显示的地址与手册不符,优先信手册,但要检查:
- 工具版本是否过旧(如老版ST-Link Utility不支持新STM32H7);
- SWD/JTAG接口是否接触不良(导致ID读错);
- 芯片是否已被锁(Read Out Protection),导致无法读取Flash信息。
经验之谈:我习惯在项目初期,用逻辑分析仪(Saleae Logic)抓取SWD时序,验证烧录器是否真的把数据写到了预期地址。方法很简单:设置SWDIO为输入,触发条件设为“SWDIO falling edge”,然后执行一次烧录,观察波形中数据包的地址字段。这比单纯看工具日志可靠十倍。
3. 链接脚本(Linker Script)是烧录地址的“法律文书”,改错一个数字就全盘崩溃
烧录地址的最终落脚点,不是烧录工具的输入框,而是链接脚本(Linker Script,.ld文件)。它是编译器(GCC/ARMCC)的“宪法”,明确规定了代码、数据、堆栈在内存中的精确布局。你填在烧录工具里的地址,只是告诉编程器“往哪写”,而链接脚本则决定了“写什么”——如果两者不一致,轻则程序跑飞,重则覆盖关键数据。
以STM32F103CBT6为例,其标准链接脚本STM32F103CBTx_FLASH.ld核心片段如下:
/* Specify the memory areas */ MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K } /* Define the section where the vector table will be placed */ SECTIONS { .isr_vector : { . = ALIGN(4); KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(4); } > FLASH .text : { . = ALIGN(4); *(.text) /* .text sections (code) */ *(.text*) /* .text* sections (code) */ . = ALIGN(4); } > FLASH }这里ORIGIN = 0x08000000,就是链接器的“法律依据”。它强制要求:
- 所有代码(.text)和中断向量表(.isr_vector)必须放在FLASH内存区域;
- FLASH区域的物理起始地址是0x08000000;
- 因此,生成的bin文件,第一个字节就是向量表的第一个DWORD(初始堆栈指针),对应物理地址0x08000000。
但如果项目需求变更,比如要加一个2KB的Bootloader,你需要:
- 修改MEMORY中FLASH的ORIGIN为0x08000800(跳过前2KB);
- 新增一个BOOTLOADER内存区域:
BOOTLOADER (rx) : ORIGIN = 0x08000000, LENGTH = 0x800; - 在SECTIONS中,将Bootloader代码段(.bootloader)显式分配到BOOTLOADER区域;
- 更新烧录工具中的地址:Bootloader烧0x08000000,App烧0x08000800。
我曾在一个医疗设备项目中,客户要求Bootloader支持AES-128加密。我们把加密算法和密钥管理代码放在0x08000000 ~ 0x080007FF,App代码从0x08000800开始。但工程师疏忽,只改了烧录地址,没改链接脚本的ORIGIN,结果编译器仍把App的向量表放在0x08000000,烧录时覆盖了Bootloader的入口,设备上电后直接进入Bootloader死循环,无法加载App。
更隐蔽的坑在向量表偏移(Vector Table Offset)。Cortex-M芯片的SCB->VTOR寄存器,可以动态设置向量表位置。如果你的App不从0x08000000开始,就必须在App初始化时,手动设置VTOR:
// App从0x08004000开始,向量表也在0x08004000 SCB->VTOR = 0x08004000;否则,即使烧录正确,CPU复位后仍会从0x00000000(映射到0x08000000)取向量,导致跳转到Bootloader代码,而非App代码。
3.1 深度解析:为什么STM32的向量表必须放在Flash起始处?——从复位向量硬件设计说起
这个问题触及了ARM Cortex-M内核的底层设计。我们来看复位时的硬件行为链:
- 芯片上电,电源稳定后,复位信号(NRST)释放;
- CPU内核(ARM Cortex-M3/M4)的复位逻辑,会将PC寄存器强制加载为地址0x00000000;
- 同时,NVIC(Nested Vectored Interrupt Controller)的VTOR寄存器,被硬件初始化为0x00000000;
- CPU从0x00000000取第一个DWORD(4字节),作为初始堆栈指针(MSP);
- 从0x00000004取第二个DWORD,作为复位中断服务程序(Reset_Handler)的入口地址;
- 然后跳转执行Reset_Handler。
这个过程,是纯硬件实现的,不依赖任何软件。因此,0x00000000这个地址,必须存放有效的MSP和Reset_Handler地址。而STM32通过“地址映射(Remap)”机制,让0x00000000在物理上指向Flash的0x08000000。所以,你的向量表,必须放在Flash的0x08000000处,且前8个字节必须是:
- 0x08000000: MSP初值(如0x20005000)
- 0x08000004: Reset_Handler地址(如0x08000101)
如果你把向量表放在0x08004000,而没设置VTOR,CPU在0x00000000读到的将是Flash中0x08000000处的数据——那可能是Bootloader的代码,或者全0xFF(空白),结果就是MSP被设为一个非法地址,Reset_Handler跳转到乱码,系统立即HardFault。
这也是为什么STM32CubeMX生成的工程,startup文件里向量表是硬编码的:
__Vectors DCD __initial_sp ; MSP = 0x20005000 DCD Reset_Handler ; Reset_Handler = 0x08000101 DCD NMI_Handler ...而链接脚本确保这个__Vectors符号,被链接到.isr_vector段,且该段被分配到FLASH的起始位置。
关键结论:烧录地址0x08000000,本质是“向量表物理存放地址”,而非“代码起始地址”。代码可以跟在向量表后面(0x08000008),但向量表必须锚定在0x08000000。
4. 烧录地址填错的四大典型症状与精准排错链路
烧录地址填错,不会直接报错,而是以各种诡异症状呈现,让新手陷入“代码没问题,硬件没问题,就是不工作”的死循环。我整理了十年踩坑经验,归纳出四大典型症状,并给出一条可复现的排错链路,帮你5分钟定位根源。
4.1 症状一:程序完全不运行,串口无任何输出,但LED也不闪(“死机”)
这是最经典的症状,90%源于烧录地址与向量表位置不匹配。排错链路:
第一步:确认烧录工具日志
查看ST-Link Utility或J-Flash的日志,确认“Programming completed successfully”是否出现。如果出现,说明烧录动作完成,问题在启动逻辑;如果报“Verify failed”,则是烧录地址/大小错误,导致校验失败。第二步:用调试器强制连接,查看PC值
不烧录,直接用ST-Link Debugger连接芯片(Target → Connect),在Debug Configurations中,取消勾选“Reset and Run”。连接成功后,打开Registers视图,找到PC寄存器。如果PC=0x00000000,说明CPU停在复位状态,尚未执行任何指令——这是向量表缺失的铁证。第三步:读取Flash起始地址内容
在ST-Link Utility中,点击“Target → Read Memory”,Address填0x08000000,Length填0x20(32字节)。观察读出的16个DWORD:- 第一个DWORD(0x08000000)应该是MSP初值(如0x20005000);
- 第二个DWORD(0x08000004)应该是Reset_Handler地址(如0x08000101)。
如果全是0x00000000或0xFFFFFFFF,说明固件根本没烧到这个地址,烧录地址填错了。
第四步:检查链接脚本与烧录地址一致性
对比你的.ld文件中ORIGIN =的值,和烧录工具中填写的地址。必须完全一致。
我曾在一个STM32L432KC项目中,客户抱怨“烧录后LED不亮”。按上述步骤,发现PC=0x00000000,读Flash 0x08000000全是0xFF。一查烧录工具,地址填成了0x08000001(少了个0),导致整个固件偏移1字节,向量表错位。修正后,秒启。
4.2 症状二:程序能跑,但中断不响应,或响应错乱(“中断失灵”)
这通常是因为向量表被烧录到了错误位置,而VTOR寄存器未被正确设置。排错链路:
第一步:确认中断向量表物理位置
用objdump -d your_app.elf反汇编,找到.isr_vector段的VMA(Virtual Memory Address)。例如:Disassembly of section .isr_vector: 08000000 <__isr_vector>: 8000000: 20005000 .word 0x20005000 8000004: 08000101 .word 0x08000101这里VMA=0x08000000,说明向量表期望在0x08000000。
第二步:检查VTOR寄存器值
在调试器中,打开Memory视图,地址填0xE000ED08(SCB->VTOR寄存器地址)。如果值是0x00000000,说明VTOR没被设置,CPU仍在0x00000000取向量;如果值是0x08004000,而你的向量表在0x08000000,那就矛盾了。第三步:检查启动文件是否调用SystemInit()