文章目录
- 前言
- 一、中断系统整体三层架构
- 二、GIC中断控制器:三类中断源
- 三、GICC核心寄存器:中断的“入场”与“退场”握手
- 1. GICC_IAR:中断应答寄存器
- 2. GICC_EOIR:中断结束寄存器
- 四、中断全链路执行流程详解
- 阶段1:系统启动与中断初始化
- 阶段2:硬件中断触发
- 阶段3:汇编层:IRQ中断入口处理
- 阶段4:C语言层:中断分发与业务处理
- 阶段5:中断返回与现场恢复
- 五、实战代码解析:GPIO按键中断驱动
- 5.1 按键初始化函数 key_init
- 5.2 按键中断处理函数
- 六、学习避坑:中断开发常见问题
- 七、学习总结
前言
今天是ARM嵌入式学习的第四天,核心攻克了Cortex-A7的中断系统。从最底层的GPIO硬件引脚、IOMUX引脚复用,到GIC通用中断控制器的工作原理,再到CPU的异常向量表、汇编中断入口,最后落地到C语言的二级中断向量表和外设驱动代码,完整走通了「按键按下→LED翻转」的整条中断链路。
本文把今天所有知识点按架构梳理,结合寄存器、汇编、C代码三层拆解,彻底搞懂中断到底是怎么从硬件跑到软件里的。
一、中断系统整体三层架构
整个ARM中断系统从上到下可以分为三层,对应硬件信号的流向:
中断信号的完整流向: GPIO引脚电平变化 → GPIO外设置中断标志 → GIC仲裁路由 → CPU触发IRQ异常 → 执行中断处理代码
二、GIC中断控制器:三类中断源
GICv2(Cortex-A7配套的中断控制器版本)将所有中断按来源和使用场景分为三大类,这是理解中断号的基础:
| SGI | Software Generated Interrupt(软件生成中断) | 0~15 | 软件写寄存器触发 | 可定向发送给任意CPU核 | 多核核间通信(IPC) |
| PPI | Private Peripheral Interrupt(私有外设中断) | 16~31 | CPU核内置私有外设 | 每个核独立一套,不可路由 | 内核通用定时器、PMU性能监控 |
| SPI | Shared Peripheral Interrupt(共享外设中断) | 32~1019 | 片上公共外设 | 全局唯一,可路由到任意CPU核 | UART、GPIO、SPI、以太网等外设中断 |
我们日常裸机开发接触最多的就是SPI,比如GPIO、串口的中断都属于这一类。
三、GICC核心寄存器:中断的“入场”与“退场”握手
每个CPU核都对应一个独立的GICC(CPU接口),它是GIC和CPU直接交互的窗口,有两个最核心的寄存器,控制着中断的完整生命周期。
1. GICC_IAR:中断应答寄存器
- 全称:Interrupt Acknowledge Register
- 偏移地址:GICC基地址 + 0x0C
- 操作方式:只读
- 核心作用:软件读取该寄存器,硬件自动完成两件事:
- 返回当前触发的中断号(Interrupt ID),后续作为C函数的入参
- GIC内部将该中断状态从Pending(挂起)修改为Active(正在处理)
2. GICC_EOIR:中断结束寄存器
- 全称:End Of Interrupt Register
- 偏移地址:GICC基地址 + 0x10
- 操作方式:只写
- 核心作用:软件将中断号写入该寄存器,通知GIC中断处理完毕,GIC清除该中断的Active状态。
❗ 重要提醒:如果不写EOIR,GIC会认为中断一直在处理中,同优先级及更低优先级的中断将永远无法触发,表现为“进一次中断后再也不进了”。
四、中断全链路执行流程详解

这是今天最核心的内容,我们按时间顺序,把从复位启动到中断触发再到返回的完整流程拆成5个阶段,结合汇编和C代码彻底讲透。
阶段1:系统启动与中断初始化
芯片上电后,先执行汇编复位入口,完成最底层硬件初始化,再进入C语言的外设初始化。
1.1 汇编复位流程(_reset_handler)
1.2 C语言中断初始化
在main中调用初始化函数,完成三层配置:
阶段2:硬件中断触发
阶段3:汇编层:IRQ中断入口处理
CPU检测到IRQ信号后,硬件自动保存CPSR和返回地址,切换到IRQ模式,跳转到_irq_handler,随后执行汇编代码:
阶段4:C语言层:中断分发与业务处理
进入C语言的中断分发函数后,通过二级向量表找到对应的外设处理函数:
- 读GPIO的ISR寄存器,判断具体是哪个引脚触发的中断(因为多个引脚共用一个GIC中断号)
- 执行业务逻辑:翻转LED、控制蜂鸣器等
- 写GPIO的ISR寄存器,清除中断标志(W1C机制,写1清0)
阶段5:中断返回与现场恢复
C函数执行完毕,返回汇编入口,完成中断收尾:

五、实战代码解析:GPIO按键中断驱动
下面结合实际代码,看每个配置在驱动里是怎么落地的。
5.1 按键初始化函数 key_init
void key_init(void)
{
//1. 引脚复用:将UART1_CTS_B引脚复用为GPIO1_IO18,关闭强制输入
IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);
//2. 引脚电气特性:配置上拉、施密特触发等参数
IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF0B0);
//3. GPIO方向:第18位清零,设置为输入模式
GPIO1->GDIR &= ~(1 << 18);
//4. 中断触发方式:上升沿触发(ICR2管pin16~31,pin18对应bit4~5)
GPIO1->ICR2 |= (3 << 4);
//5. 引脚中断屏蔽:置1使能该引脚的中断输出
GPIO1->IMR |= (1 << 18);
//6. GIC设置中断优先级(99为对应中断号,0为最高优先级)
GIC_SetPriority(99, 0);
//7. 注册中断处理函数到软件二级向量表
system_interrupt_register(GPIO1_Combined_16_31_IRQn, key_irq_hangdler);
//8. GIC层面使能中断,允许中断转发到CPU
GIC_EnableIRQ(99);
}
5.2 按键中断处理函数
void key_irq_hangdler(void)
{
// 判断是否是pin18触发的中断(GPIO合并中断必须做引脚判断)
if ((GPIO1->ISR & (1 << 18)) != 0)
{
// 业务逻辑:翻转LED、控制蜂鸣器
led_nor();
beep_nor();
// 清除GPIO中断标志(W1C机制,直接写1对应位即可)
GPIO1->ISR = (1 << 18);
}
}
六、学习避坑:中断开发常见问题
七、学习总结
今天第五天的学习,彻底把ARM中断的“黑盒”打开了:
- 理解了GIC的三类中断源和仲裁路由机制
- 搞懂了IAR和EOIR两个寄存器的握手逻辑
- 打通了从硬件引脚→GIC→汇编入口→C语言分发→外设处理的完整链路
- 掌握了GPIO中断驱动的编写方法和常见坑点
中断是嵌入式实时性的核心,理解了全链路之后,后面学习UART、定时器等外设中断就都是同一个套路,只是外设寄存器配置不同而已。
网硕互联帮助中心





评论前必须登录!
注册