前言
在嵌入式裸机开发中,中断是绕不开的核心话题。本文记录了从按键轮询 到 GIC 中断控制器的学习过程,涵盖 i.MX6ULL 平台上的中断初始化原理与代码实践,适合正在学习 ARM 裸机开发的同学参考。
一、课前回顾
在进入中断之前,我们已经完成了以下工作:
- 利用 C 语言操作 GPIO 点亮 LED 灯
- 移植 NXP SDK 包,对寄存器访问驱动 Beep
- 重构工程并修改 Makefile
- 编写链接脚本,构建各段在 RAM 中的位置
每日提问复习:
-
volatile关键字的作用?- 告诉编译器该变量可能被意外修改,禁止优化,每次都要从内存中读取。
-
链接脚本的作用是什么?
- 描述各段(.text/.data/.bss 等)在内存中的布局位置。
二、按键驱动实验(轮询方式)
在正式进入中断之前,先用轮询方式实现一个按键驱动,通过对比来体会中断的优势。
1. 硬件介绍
开发板上有三个按键:
- 中间:复位按钮
- 左边:低功耗按钮
- 右边:用户可独立控制的试验按键
原理图分析:开关断开时高电平,开关按下时低电平。
2. 按键代码编写(轮询方式)
(1)初始化流程
① 复用功能配置(IOMUXC)
参考手册:IMX6ULL参考手册.pdf Chapter 32 ------ IOMUX Controller
以 UART1_CTS_B 引脚复用为 GPIO1_IO18 为例:
c
IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);
| 位域 | 值 | 说明 |
|---|---|---|
| SION | 0 | Input Path is determined by functionality |
| MUX_MODE | 0101 | ALT5 --- GPIO1_IO18 |
② 电气特性配置(PAD CTL)
c
IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF080);
| 位域 | 值 | 说明 |
|---|---|---|
| HYS | 0 | 迟滞比较器关闭 |
| PUS | 11 | 22K 上拉 |
| PUE | 1 | 选择拉 |
| PKE | 1 | 使能拉/保持 |
| ODE | 0 | 推挽输出 |
| SPEED | 10 | medium(100MHz) |
| DSE | 000 | 输出驱动关闭(输入时无用) |
| SRE | 0 | 慢压摆率 |
③ GPIO 方向寄存器(GPIOx_GDIR)
c
GPIO1->GDIR &= ~(1 << 18); // 0 = INPUT
④ GPIO 工作时钟(CCM_CCGR1)
GPIO1 组所有引脚共用该时钟门,需要打开。
(2)运行时检测
通过 GPIOx_DR 数据寄存器读取:
- 开关断开 → 高电平
- 开关按下 → 低电平
(3)轮询方式的问题
c
delay(0x7FFFFF); // 模拟耗时业务
当主循环承担大量、复杂、耗时 的业务时,轮询会漏查按键。
典型场景:汽车刹车系统。如果系统正在执行耗时任务,按键/刹车信号可能被漏掉,后果严重。
这,就是中断存在的意义。
三、中断方式
1. 中断的概念
中断:CPU 能打断当前正在进行的工作,去处理更紧急的任务,处理完毕后回到原处继续工作。
2. 中断流程
① 中断源发出中断请求
② CPU 检查是否响应中断以及是否被屏蔽
③ 检查中断优先级
④ 保护现场
⑤ 执行中断服务函数
⑥ 恢复现场
- 内核(Kernel):被打断的一方
- 外设:发出中断的一方
- GPIO 中断:外部中断(EINT)
3. 中断控制器 GIC
GIC(Generic Interrupt Controller):通用中断控制器。
参考文档:《ARM Generic Interrupt Controller V2.0.pdf》
(1)中断源分类
GIC V2.0 理论上支持 8 个内核,i.MX6ULL 只有一个内核(processor 0)。
每个内核可响应 1020 个中断源:
| 编号范围 | 类型 | 说明 |
|---|---|---|
| 0 ~ 15 | SGI | Software-generated Interrupt,软件中断,用于多核通信,通过 GICD_SGIR 触发 |
| 16 ~ 31 | PPI | Private Peripheral Interrupt,私有中断,每个核独有的中断 |
| 32 ~ 1019 | SPI | Shared Peripheral Interrupt,共享中断(注意:不是 SPI 总线) |
(2)GIC 的两大组件
- Distributor(分发器):管理所有中断源,决定分发到哪个 CPU
- CPU Interface(CPU 接口):与 CPU 核直接对接,处理中断的屏蔽、优先级、响应
参考 IMX6ULL参考手册.pdf 3.2 节:中断编号 0-128。
参考 MCIMX6Y2.h:IRQn_Type 定义了 0-159 个外设中断。
4. 协处理器 CP15
位置:《Cortex-A7 Technical Reference Manual》Chapter 4 System Control
(1)CP15 功能
- 系统控制与配置
- MMU 配置与管理
- Cache 配置与管理
- 虚拟化与安全
- 系统性能监控
(2)寄存器组 c0 - c15
主要关注:
c0 --- MIDR:存储内核基本信息。
c1 --- SCTLR(System Control Register)
| 位 | 名称 | 说明 |
|---|---|---|
| bit13 | V | 0 = 异常向量基址 0x00000000;1 = 高地址 0xFFFF0000 |
| bit12 | I | 0 = 指令 Cache 关闭(复位值);1 = 打开 |
示例代码(开启指令 Cache,使用普通向量表):
asm
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #(1 << 13)
orr r0, r0, #(1 << 12)
mcr p15, 0, r0, c1, c0, 0
c12 --- VBAR(Vector Base Address Register):异常向量表基地址
c
__set_VBAR(0x87800000);
c15 --- CBAR(Configuration Base Address Register):GIC 寄存器基地址
asm
mrc p15, 4, r0, c15, c0, 0
四、按中断流程编写代码
1. 降低程序耦合性
优秀的中断模块代码应满足:
- 满足用户基本需求
- 程序稳定可靠
- 满足 OCP 原则 (Open Close Principle)
- 对修改关闭,对扩展开放
- 通过封装 + 回调函数实现
2. 模块化设计
中断模块:统一管理中断注册、分发、回调。
GPIO 模块封装:
c
extern void gpio_init(GPIO_Type *gpio, int pin, gpio_config_t *config);
extern int gpio_read(GPIO_Type *gpio, int pin);
extern void gpio_write(GPIO_Type *gpio, int pin, int value);
通过注册回调函数的方式,新增中断源时无需修改中断核心代码,只需注册新的回调,符合 OCP 原则。
五、GIC 初始化代码详解
以 GICv2 为例,GIC_Init() 的核心逻辑如下:
c
FORCEDINLINE __STATIC_INLINE void GIC_Init(void)
{
uint32_t i;
uint32_t irqRegs;
GIC_Type *gic = (GIC_Type *)(__get_CBAR() & 0xFFFF0000UL);
irqRegs = (gic->D_TYPER & 0x1FUL) + 1;
/* 关闭所有 PPI、SGI、SPI */
for (i = 0; i < irqRegs; i++)
gic->D_ICENABLER[i] = 0xFFFFFFFFUL;
/* 设置优先级掩码:允许所有优先级 */
gic->C_PMR = (0xFFUL << (8 - __GIC_PRIO_BITS)) & 0xFFUL;
/* 无子优先级,所有优先级均可抢占 */
gic->C_BPR = 7 - __GIC_PRIO_BITS;
/* 使能 Distributor Group 0 */
gic->D_CTLR = 1UL;
/* 使能 CPU Interface Group 0 */
gic->C_CTLR = 1UL;
}
流程总结:
| 步骤 | 操作 | 目的 |
|---|---|---|
| 1 | 读 CBAR 获取 GIC 基址 | 定位寄存器 |
| 2 | 读 D_TYPER 算 IRQ 组数 | 确定循环范围 |
| 3 | 写 D_ICENABLER 全 1 | 关闭所有中断 |
| 4 | 设 C_PMR | 不屏蔽任何优先级 |
| 5 | 设 C_BPR | 无子优先级,全可抢占 |
| 6 | 使能 D_CTLR | 启动分发器 Group 0 |
| 7 | 使能 C_CTLR | 启动 CPU 接口 Group 0 |
六、总结
- 轮询 在复杂业务下会漏查事件,中断能及时响应紧急事件。
- GIC 是 ARM 处理中断的核心控制器,分为 Distributor 和 CPU Interface。
- CP15 协处理器负责系统控制,其中 VBAR 决定异常向量表基址,CBAR 决定 GIC 基址。
- 中断模块设计要遵循 OCP 原则,通过回调注册实现解耦。
- GIC 初始化的关键是:关闭所有中断 → 设置优先级 → 使能 Group 0。
掌握这些基础,就可以继续深入编写中断服务函数、配置外部中断触发方式(上升沿/下降沿/双边沿)等内容。
参考资料:
- 《ARM Generic Interrupt Controller V2.0.pdf》
- 《IMX6ULL参考手册.pdf》
- 《Cortex-A7 Technical Reference Manual.pdf》
- 《IMX6ULL_MINI_V2.2(Mini底板原理图).pdf》
如果本文对你有帮助,欢迎点赞收藏,后续会继续更新中断服务函数的具体实现~