欢迎光临
我们一直在努力

裸机开发:ARMv7-A中断驱动LED/蜂鸣器实战

无操作系统(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。 中断处理函数应避免长时间阻塞。

赞(0)
未经允许不得转载:171主机测评 » 裸机开发:ARMv7-A中断驱动LED/蜂鸣器实战
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址