嵌入式学习的第四十八天-中断+OCP原则

一、GIC通用中断控制器

1.GIC通用中断控制器

GIC 是 ARM 公司给 Cortex-A/R 内核提供的一个中断控制器,GIC接收众多外部中断,然后对其进行处理,最终通过VFIQ、VIRQ、FIQ 和 IRQ给内核;这四个 信号的含义如下: VFIQ:虚拟快速 FIQ。 VIRQ:虚拟 IRQ。 FIQ:快速中断 IRQ。 IRQ:中断 IRQ。

2.GIC中断分类

SPI(Shared Peripheral Interrupt),共享中断, (注意!不是 SPI 总线那个中断),这类中断泛指所有的 外设中断;
PPI(Private Peripheral Interrupt),私有中断,我们说了 GIC 是支持多核的,每个核肯定有自己独有 的中断。这些独有的中断肯定是要指定的核心处理,因此这些中断就叫做私有中断;
SGI(Software-generated Interrupt),软件中断,由软件触发引起的中断,通过向寄存器GICD_SGIR 写入数据来触发,系统会使用 SGI 中断来完成多核之间的通信。
从图中可以明显看出,Distributor(分发器)可以分发所有共享中断给8个内核,但是私有中断和 软件中断只出现自己独自的内核中。
中断ID:中断源有很多,为了区分这些不同的中断源肯定要给他们分配一个唯一 ID,这些 ID 就 是中断 ID。每一个 CPU 最多支持 1020 个中断 ID,中断 ID 号为 ID0~ID1019。
ID0~ID15:这 16 个 ID 分配给 SGI; ID16~ID31:这 16 个 ID 分配给 PPI;ID32~ID1019:
这 988 个 ID 分配给 SPI。

3.GIC的组成

由分发器 (1个)、CPU接口(几核就几个)

(1)分发器

功能:

  • 全局中断使能控制;
  • 控制每一个中断的使能或者关闭;
  • 设置每个中断的优先级;
  • 设置每个中断的目标处理器列表;
  • 设置每个外部中断的触发模式:电平触发或边沿触发;
  • 设置每个中 断属于组 0 还是组 1;
(2)CPU接口

功能:

  • 使能或者关闭发送到 CPU Core 的中断请求信号;
  • 应答中断;
  • 通知中断处理完成;
  • 设置优先级掩码,通过掩码来设 置哪些中断不需要上报给 CPU Core;
  • 定义抢占策略;
  • 当多个中断到来的时候,选择优先级最高的 中断通知给 CPU Core;

4.协处理器

总共由16个,cp0~cp15;其中最常使用的cp15

(1)作用

  • 获取GIC的基地址(CBAR)
  • MMU的配置(使能/禁用;SCTLR)
  • cache的配置
  • 监控系统性能
  • 配置中断控制器(优先级、分组、使能/禁用,VBAR:设置中断向量表基地址)
  • 访问寄存器(mrc读、mcr写)
  • 获取或结束中断(IAR、EOIR)

(2)读写访问指令

MRC: 将 CP15 协处理器中的寄存器 数据读到 ARM 寄存器中;
MCR: 将 ARM 寄存器的数据写入到 CP15 协处理器寄存器中。

