目录

学习嵌入式第五十三天

学习嵌入式第五十三天

中断

一、按键

1.初始化

  • 复用功能配置
    1. IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_B:低四位(0101)
    2. IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);
    3. SION(信号监控)1位:0 //DISABLED
    4. MUX_MODE(复用功能)4:0101
  • 电气特性
    1. IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF080);
    2. HYS(压摆率)1位:0 //HYS_0_Hysteresis_Disabled;
    3. PUS(上拉或下拉)2位:11 //PUS_3_22K_Ohm_Pull_Up — 22K Ohm Pull Up
    4. PUE(拉与保持选择)1位: 1 //选择拉
    5. PKE(拉或保持使能)1位: 1 //使能
    6. ODE(漏极开漏)1位: 0 //使能
    7. SPEED(速度)2位: 10 //SPEED_2_medium_100MHz_ — medium(100MHz)
    8. DSE(驱动能力)3位: 000 //DSE_0_output_driver_disabled_ — output driver disabled; 输出是才有用,所以关闭
    9. SRE(系统寄存器使能)1位: 0 //SRE_0_Slow_Slew_Rate — Slow Slew Rate
  • 方向寄存器
    1. GPIO1->GDIR &= ~(1 « 18)
    2. 0 INPUT — GPIO is configured as input
    3. 1 OUTPUT — GPIO is configured as output
  • GPIO工作时钟
    • 打开CCM Clock Gating Register ,CCM_CCGR1: GPIO1组所有引脚共用该CG门;

2.运行时开关检测

  • GPIO数据寄存器
    • GPIO1_DR:开关断开高电平,开关按下低电平

二、中断

1.概念

  • CPU能打断当前正在进行的工作,去处理更为紧急的任务,并且在处理完中断任务后,可以回到原先的地方继续工作
  • 中断流程
    1. 中断源发出中断请求
    2. CPU检查是否响应中断以及该终端是否被屏蔽
    3. 检查当前产生的中断的中断优先级
    4. 保护现场
    5. 执行中断服务函数
    6. 恢复现场

2.中断控制器GIC

  • 通用的中断控制器
  • 每个内核可以响应1020个中断源,其中015是SGI,1631是PPI,能够作为外设中断源的是SPI32~1019
  • Distributor
    1. SGI(Software-generated Interrupt),软件中断:由软件触发引起的中断,通过向寄存器GICD_SGIR 写入数据来触发,系统会使用 SGI 中断来完成多核之间的通信
    2. PPI(Private Peripheral Interrupt),私有中断: GIC 是支持多核的,每个核都有自己独有的中断。这些独有的中断势必是要指定的核心处理,而这些中断就叫做私有中断
    3. SPI(Shared Peripheral Interrupt),共享中断:可以被任意一个CPU处理,GIC会将SPI分配给最适合的CPU,可以避免单个CPU过载

3.协处理器(coprocrssor)

  • 功能:负责所有系统控制与配置和MMU的配置与管理,以及Cache的配置与管理,并负责虚拟化和安全设置和系统性能监视
  • 寄存器组:c0~c15
  • cp15寄存器读写
    • 读:MRC<c> <coproc>, <opc1>, <Rt>, <CRn>, <CRm>{, <opc2>}
    • MCR<c> <coproc>, <opc1>, <Rt>, <CRn>, <CRm>{, <opc2>}
  • 中断相关寄存器组
    • c0 registers:MIDR(Main ID Register):主标识寄存器,存储内核的一些基本信息,识别处理器型号和版本
    • c1 registers:SCTLR(System Control Register):系统控制寄存器,控制处理器核心功能
    • c12 registers:VBAR(Vector Base Address Register),异常向量表基地址寄存器,存储异常向量表的基地址,并决定CPU发生异常时跳转到哪里
    • c15 registers:CBAR(Configuration Base Address Register),配置基地址寄存器,存储GIC分发器的基地址,用于软件定位GIC硬件模块

三、中断实现流程


1.相关寄存器与关键偏移

模块基础来源说明
VBAR软件设置异常向量表基地址
CBARmrc p15,4,r1,c15,c0,0GIC Distributor 基地址(硬件定义)
CPU Interface 基地址CBAR + 0x2000GIC CPU Interface
IAR (Interrupt Acknowledge)CPU_IF + 0x0C读取当前待处理的中断 ID
EOIR (End Of Interrupt)CPU_IF + 0x10写入中断 ID 以结束处理

