学习嵌入式第五十三天
文章目录
- 中断
- 一、按键
- 1.初始化
- 2.运行时开关检测
- 二、中断
- 1.概念
- 2.中断控制器GIC
- 3.协处理器(coprocrssor)
- 三、中断实现流程
- 1.相关寄存器与关键偏移
- 2.初始化阶段流程
- 1. 中断控制器初始化
- 2. 外设初始化
- 3. 注册回调函数
- 3.异常向量与 IRQ 入口
- 4.IRQ 进入时的汇编执行流程
- 5.根据中断ID调用相应的用户注册的处理函数
- 6.代码与流程串联速览
中断
一、按键
1.初始化
- 复用功能配置
- IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_B:低四位(0101)
- IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);
- SION(信号监控)1位:0 //DISABLED
- MUX_MODE(复用功能)4:0101
- 电气特性
- IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF080);
- HYS(压摆率)1位:0 //HYS_0_Hysteresis_Disabled;
- PUS(上拉或下拉)2位:11 //PUS_3_22K_Ohm_Pull_Up — 22K Ohm Pull Up
- PUE(拉与保持选择)1位: 1 //选择拉
- PKE(拉或保持使能)1位: 1 //使能
- ODE(漏极开漏)1位: 0 //使能
- SPEED(速度)2位: 10 //SPEED_2_medium_100MHz_ — medium(100MHz)
- DSE(驱动能力)3位: 000 //DSE_0_output_driver_disabled_ — output driver disabled; 输出是才有用,所以关闭
- SRE(系统寄存器使能)1位: 0 //SRE_0_Slow_Slew_Rate — Slow Slew Rate
- 方向寄存器
- GPIO1->GDIR &= ~(1 << 18)
- 0 INPUT — GPIO is configured as input
- 1 OUTPUT — GPIO is configured as output
- GPIO工作时钟
- 打开CCM Clock Gating Register ,CCM_CCGR1: GPIO1组所有引脚共用该CG门;
2.运行时开关检测
- GPIO数据寄存器
- GPIO1_DR:开关断开高电平,开关按下低电平
二、中断
1.概念
- CPU能打断当前正在进行的工作,去处理更为紧急的任务,并且在处理完中断任务后,可以回到原先的地方继续工作
- 中断流程
- 中断源发出中断请求
- CPU检查是否响应中断以及该终端是否被屏蔽
- 检查当前产生的中断的中断优先级
- 保护现场
- 执行中断服务函数
- 恢复现场
2.中断控制器GIC
- 通用的中断控制器
- 每个内核可以响应1020个中断源,其中015是SGI,1631是PPI,能够作为外设中断源的是SPI32~1019
- Distributor
- SGI(Software-generated Interrupt),软件中断:由软件触发引起的中断,通过向寄存器GICD_SGIR 写入数据来触发,系统会使用 SGI 中断来完成多核之间的通信
- PPI(Private Peripheral Interrupt),私有中断: GIC 是支持多核的,每个核都有自己独有的中断。这些独有的中断势必是要指定的核心处理,而这些中断就叫做私有中断
- 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 | 软件设置 | 异常向量表基地址 |
CBAR | mrc p15,4,r1,c15,c0,0 | GIC Distributor 基地址(硬件定义) |
CPU Interface 基地址 | CBAR + 0x2000 | GIC 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_handlerldr pc, = _undefined_handlerldr pc, = _supervisor_handlerldr pc, = _prefetch_handlerldr pc, = _data_abort_handlerldr pc, = _not_use_handlerldr pc, = _irq_handlerldr pc, = _fiq_handler
4.IRQ 进入时的汇编执行流程
_irq_handler:sub lr, lr, #4 @ ARM 模式下返回地址校正stmfd sp!, {r0-r12, lr} @ 保存通用寄存器 + lrmrc p15, 4, r1, c15, c0, 0 @ 读取 CBAR (GIC Distributor 基址)add r1, r1, #0x2000 @ CPU Interface 基地址 = CBAR + 0x2000ldr r0, [r1, #0x0C] @ 读取 IAR,取得中断 IDstmfd sp!, {r0, r1} @ 保护中断号与 CPU IF 基址cps #0x1F @ 进入 System 模式以调用 C 函数stmfd sp!, {lr}bl system_interrupt_handler @ 传递中断号(ABI:r0 里是 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)使能GICIRQ发生 -> _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
恢复现场 + 异常返回
---