i.MX6ULL 中断(一)
2026/7/22 5:01:09 网站建设 项目流程

1. 中断的基本概念

中断是嵌入式系统中一种重要的机制,它允许外设或软件在需要时暂停 CPU 当前正在执行的任务,转而处理一个更紧急的事件。处理完该事件后,CPU 再返回到被暂停的任务继续执行。

可以把中断想象成一个电话铃声:你正在写代码(执行主程序),突然电话响了(中断请求),你停下手中的工作去接电话(执行中断服务函数),接完电话后继续写代码(恢复现场)。

1.1 中断处理流程

一个完整的中断处理流程通常包含以下步骤:

  1. 中断源发出中断请求:外设(如按键、定时器、串口)检测到特定事件,向 CPU 发送一个中断信号。
  2. CPU 检查中断响应条件:CPU 在当前指令执行完毕后,检查是否有中断请求,以及该中断是否被屏蔽(全局中断使能位或中断屏蔽寄存器)。
  3. 中断优先级比较:如果同时有多个中断请求,中断控制器(如 GIC)会比较它们的优先级,选择优先级最高的中断进行处理。
  4. 保护现场:CPU 自动(或通过软件)将当前正在执行的程序的上下文(如 PC、LR、通用寄存器等)保存到栈中,以便中断返回后能恢复执行。
  5. 执行中断服务函数:CPU 跳转到中断向量表中对应的入口地址,执行中断服务函数(ISR)。
  6. 恢复现场:中断服务函数执行完毕后,从栈中恢复之前保存的上下文,并执行中断返回指令,使 CPU 回到被中断的程序继续执行。

2. GIC(通用中断控制器)介绍

在 ARM Cortex-A7 架构的 i.MX6ULL 处理器中,中断管理由GIC(Generic Interrupt Controller,通用中断控制器)负责。GIC 是连接外设中断源和 CPU 核心的桥梁,负责中断的使能、屏蔽、优先级排序和分发。

2.1 GIC 版本与 i.MX6ULL

  • GIC V1:用于 Cortex-A5 等早期处理器。
  • GIC V2:用于 32 位处理器,如 Cortex-A7、Cortex-A9。i.MX6ULL 使用的就是 GIC V2
  • GIC V3 - V4:用于 64 位处理器,如 Cortex-A53(手机芯片)。
  • GIC V5:重新架构,用于数百核处理器(服务器、高算力芯片)。

2.2 GIC 支持的中断类型

GIC 支持多种中断类型,i.MX6ULL 的 GIC 最多可支持 1020 个中断,但实际只使用了 0 - 159 号(共 160 个)。主要分为三类:

  • SGI(Software Generated Interrupt,软件中断):编号 0 - 15,由软件通过写寄存器触发,常用于核间通信。
  • PPI(Private Peripheral Interrupt,私有外设中断):编号 16 - 31,每个 CPU 核心私有的中断,如定时器、看门狗等。
  • SPI(Shared Peripheral Interrupt,共享外设中断):编号 32 - 1019,由外部设备(如 GPIO、UART、I2C)产生的中断,所有 CPU 核心都可以处理。

2.3 GIC 关键寄存器

在汇编代码中,我们通过协处理器(CP15)来获取 GIC 的基地址,并操作其关键寄存器:

  • GIC_IAR(Interrupt Acknowledge Register):读取该寄存器可以获取当前发生的中断号。
  • GIC_EOIR(End Of Interrupt Register):中断服务函数执行完毕后,向该寄存器写入中断号,通知 GIC 中断处理结束。

3. i.MX6ULL 配置按键中断的完整流程

下面我们以 i.MX6ULL 开发板上的按键Key0(连接到 GPIO1_IO18)为例,详细讲解如何配置一个下降沿触发的中断。

3.1 硬件连接

  • 按键 Key0 连接到GPIO1_IO18引脚。
  • 按键按下时,引脚电平从高电平变为低电平(下降沿)。
  • 按键松开时,引脚电平从低电平变为高电平(上升沿)。

目标:当按键按下时,触发中断,在中断服务函数中点亮 LED。

3.2 软件配置步骤