以MRC指令
为例,从CP15的某个寄存器读取数据到ARM寄存器的指令格式为:
MRC{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>
cond:指令执行的条件码,就是之前我们使用过的指令条件,eq,lt什么的。如果忽略的话就表示无条件执行;
p15:表示要读取的是CP15当中的某个寄存器;
opc1:协处理器要执行的操作码1,其实就是一个数,要做什么将来查表;
Rt:ARM 目标寄存器,读出来的数据放到哪个ARM寄存器里。 CP15
CRn:CP15 协处理器的目标寄存器,就是你要读取CP15的哪个寄存器(C0~C15);
CRm:协处理器中附加的目标寄存器或者源操作数寄存器,如果不需要附加信息就将CRm 设置 为 C0,否则结果不可预测。
opc2:可选的协处理器特定操作码2,使用时查表。
MCR与MRC类似的,只是Rt的作用变成了要写入CP15某个寄存器的值。

mrc p15 0, r0, c0, c0, 0

简单总结一下,通过 MIDR 寄存器可以获取到处理器内核信息;通过 SCTLR 寄存器可以使能或禁止 MMU、I/D Cache 等;通过 VBAR 寄存器可以设置中断向量偏移;通过CBAR 寄存器可以获取 GIC 基地址。

二、外部中断

1.设置GIC

(1)读取SCTLR,将V位置0(软件可以通过 VBAR来重新映射这个基地址)I位置1(I cache使能)

(2)通过GIC查询当前中断ID;先获取GIC基地址(CBAR);对其进行偏移(IAR),获得中断ID,然后进入中断处理函数

2.中断服务函数

(1)中断初始化;重新定位异常向量表的位置到0x87800000;并且调用GIC_Init函数

(2)对于GPIO1->ICR2(触发方式)、GPIO1->IMR(该中断使能)在中断源初始化进行配置

(3)中断服务函数

注意:先要在相对应的中断源的初始化函数里面注册在中断向量数组中;

例子:注册完中断之后,中断发生就会调用中断服务函数

内敛函数:INLINE,定义被放在头文件中

三、OCP原则(开闭原则)

对代码扩展是开放的,对代码的修改是关闭的。

四、启动代码

复制代码
.global _start
//异常向量表
//位于内存起始位置,当特定异常发生时,处理器会自动跳转到对应的地址上
_start:
    ldr pc, = _reset_handler
    ldr pc, = _undefined_handler
    ldr pc, = _svc_handler
    ldr pc, = _prefetch_handler
    ldr pc, = _data_abort_handler
    ldr pc, = _not_user_handler
    ldr pc, = _irq_handler
    ldr pc, = _fiq_handler
//异常处理程序 大多是简单的无限循环
_undefined_handler:
    ldr pc, = _undefined_handler
_svc_handler:
    ldr pc, = _svc_handler
_prefetch_handler:
    ldr pc, = _prefetch_handler
_data_abort_handler:
    ldr pc, = _data_abort_handler
_not_user_handler:
    ldr pc, = _not_user_handler
_fiq_handler:
    ldr pc, = _fiq_handler
    //IRQ中断处理程序
 _irq_handler:
    subs lr,lr,#4           //调整返回地址
    stmfd sp!,{r0-r12,lr}   //保存寄存器
    //读取中断控制器状态
    mrc p15,4,r1,c15,c0,0   //获取外设基地址
    add r1,r1,#0x2000       //偏移到中断控制器
    ldr r0,[r1,#0x0C]       //读取中断状态

    stmfd sp!,{r0,r1}       //保存临时寄存器
    //切换到系统模式处理中断
    cps #0x1F               //切换到系统模式
    stmfd sp!,{lr}          //保存系统模式LR
    bl system_interrupt_handler //调用C中断处理函数
    ldmfd sp!,{lr}          //恢复LR模式
    cps #0x12               //切换回IRQ模式
    //清除中断
    ldmfd sp!,{r0,r1}       //恢复临时寄存器
    str r0,[r1,#0x10]       //写EQIR寄存器

    ldmfd sp!,{r0-r12,pc}^  //恢复寄存器并返回

    //启动代码执行程序
_reset_handler:
    cpsid i                 //禁用中断
    //配置CP15系统控制寄存器
    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    //写回控制寄存器

    // 设置IRQ模式栈指针
    cps #0x12               // 切换到IRQ模式
    ldr sp, =0x82000000     // 设置IRQ栈

    cps #0x1F               // 切换到系统模式
    ldr sp, =0x84000000     // 设置系统栈
    cpsie i                 // 启用中断

    bl _init_bss            // 初始化BSS段
    b main                  // 跳转到主程序
    b finished              // 永远不会执行(冗余)
//初始化BSS段(清零)
_init_bss:
    ldr r0,= __bss_start    // BSS段起始地址
    ldr r1,= __bss_end      // BSS段结束地址
loop:
    mov r2,#0               // 清零值
    str r2,[r0]             // 存储0到当前地址
    add r0,r0,#4            // 移动到下一个字
    cmp r0,r1               // 比较是否到达结束
    blt  loop               // 如果未结束则循环
    bx lr


finished:
    b finished    
相关推荐
西岸行者7 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意7 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码7 天前
嵌入式学习路线
学习
毛小茛7 天前
计算机系统概论——校验码
学习
babe小鑫7 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms7 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下7 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。7 天前
2026.2.25监控学习
学习
im_AMBER7 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J7 天前
从“Hello World“ 开始 C++
c语言·c++·学习