AVR-GCC编译ATMega328p汇编时INT0中断向量表映射异常问题
问题原因
你遇到的问题核心是手动编写的中断向量表和avr-gcc默认链接的CRT(C运行时)启动代码自带的向量表冲突:
avr-gcc默认会将自带的启动代码插到固件的最开头位置,你自己写的.org 0x0000指令对应的代码并没有被放到0地址的向量区,而是被挤到了代码段后半部分,所以你写的向量跳转指令完全不会生效。同时Makefile里的MCU型号不统一也可能引发定义不匹配问题。
修复方案
提供两种可选修复方式,按需选择即可:
方案1:遵循avr-gcc标准规则,使用自动生成的向量表(推荐)
不需要手动编写向量表,让工具链自动填充向量地址,修改如下:
- 修改汇编代码
- 把
.include "m328pdef.inc"移到代码最开头,避免前面用到的寄存器定义缺失 - 删掉开头的
.org 0x0000和两条rjmp跳转指令,这部分由工具链自动生成 - 声明
INT0_vect为全局符号,让链接器识别这是INT0的中断服务程序
示例修改后代码开头:
.include "m328pdef.inc" .equ F_CPU, 16000000 .text .global main .global INT0_vect ; 声明为全局符号,链接器会自动匹配到INT0向量 INT0_vect: ldi r16,0x20 out PORTB,r16 reti .include "wait.asm" main: ; 后续保留你原有的main、loop代码即可 - 把
- 修改Makefile
统一MCU型号,把所有的-mmcu=atmega328改成-mmcu=atmega328p,避免头文件、向量表定义不匹配。
方案2:保留手动编写的向量表
如果你坚持要自己控制向量表,需要告诉链接器不要加载默认的启动代码,修改如下:
- 修改汇编代码
- 把
.include "m328pdef.inc"移到代码最开头 - 手动添加栈指针初始化逻辑,默认的CRT会负责初始化SP,禁用CRT后需要自己实现,否则函数调用、中断都会异常
- 声明自定义入口点
示例修改后代码开头:
.include "m328pdef.inc" .equ F_CPU, 16000000 .text .global main .global _start ; 自定义入口点 _start: .org 0x0000 rjmp main ; 复位向量 rjmp INT0_vect ; INT0向量 INT0_vect: ldi r16,0x20 out PORTB,r16 reti .include "wait.asm" main: ; 手动初始化栈指针 ldi r16, lo8(RAMEND) out SPL, r16 ldi r16, hi8(RAMEND) out SPH, r16 ; 后续保留你原有的中断配置、loop代码即可 - 把
- 修改Makefile
- 统一MCU型号为
-mmcu=atmega328p - 链接时添加
-nostartfiles参数,禁用默认启动代码:
main.elf:main.o $(CC) $(MCU) -nostartfiles -o main.elf main.o - 统一MCU型号为
两种方案修改后重新编译,反汇编就能看到INT0向量已经指向你写的INT0_vect入口。
内容的提问来源于stack exchange,提问作者Chandler Klüser
相关产品推荐
相关产品推荐

