XuSenfeng

个人站

复读了,更新随缘,有的文件不全或者图片缺失具体看我的笔记库(https://github.com/XuSenfeng/note)


中断

目录

中断

stm32中断

ARM芯片从0x00000000开始运行, 最开始的时候设置有中断向量表, 用于主要用来记录中断对应的中断函数

代码最开始的地方存放堆栈栈顶指针, 之后存放各种中断函数

中断向量偏移

ARM从0x00000000开始的但是stm32是从0x80000000地址开始的, 如果代码一定要从别的位置开始, 就需要告诉内核, 设置中断向量偏移,

设置内核SCB的VTOR寄存器为新的中断向量表起始地址就可以了

NVIC终端管理

使能关闭终端, 设置优先级

中断服务函数的编写

Cortex-A7中断系统

中断向量表

只有八个, 位于地址的最开始部分

  • 复位: 上电以后第一个, 初始化工作
  • 未定义指令: 指令不能识别
  • 软中断: SWI指令中断, LInux使用进入内核, SVC特权模式
  • 指令预取中止中断: 预取指令出错
  • 数据访问中止中断: 数据访问出错
  • 未使用:
  • IRQ中断: 外部中断, 所有的外设中断
  • FIQ中断: 需要快速处理的终端

我们外面使用的是IRQ

没有已经写好的中断向量表, 用户自己定义

所有的裸机中断偏移都是在0x87800000开始的, 在main函数最开始的地址设置

GIC中断控制器

和NVIC一样, 用来控制中断, 有四个版本, V1已经被放弃了, V2是给ARMv7-A架构使用的, Cortex-A7, Cortex-A9等, V3,V4是给ARMv8-A/R使用的, 64位芯片

GIC-V2最多支持八个核

接收终端之后发送给内核, 但是内核只提供了四个信号,

  • VFIQ: 虚拟快速
  • VIRQ: 虚拟快速
  • FIQ: 快速中断
  • IRQ: 外部快速

cpsid i: 禁止IRQ

cpsie i: 使能IRQ

cpsid f: 禁止FIQ

cpsie f: 使能FIQ

或者使用寄存器GICD_ISENABLERnGICD_ ICENABLERn, 一个bit控制一个中断ID的使能, 16个GICD_ISENABLER寄存器来完成中断的使能。同理,也需要16个GICD_ICENABLER寄存器来完成中断的禁止。

GIC把所有的中断源分为了三种

  • SPI: 共享中断, 所有的核共享的中断
  • PPI: 私有中断, 独有的中断
  • SGI: 软件终端

中断ID

为了区分不同的中断, 最多支持1020个, 号码为0-1019, 包含了以上的三大类, 0-15给SGI, 16-31给PPI, 剩下的SPI

6ULL支持128个中断ID, ,加上前面属于PPI和SGI的32个ID,I.MX6U的中断源共有160个中断

前32个作为CPU的中断,

中断函数编写

IRQ中断服务函数的编写, 另一个是在IRQ中断服务函数中查找运行的具体函数

编写按键中断例程

中断向量表

start.s文件中编写中断向量表, 复位中断函数, IRQ中断服务函数

编写复位中断函数

关闭I.D Cache和MMU

CP15协处理器: CP15寄存器一般用于存储系统管理, 在中断中也会用, 有16个32位寄存器

c0-c15有特定的指令访问

  • MCR: 读取, 将CP15协处理器中的寄存器数据读到ARM寄存器中
  • MRC: 写入, 将ARM寄存器的数据写入到CP15协处理器寄存器中
MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>

cond:指令执行的条件码,如果忽略的话就表示无条件执行

opc1:协处理器要执行的操作码

Rt:ARM源寄存器,要写入到CP15寄存器的数据就保存在此寄存器中

CRn:CP15协处理器的目标寄存器。

CRm:协处理器中附加的目标寄存器或者源操作数寄存器,如果不需要附加信息就将CRm设置为C0,否则结果不可预测。

opc2:可选的协处理器特定操作码,当不需要的时候要设置为0

QQ图片20220815234911

在Cortex-A7 Technical… 手册中有每一个寄存器操作的指令, CRn=c1,opc1=0,CRm=c0,opc2=0的时候就表示此时的c1就是SCTLR寄存器,也就是系统控制寄存器

读取SCTLR寄存器, 采用读改写, 系统控制寄存器, bit0打开关闭MMU, bit1对齐控制位, bit2D Cache打开关闭, bit 11分支预测, bit12 I Cache

中断向量偏移

把新的中断向量表首地址写入CP15协处理器的VBAR寄存器

QQ图片20220815234553

从图中可以知道, CRn=c12,opc1=0,CRm=c0,opc2=0的时候就表示此时c12为VBAR寄存器

ldr r0, =0X87800000;r0=0X87800000
MCR p15, 0, r0, c12, c0, 0;将r0里面的数据写入到c12中

中断服务函数

保存寄存器

QQ图片20220815235124

mrc p15, 4, r1, c15, c0, 0读取CP15的寄存器CBAR, CBAR保存了GIC的控制寄存器首地址, GIC首地址偏移0x1000-0x1fff为GIC分发器, 0x2000-0x3fff为CPU接口端, 这意味着我们可以访问GIC控制器

分发器端口:处理各个中断事件的分发问题,也就是中断事件应该发送到哪个CPU Interface上去。分发器收集所有的中断源,可以控制每个中断的优先级,它总是将优先级最高的中断事件发送到CPU接口端

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

CPU Interface(CPU接口端):每个CPU Core都可以在GIC中找到一个与之对应的CPU Interface

从CPU相关的寄存器可以获得中断号, GICC_IAR的bit0-9保存中断ID, 可以使用这些ID得到对应的中断处理函数

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

得到之后先进入SVC模式, 允许其他的中断,保存lr(上一级返回地址)寄存器, 然后跳转到处理的函数, 返回以后出栈这一个, 因为不同模式栈不同, 然后返回到IRQ模式, 恢复现场

在处理完函数之后, 需要把GICC_IAR写入GICC_EOIR寄存器里面

最后在返回的时候

subs pc, lr, #4				/* 将lr-4赋给pc */

在ARM的指令是三级流水线:取指、译指、执行,pc指向的是正在取值的地址,这就是很多书上说的pc=当前执行指令地址+8, 就需要将lr-4赋值给pc,也就是pc=0X2004,从指令“MOV R2,R3”开始执行

GPIO中断设置

GPIO的设置

GPIOx_ICR1, GPIOx_ICR2每一个寄存器使用两个位来控制中断, 分别控制低16高16位, 设置触发方式

  • 00LOW_LEVEL — Interrupt n is low-level sensitive.低电平
  • 01HIGH_LEVEL — Interrupt n is high-level sensitive.高电平
  • 10RISING_EDGE — Interrupt n is rising-edge sensitive.上升沿
  • 11FALLING_EDGE — Interrupt n is falling-edge sensitive下降沿

这里的按键设置为下降沿,

GPIOx_IMR:掩码寄存器, 控制是不是屏蔽某一位的中断

  • 0MASKED — Interrupt n is disabled.
  • 1UNMASKED — Interrupt n is enabled

GPIOx_EDGE_SEL:用来控制上升沿以及下降沿都会触发中断, 设置的时候ICR寄存器不会发挥作用

GPIOx_ISR: 中断标志位, 处理完以后需要清除这个寄存器, 写1进行清除

GIC的设置

在手册的第三章查找对应的中断, 然后进行中断号使能

  • 66 gpio1-Combined interrupt indication for GPIO1 signal 0 throughout15

  • 67 gpio1-Combined interrupt indication for GPIO1 signal 16 throughout31

  • 这里的数字应该加上32, 这里中断号为99

  • 设置中断的优先级

  • 添加中断的优先级

实现

完成GPIO中断初始化

使用寄存器号使能GICD_ISENABLER寄存器

关闭掩码

总结

中断向量表必须在程序开始的位置

然后设置向量表的偏移, 设置中断时候的栈

打开中断GIC的相关的中断, 设置中断产生的方法

在中断开始之后, 首先保护现场, , 获取中断的编号, 然后进入判断的函数, 根据得到的中断数进行中断函数调用

返回的时候清除中断的标志位

返回恢复现场