ARM汇编学习笔记(五):从按键轮询到 GIC 中断控制器(ARM Cortex-A7 )

前言

在嵌入式裸机开发中,中断是绕不开的核心话题。本文记录了从按键轮询GIC 中断控制器的学习过程,涵盖 i.MX6ULL 平台上的中断初始化原理与代码实践,适合正在学习 ARM 裸机开发的同学参考。


一、课前回顾

在进入中断之前,我们已经完成了以下工作:

  • 利用 C 语言操作 GPIO 点亮 LED 灯
  • 移植 NXP SDK 包,对寄存器访问驱动 Beep
  • 重构工程并修改 Makefile
  • 编写链接脚本,构建各段在 RAM 中的位置

每日提问复习:

  1. volatile 关键字的作用?

    • 告诉编译器该变量可能被意外修改,禁止优化,每次都要从内存中读取。
  2. 链接脚本的作用是什么?

    • 描述各段(.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.hIRQn_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. 降低程序耦合性

优秀的中断模块代码应满足:

  1. 满足用户基本需求
  2. 程序稳定可靠
  3. 满足 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

六、总结

  1. 轮询 在复杂业务下会漏查事件,中断能及时响应紧急事件。
  2. GIC 是 ARM 处理中断的核心控制器,分为 DistributorCPU Interface
  3. CP15 协处理器负责系统控制,其中 VBAR 决定异常向量表基址,CBAR 决定 GIC 基址。
  4. 中断模块设计要遵循 OCP 原则,通过回调注册实现解耦。
  5. GIC 初始化的关键是:关闭所有中断 → 设置优先级 → 使能 Group 0。

掌握这些基础,就可以继续深入编写中断服务函数、配置外部中断触发方式(上升沿/下降沿/双边沿)等内容。


参考资料:

  • 《ARM Generic Interrupt Controller V2.0.pdf》
  • 《IMX6ULL参考手册.pdf》
  • 《Cortex-A7 Technical Reference Manual.pdf》
  • 《IMX6ULL_MINI_V2.2(Mini底板原理图).pdf》

如果本文对你有帮助,欢迎点赞收藏,后续会继续更新中断服务函数的具体实现~

相关推荐
既然如此,那就开摆1 小时前
2023_Kirillov_SAM_总结
笔记·学习·遥感
志尊宝1 小时前
Vue3 零基础每日笔记(048):嵌套路由与命名视图——后台管理系统布局雏形
前端·javascript·vue.js·笔记·html5
西城微科方案开发2 小时前
基于SIC8F0535芯片的红外额温枪开发方案
单片机·嵌入式硬件
摇滚侠2 小时前
《Spring Boot 3:高级与架构设计》第 2 章 IOC容器的高级机制 BeanFactoryPostProcessor 个人理解 6
java·spring boot·笔记·后端
LCG元2 小时前
STM32F103 NTC 热敏电阻测温实战:分压网络设计、Steinhart-Hart 标定与 ADC 采样优化实测
stm32·单片机·嵌入式硬件
M78佐菲2 小时前
ARM学习笔记(五)
linux·arm开发·笔记·嵌入式硬件·学习
恒锐丰科技林技术员2 小时前
EG2184 半桥栅极驱动芯片详解:高压半桥电路的性价比之选
经验分享·嵌入式硬件·硬件工程
志尊宝2 小时前
Vue3 零基础每日笔记(052):路由常见坑一次排雷——404、刷新丢参数、部署白屏
前端·javascript·vue.js·笔记·html5
_Ningye2 小时前
通用异步收发器 UART
单片机