2.初始化阶段流程

1. 中断控制器初始化
void system_interrupt_init(void) {
    __set_VBAR(0x87800000);  // 设置异常向量表基地址
    GIC_Init();              // 初始化 GIC
}
2. 外设初始化
void key_init(void){
    IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);
    IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xF0B0);
    GPIO1->GDIR &= ~(1 << 18);          // 输入
    GPIO1->ICR2 |= (3 << 4);            // 双边沿 or 具体边沿配置
    GPIO1->IMR  |= (1 << 18);           // 解除屏蔽
    system_interrupt_register(GPIO1_Combined_16_31_IRQn, key_16_31_handler);
    GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);
    GIC_SetPriority(GPIO1_Combined_16_31_IRQn, 0);
}
3. 注册回调函数
static irq_interrupt_t interrupt_Vector_table[160] = { NULL };

void system_interrupt_register(unsigned int irq, irq_interrupt_t handler) {
    interrupt_Vector_table[irq] = handler;
}

3.异常向量与 IRQ 入口

启动时在 start.S 中放置向量表(VBAR 指向的区域):

_start:
    ldr pc, = _start_handler
    ldr pc, = _undefined_handler
    ldr pc, = _supervisor_handler
    ldr pc, = _prefetch_handler
    ldr pc, = _data_abort_handler
    ldr pc, = _not_use_handler
    ldr pc, = _irq_handler
    ldr pc, = _fiq_handler

4.IRQ 进入时的汇编执行流程

_irq_handler:
    sub lr, lr, #4              @ ARM 模式下返回地址校正
    stmfd sp!, {r0-r12, lr}     @ 保存通用寄存器 + lr

    mrc p15, 4, r1, c15, c0, 0  @ 读取 CBAR (GIC Distributor 基址)
    add r1, r1, #0x2000         @ CPU Interface 基地址 = CBAR + 0x2000
    ldr r0, [r1, #0x0C]         @ 读取 IAR,取得中断 ID

    stmfd sp!, {r0, r1}         @ 保护中断号与 CPU IF 基址
    cps #0x1F                   @ 进入 System 模式以调用 C 函数
    stmfd sp!, {lr}
    bl system_interrupt_handler @ 传递中断号(ABIr0 里是 irq
    ldmfd sp!, {lr}
    cps #0x12                   @ 回到 IRQ 模式
    ldmfd sp!, {r0, r1}         @ 恢复中断号与 CPU IF 基址

    str r0, [r1, #0x10]         @ 写 EOIR:告知 GIC 中断处理完毕
    ldmfd sp!, {r0-r12, pc}^    @ 恢复现场 + 异常返回( ^ 恢复 CPSR )

关键点:

  • mrc p15,4,... 获取 GIC 基址(CBAR)
  • IAR 读取时自动“应答”中断
  • EOIR 写入后才真正释放该中断
  • 通过模式切换 cps #0x1F 进入 System 模式调用 C 函数,避免在 IRQ 模式下栈/环境受限

5.根据中断ID调用相应的用户注册的处理函数

void system_interrupt_handler(IRQn_Type irq){
    if (interrupt_Vector_table[irq] != NULL) {
        interrupt_Vector_table[irq]();   // 调用已注册的设备具体处理函数
    }
}

6.代码与流程串联速览

main():
  system_interrupt_init()
  key_init()
  while(1){ ... }

key_init():
  配复用 & 上拉 & 输入
  配触发方式(ICR2)
  解除屏蔽(IMR)
  注册函数(system_interrupt_register)
  使能GIC

IRQ发生 -> _irq_handler():
  保存现场
  读取CBAR -> +0x2000 -> 读IAR得到irq
  进入System模式 -> 调用 system_interrupt_handler(irq)
    -> 调用 key_16_31_handler()
       -> led_nor()
       -> 清 GPIO1->ISR 位
  返回IRQ模式
  写EOIR
  恢复现场 + 异常返回

AR -> +0x2000 -> 读IAR得到irq
进入System模式 -> 调用 system_interrupt_handler(irq)
-> 调用 key_16_31_handler()
-> led_nor()
-> 清 GPIO1->ISR 位
返回IRQ模式
写EOIR
恢复现场 + 异常返回


---