配置一个 GPIO 中断需要完成以下步骤:

  1. 配置引脚复用功能:将UART1_CTS_B引脚复用为GPIO1_IO18
  2. 配置引脚电气属性:设置上下拉、驱动能力等。
  3. 配置 GPIO 方向为输入GPIO1->GDIR寄存器对应位清零。
  4. 配置中断触发方式:设置GPIO1->ICR2寄存器,选择下降沿触发。
  5. 使能 GPIO 引脚中断:设置GPIO1->IMR寄存器对应位为 1。
  6. 初始化 GIC:调用GIC_Init()函数。
  7. 在 GIC 中使能该中断:调用GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn)
  8. 编写中断服务函数:在system_interrupt_handler中处理中断。
  9. 编写汇编启动代码:设置异常向量表、切换模式、设置栈指针等。

4. 代码实现

4.1 按键初始化函数 (key.c)

#include"key.h"#include"MCIMX6Y2.h"#include"fsl_iomuxc.h"#include"core_ca7.h"voidkey_init(void){// 1. 引脚复用: 将 UART1_CTS_B 复用为 GPIO1_IO18IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);// 2. 电气属性: 设置上下拉、驱动能力等 (0xF0B0 为具体配置值)IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xF0B0);// 3. GPIO方向: 设置为输入模式 (GDIR 对应位清零)GPIO1->GDIR&=~(1<<18);// 4. 配置中断触发方式: 下降沿触发 (ICR2 对应位设置为 11)GPIO1->ICR2|=(3<<4);// 5. 使能GPIO1_18引脚中断 (IMR 对应位置1)GPIO1->IMR|=(1<<18);// 6. GIC使能中断GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);}

4.2 主函数 (main.c)

#include"MCIMX6Y2.h"#include"fsl_iomuxc.h"#include"core_ca7.h"#include"led.h"#include"beep.h"#include"key.h"voidenable_clock(void){// 使能所有外设时钟CCM->CCGR0=0xFFFFFFFF;CCM->CCGR1=0xFFFFFFFF;CCM->CCGR2=0xFFFFFFFF;CCM->CCGR3=0xFFFFFFFF;CCM->CCGR4=0xFFFFFFFF;CCM->CCGR5=0xFFFFFFFF;CCM->CCGR6=0xFFFFFFFF;}voidsystem_interrupt_init(void){// 初始化 GICGIC_Init();}// 系统中断服务函数voidsystem_interrupt_handler(intnum){// 判断是否为 GPIO1_Combined_16_31 中断if(num==GPIO1_Combined_16_31_IRQn){// 判断是否为 GPIO1_IO18 引脚产生的中断if((GPIO1->ISR&(1<<18))!=0){// 点亮 LEDled_on();// 清除中断标志位 (向 ISR 对应位写1)GPIO1->ISR|=(1<<18);}}}voiddelay(unsignedintn){while(n--);}intmain(void){// 1. 使能外设时钟enable_clock();// 2. GIC初始化system_interrupt_init();// 3. LED初始化led_init();// 4. 蜂鸣器初始化beep_init();// 5. 按键初始化 (配置中断)key_init();// 初始状态: 关闭LED和蜂鸣器led_off();beep_off();while(1){// 主循环: 等待中断发生delay(0xFFFFFFFF);}return0;}

4.3 汇编启动文件 (start.S)

汇编代码负责最底层的初始化工作,包括设置异常向量表、切换 CPU 工作模式、设置栈指针、使能中断等。

