You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AVR-GCC编译ATMega328p汇编时INT0中断向量表映射异常问题

问题原因

你遇到的问题核心是手动编写的中断向量表和avr-gcc默认链接的CRT(C运行时)启动代码自带的向量表冲突:
avr-gcc默认会将自带的启动代码插到固件的最开头位置,你自己写的.org 0x0000指令对应的代码并没有被放到0地址的向量区,而是被挤到了代码段后半部分,所以你写的向量跳转指令完全不会生效。同时Makefile里的MCU型号不统一也可能引发定义不匹配问题。


修复方案

提供两种可选修复方式,按需选择即可:

方案1:遵循avr-gcc标准规则,使用自动生成的向量表(推荐)

不需要手动编写向量表,让工具链自动填充向量地址,修改如下:

  1. 修改汇编代码
    • 把.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代码即可
    
  2. 修改Makefile
    统一MCU型号,把所有的-mmcu=atmega328改成-mmcu=atmega328p,避免头文件、向量表定义不匹配。

方案2:保留手动编写的向量表

如果你坚持要自己控制向量表,需要告诉链接器不要加载默认的启动代码,修改如下:

  1. 修改汇编代码
    • 把.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代码即可
    
  2. 修改Makefile
    • 统一MCU型号为-mmcu=atmega328p
    • 链接时添加-nostartfiles参数,禁用默认启动代码:
    main.elf:main.o
        $(CC) $(MCU) -nostartfiles -o main.elf main.o
    

两种方案修改后重新编译,反汇编就能看到INT0向量已经指向你写的INT0_vect入口。

内容的提问来源于stack exchange,提问作者Chandler Klüser

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 07:54:03