无操作系统(no OS)环境下实现按键中断驱动 LED/蜂鸣器响应的底层系统初始化与中断处理流程。
| 特征 | 说明 |
|---|---|
| 架构 | ARMv7-A(32 位),使用 cpsid i、mcr p15,...、VBAR、GIC 等关键词明确指向 Cortex-A 系列(如 NXP i.MX6ULL、Raspberry Pi 早期裸机等) |
| 运行环境 | 无操作系统(bare-metal),直接运行在硬件上,从 _reset_handler 开始执行 |
| 核心功能 | 按键(GPIO)触发中断 → 控制 LED 和蜂鸣器 |
| 中断控制器 | 使用 GIC(Generic Interrupt Controller),这是 Cortex-A 的标准中断控制器 |
| 启动流程 | 手动初始化 CPU 模式、栈、.bss、向量表、GIC、GPIO,完全自主控制启动过程 |
| 代码结构 | 包含汇编(start.s)、C 初始化函数、中断服务例程(ISR)注册机制 |
阶段 0:系统复位 & CPU 初始化
触发条件为上电或复位,入口函数为 _reset_handler(位于 start.s)。该阶段的主要目的是配置 CPU 基本工作状态,确保系统安全启动。
关键操作
禁用 IRQ 通过 cpsid i 指令实现,确保 CPU 内部禁止中断。
配置异常向量表基地址(VBAR)通过 mcr 或 __set_VBAR 完成,必须在 IRQ 使能前设置。
切换 CPU 模式(如 IRQ 模式 cps #0x12 和 SYS/用户模式 cps #0x1F)。
初始化栈指针为每种 CPU 模式单独设置,例如 ldr sp, =0x82000000。
清空 .bss 段通过循环写 0 实现,避免全局变量包含垃圾值。
注意事项
VBAR 设置必须在 IRQ 使能前完成。
栈指针需为每种模式独立初始化。
.bss 清零必须正确执行,否则可能导致未定义行为。
阶段 1:系统中断控制器初始化
触发条件为 main() 或初始化函数调用,主要函数为 system_interrupt_init()。
关键操作
通过 __set_VBAR(0x87800000) 设置异常向量表入口地址。
初始化 GIC 中断控制器:
- 禁止所有中断(
GICD_CTLR = 0)。 - 清除残留中断状态。
- 设置默认优先级阈值。
注意事项
GIC 初始化必须在 IRQ 使能前完成。
VBAR 地址需与 _irq_handler 入口严格对应。
阶段 2:GPIO(按键)初始化
触发条件为 key_init() 调用,目的是配置引脚复用、中断触发及注册服务函数。
关键操作
引脚复用通过 IOMUXC_SetPinMux() 将 UART1_CTS_B 改为 GPIO1_IO18。
电气特性通过 IOMUXC_SetPinConfig() 设置驱动能力、上拉/下拉等。
配置 GPIO 为输入模式:GPIO1->GDIR &= ~(1<<18)。
设置中断触发方式(如边沿触发):GPIO1->ICR2 = (3<<4)。
启用 GPIO 内部中断:GPIO1->IMR = (1<<18)。
注册中断服务函数:system_interrupt_register(GPIO1_Combined_16_31_IRQn, key_irq_handler)。
注意事项
GIC 需通过 GIC_EnableIRQ() 允许中断转发至 CPU。
中断优先级通过 GIC_SetPriority() 设置,数值越低优先级越高。
阶段 3:按键触发 GPIO 中断
触发条件为用户按下按键,硬件自动检测电平变化并生成中断请求。
关键操作
GPIO 硬件比较采样电平与 ICR 配置的触发条件(如上升/下降沿)。
中断状态寄存器 ISR.bit18 置 1,触发组合中断线 GPIO1_IO16~31 至 GIC。
注意事项
ISR 为写 1 清零类型,处理完成后需手动清除。
IMR 必须置 1 以允许中断信号传递。
阶段 4:GIC 转发中断
GIC 判断中断源是否使能,并检查优先级是否允许进入 CPU。
关键操作
GIC 读取 ISENABLER 确认中断使能状态。
检查 IPRIORITYR 优先级是否高于 CPU 当前阈值。
拉低 CPU 的 IRQ 引脚信号。
注意事项
未调用 GIC_EnableIRQ() 将导致 CPU 无法接收中断。
优先级过低或阈值过高可能屏蔽中断。
阶段 5:CPU 进入 IRQ 模式
CPU 检测 IRQ 信号后自动保存现场并跳转至异常向量表。
关键操作
硬件自动完成:
- 保存
SPSR_irq = CPSR。 - 保存
LR_irq = PC+4。 - 切换
CPSR.M为 IRQ 模式。 - 跳转至
VBAR + 0x18(_irq_handler入口)。
注意事项
_irq_handler 地址必须与 VBAR 设置一致。
IRQ 模式栈需预先初始化。
阶段 6:执行 IRQ 处理函数
处理流程为 _irq_handler → system_interrupt_handler() → key_irq_handler()。
关键操作
汇编保存现场:stmfd sp!, {r0-r12, lr}。
通过 Vector_table 调用 key_irq_handler() 执行用户逻辑(如 LED 翻转)。
清除 GPIO 中断标志:GPIO1->ISR |= (1 << 18)。
恢复现场并返回:ldmfd sp!, {r0-r12, pc}^。
注意事项
必须清除 ISR 标志以避免中断丢失。
保存和恢复现场需完整,防止寄存器污染。
阶段 7:中断处理完成返回
CPU 恢复至触发 IRQ 前的模式,用户程序继续执行。
关键操作
通过 ldmfd sp!, {r0-r12, pc}^ 恢复 CPSR 和 PC。
注意事项
返回指令需使用 ^ 以恢复 SPSR。
中断处理函数应避免长时间阻塞。