.global _start _start: ldr pc, =_reset_handler ldr pc, =_undefine_handler ldr pc, =_svc_handler ldr pc, =_prefetch_aboart_handler ldr pc, =_data_abort_handler ldr pc, =_reserved_handler ldr pc, =_irq_handler ldr pc, =_fiq_handler _fiq_handler: ldr pc, =_fiq_handler _irq_handler: // 1. 修正返回地址: IRQ模式下,LR保存的是当前指令+4,需要减4 subs lr, lr, #4 // 2. 获取GIC基地址 (通过CP15协处理器读取CBAR寄存器) mrc p15, 4, r1, c15, c0, 0 add r1, r1, #0x2000 // 3. 读取GIC_IAR寄存器,获取中断号 ldr r0, [r1, #0x0C] // 4. 保护现场: 保存寄存器到栈中 stmfd sp!, {r0-r12, lr} stmfd sp!, {r0, r1} // 5. 切换到System模式,以便使用System模式的栈 cps #0x1F stmfd sp!, {lr} // 6. 调用C语言中断服务函数,参数为中断号 bl system_interrupt_handler // 7. 恢复现场 ldmfd sp!, {lr} cps #0x12 // 切换回IRQ模式 ldmfd sp!, {r0, r1} // 8. 写GIC_EOIR寄存器,通知GIC中断处理结束 str r0, [r1, #0x10] ldmfd sp!, {r0-r12, pc}^ _reserved_handler: ldr pc, =_reserved_handler _data_abort_handler: ldr pc, =_data_abort_handler _prefetch_aboart_handler: ldr pc, =_prefetch_aboart_handler _svc_handler: ldr pc, =_svc_handler _undefine_handler: ldr pc, =_undefine_handler _reset_handler: // 1. 切换工作模式到IRQ模式 cps #0x12 // 2. 使能IRQ中断 (CPSR的I位清零) cpsie i // 3. 设置IRQ模式下的栈指针 ldr sp, =0x86000000 // 4. 切换工作模式到System模式 cps #0x1F // 5. 设置System模式下的栈指针 ldr sp, =0x84000000 // 6. 清空BSS段 bl _bss_clear // 7. 打开I-Cache (指令缓存) bl _enable_icache // 8. 设置异常向量表重映射地址 bl _set_vbar // 9. 跳转到main函数 b main b finished _enable_icache: mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #(1 << 13) // 清除V位 orr r0, r0, #(1 << 12) // 设置I位 mcr p15, 0, r0, c1, c0, 0 bx lr _set_vbar: ldr r0, =0x87800000 mcr p15, 0, r0, c12, c0, 0 bx lr _bss_clear: ldr r0, = __bss_start ldr r1, = __bss_end loop: mov r2, #0 str r2, [r0] add r0, r0, #4 cmp r0, r1 blt loop bx lr finished: b finished

5. 代码详解

5.1 中断服务函数执行流程

当按键按下时,整个中断处理流程如下:

  1. 外设(GPIO1_IO18)检测到下降沿,向 GIC 发送中断信号。
  2. GIC接收中断,经过优先级判断后,向 CPU 核心发送 IRQ 信号。
  3. CPU 核心响应 IRQ,自动切换到 IRQ 模式,并跳转到异常向量表中的_irq_handler
  4. _irq_handler(汇编)
    • 修正返回地址(subs lr, lr, #4)。
    • 通过 CP15 协处理器获取 GIC 基地址。
    • 读取GIC_IAR寄存器获取中断号。
    • 保护现场(保存寄存器到栈)。
    • 切换到 System 模式,调用 C 函数system_interrupt_handler
  5. system_interrupt_handler(C 语言)
    • 判断中断号是否为GPIO1_Combined_16_31_IRQn
    • 判断具体中断源是否为GPIO1_IO18(通过GPIO1->ISR寄存器)。
    • 执行中断处理:点亮 LED。
    • 清除中断标志位(向GPIO1->ISR写 1)。
  6. 返回汇编
    • 恢复现场。
    • GIC_EOIR寄存器写入中断号,通知 GIC 处理完毕。
    • 执行ldmfd sp!, {r0-r12, pc}^指令,从 IRQ 模式返回,恢复被中断的程序继续执行。

5.2 关键点说明

  • GIC_IARGIC_EOIR:这两个寄存器是 GIC 编程的核心。IAR用于获取中断号,EOIR用于告知 GIC 中断处理结束。在汇编代码中,GIC_IAR的偏移地址是0x0CGIC_EOIR的偏移地址是0x10
  • GPIO1->ISR寄存器:当 GPIO 引脚检测到配置的中断事件(如下降沿)时,ISR寄存器的对应位会自动置 1。在中断服务函数中,必须通过向该位写 1 来清除中断标志,否则会一直触发中断。
  • cpsie i指令:用于使能 CPU 的 IRQ 中断。如果不执行这条指令,即使外设和 GIC 都配置好了,CPU 也不会响应中断。
  • 栈指针设置:IRQ 模式和 System 模式需要分别设置栈指针,因为它们在发生中断时会使用不同的栈空间。

6. 总结

本文详细介绍了 i.MX6ULL 处理器上按键中断的完整配置流程,从中断的基本概念、GIC 中断控制器的工作原理,到具体的代码实现和汇编启动代码。通过这个例子,可以清晰地理解 ARM Cortex-A7 架构下中断处理的完整链路:外设 -> GIC -> CPU -> 异常向量表 -> 汇编处理 -> C 语言服务函数

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询