ARM架构——中断系统详解

目录

一、中断的引用

[1.1 轮询方式的局限性](#1.1 轮询方式的局限性)

[1.2 中断系统简介](#1.2 中断系统简介)

[二、ARM 中断系统硬件架构](#二、ARM 中断系统硬件架构)

[2.1 通用中断控制器 GIC](#2.1 通用中断控制器 GIC)

[2.1.1 GIC 中断分类](#2.1.1 GIC 中断分类)

[2.1.2 架构组成](#2.1.2 架构组成)

[2.2 协处理器 CP15](#2.2 协处理器 CP15)

[2.2.1 访问指令](#2.2.1 访问指令)

[2.2.2 关键寄存器](#2.2.2 关键寄存器)

三、代码实现:中断驱动的按键控制

[3.1 向量表管理与初始化](#3.1 向量表管理与初始化)

[3.2 GPIO 与中断配置](#3.2 GPIO 与中断配置)

[3.3 中断服务函数 (ISR)](#3.3 中断服务函数 (ISR))

[3.4 主程序流程](#3.4 主程序流程)

[四、软件设计原则:低耦合与 OCP](#四、软件设计原则:低耦合与 OCP)


一、中断的引用

1.1 轮询方式的局限性

在学习中断之前,我们通常使用轮询(Polling) 的方式来处理按键等外设输入:

cpp 复制代码
while (1)
{  
    if ((GPIO1->DR & (1 << 18)) == 0)  // 检测到低电平
    {   
        led_nor();  
        beep_nor();  
    }  
    delay(0x7FFFFF); // 模拟大量复杂业务  
}  

**轮询方式的原理:**CPU 周期性地读取 GPIO 引脚状态,判断是否有按键按下。

轮询的致命缺陷:

  • 漏查风险:当主程序需要处理大量、复杂且耗时的业务时(例如模拟一个 delay(0x7FFFFF) 的长延时),CPU 无法及时检查按键状态。
  • 实时性差:就像"汽车刹车"这种高实时性场景,如果 CPU 正在处理其他任务而无法立即响应刹车信号,后果不堪设想。

因此,我们需要引入中断(Interrupt)。

1.2 中断系统简介

定义:中断是指 CPU 能打断当前正在进行的工作,去处理更为紧急的任务(中断服务函数),并且在处理完后,能自动回到原先的地方继续工作。

中断处理的标准流程:

  1. 中断请求:中断源(外设)发出信号。
  2. 中断响应检查:CPU 检查是否响应该中断,以及该中断是否被屏蔽。
  3. 优先级检查:GIC 判定当前中断的优先级。
  4. 保护现场:保存被暂停程序的上下文。
  5. 执行中断服务函数(ISR) :处理紧急任务。
  6. 恢复现场:还原上下文,继续执行原程序。

二、ARM 中断系统硬件架构

2.1 通用中断控制器 GIC

IMX6ULL 使用的是单核 Cortex-A7,其内部集成了 GIC v2.0 控制器。GIC 负责管理所有的中断源,并决定分发给哪个核心处理。GIC逻辑分区如下:
GIC逻辑分区

2.1.1 GIC 中断分类

根据 ARM GIC v2.0 规范,中断源被分为三类(共 1020 个 ID):

类型 全称 ID 范围 描述
SGI Software-generated Interrupt (软件中断) 0 - 15 由软件向寄存器 GICD_SGIR 写入数据触发,常用于多核间通信。
PPI Private Peripheral Interrupt (私有中断) 16 - 31 每个核心独有的中断(如 Generic Timer),必须由指定核心处理。
SPI Shared Peripheral Interrupt (共享中断) 32 - 1019 外部外设产生的中断(如 GPIO、I2C 等),所有核心共享。

注意:这里的 SPI 指的是共享中断,不要和通信协议 SPI 总线混淆。

2.1.2 架构组成

GIC 主要由两部分组成:

  • Distributor (分发器) :负责检测、排序和分发中断。
  • CPU Interface (CPU 接口) :负责将分发器发送的中断信号传输给处理器核心。

2.2 协处理器 CP15

在配置中断和系统底层时,离不开协处理器 CP15。它负责系统控制、MMU 配置、Cache 管理以及虚拟化等任务。

2.2.1 访问指令

CP15 包含 c0 到 c15 组寄存器,通过专用指令访问:

  • MRC:读 CP15 寄存器。
  • MCR:写 CP15 寄存器。

2.2.2 关键寄存器

  • MIDR (Main ID Register, c0) :存储内核的基本信息。
  • SCTLR (System Control Register,c1) :系统控制寄存器,其中两个位至关重要:
    • Bit 13 (V 位) :控制异常向量表的基地址。
      • 0:基地址为 0x00000000(软件可通过 VBAR 重映射)。
      • 1:基地址为 0xFFFF0000(高地址向量,不可重映射)。
    • Bit 12 (I 位) :指令 Cache 开关。
  • **VBAR (Vector Base Address Register, c12):**向量基地址寄存器,用于重新映射异常向量表的基地址。在IMX6ULL中,我们将向量表重映射到 0x87800000。
  • **CBAR (Configuration Base Address Register, c15):**配置基地址寄存器,指向GIC控制器的物理基地址。

三、代码实现:中断驱动的按键控制

本文的核心目标是实现 "按键中断触发 LED 翻转 + 蜂鸣器翻转",遵循 "低耦合、高可扩展" 的设计原则。

3.1 向量表管理与初始化

首先,我们需要定义一个中断服务函数指针数组,用于注册和管理中断。

cpp 复制代码
// 定义中断向量表 
typedef void (*irq_handler_t)(void); 
irq_handler_t Vector_table[160];  
  
// 系统中断初始化  
void system_interrupt_init(void)  
{  
  // 1. 重映射异常向量表基地址  
  __set_VBAR(0x87800000);  
  // 2. 初始化GIC控制器  
  GIC_Init();  
}  
  
// 注册中断处理函数  
int system_interrupt_register(IRQn_Type irq, irq_handler_t handler)  
{  
  if (irq > PMU_IRQ2_IRQn || irq < IOMUXC_IRQn) return -1;  
  if (handler == NULL) return -2;  
  Vector_table[irq] = handler; // 将中断号与处理函数关联  
  return 0;  
}  

3.2 GPIO 与中断配置

在 key_init 中,我们不仅配置 GPIO 为输入,还需要配置 GPIO 的中断触发方式和使能。

  • **复用配置:**将 UART1_CTS_B 复用为 GPIO1_IO18。
  • **电气特性:**配置上拉电阻和输入迟滞。
  • **中断触发:**配置 GPIO1->ICR2 寄存器,选择下降沿触发(按下时电平由高变低)。
  • **中断使能:**配置 GPIO1->IMR 寄存器,置位对应位以允许中断。
cpp 复制代码
void key_init(void)  
{  
  // 1. 复用功能配置  
  IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);  
  // 2. 电气特性配置  
  IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF0B0);  
  // 3. 引脚方向设置为输入模式  
  GPIO1->GDIR &= ~(1 << 18);  
  
  // 4. 配置中断触发方式 (ICR2寄存器,bit4-5对应GPIO1_18)  
  GPIO1->ICR2 |= (3 << 4); // 下降沿触发  
  
  // 5. 中断屏蔽寄存器 (IMR) - 解除屏蔽  
  GPIO1->IMR |= (1 << 18);  
  
  // 6. GIC配置  
  GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn); // 使能该中断  
  GIC_SetPriority(GPIO1_Combined_16_31_IRQn, 0); // 设置优先级  
  
  // 7. 注册回调函数  
  system_interrupt_register(GPIO1_Combined_16_31_IRQn, key_irq_handler);  
}

3.3 中断服务函数 (ISR)

当中断发生时,CPU会跳转到对应的处理函数。

cpp 复制代码
void key_irq_handler(void)  
{  
  // 检查GPIO中断状态寄存器 (ISR)  
  // 注意:ISR用于指示中断是否发生,IMR用于使能/屏蔽。  
  if ((GPIO1->ISR & (1 << 18)) != 0)  
  {  
    // 执行业务逻辑:翻转LED和蜂鸣器  
    led_nor();  
    beep_nor();  
  
    // 必须手动清除中断标志位  
    GPIO1->ISR |= (1 << 18);  
  }  
}  

3.4 主程序流程

主程序在初始化完成后,进入无限循环,此时CPU可以自由执行其他任务(如 g_delay),而按键的中断请求会被GIC捕获并打断主循环。

cpp 复制代码
int main(void)  
{  
  system_interrupt_init(); // 初始化中断系统  
  clock_cg_init();         // 开启时钟  
  beep_init();  
  led_init();  
  key_init();              // 配置按键中断  
  
  while (1)  
  {  
    g_delay(0x7FFFF);     // 主循环执行耗时任务,但不会阻塞中断响应  
  }  
  return 0;  
}  

四、软件设计原则:低耦合与 OCP

在编写中断驱动代码时,我们遵循了良好的软件工程原则:

  1. 低耦合:
    1. 中断模块只负责中断的底层处理(如向量表管理)。
    2. GPIO模块只负责引脚的输入输出。
    3. 业务逻辑(按键处理)独立封装。
  2. 开闭原则 (OCP):
    1. 对修改关闭:核心的中断分发逻辑(system_interrupt_handler)不需要因为新增设备而修改。
    2. 对扩展开放:当需要增加一个新的按键或设备时,只需调用 system_interrupt_handler 注册一个新的函数指针,无需改动核心代码。
相关推荐
爱吃生蚝的于勒2 小时前
【Linux】零基础学习命名管道-共享内存
android·linux·运维·服务器·c语言·c++·学习
ZL.zheng2 小时前
can范围唤醒的那些事?
arm开发
ShiMetaPi2 小时前
GM-3568JHF丨ARM+FPGA异构开发板应用开发教程:04 MIPI屏幕检测案例
arm开发·fpga开发·rk3568
麒qiqi2 小时前
IMX6ULL 裸机开发进阶:按键驱动(轮询→中断)+ 中断控制器 GIC 实战
单片机·嵌入式硬件
Y1rong2 小时前
STM32之SysTick
stm32·单片机·嵌入式硬件
Y1rong2 小时前
STM32之定时器(一)
stm32·单片机·嵌入式硬件
小龙报2 小时前
【C语言进阶数据结构与算法】LeetCode27 && LeetCode88顺序表练习:1.移除元素 2.合并两个有序数组
c语言·开发语言·数据结构·c++·算法·链表·visual studio
无限进步_3 小时前
C语言实现贪吃蛇游戏完整教程【最终版】
c语言·开发语言·c++·git·游戏·github·visual studio
广药门徒3 小时前
WINDOWS 10如何在PCB 等软件自由使用快捷键
嵌入式硬件