九.GPIO中断试验1——中断原理

九.GPIO中断试验1——中断原理
教程I.MX6U的中断系统讲解是从STM32引⼊的,这就对我这种没接触过STM32的⼩⽩不太友好!并且中断可以说是到⽬前为⽌最最重要的知识点。还好,STM32只是⼤致过了⼏个知识点
STM32的中断系统回顾
参考教程给出的STM32的中断系统,主要有下⾯⼏个知识点
爪形手1. 中断向量表
2. 向量中断控制器NVIC
3. 中断使能
4. 中断服务函数
我们⼀个个来看看!
中断向量表
中断向量表说⽩了也是⼀个表,表⾥放的是中断向量,中断服务程序的⼊⼝地址或者存放中断程序服务的⾸地址称为中断向量,所以说中断向量表也就是⼀系列中断服务程序⼊⼝地址组成的表。这些中断服务程序或函数在中断向量表⾥的位置是由半导体⼚商定好的,当某个中断倍触发后会⾃动跳转到向量表中对应中断服务对应的⼊⼝地址。中断向量表在整个程序的最前⾯。
1 __Vectors  DCD __initial_sp ; Top of Stack
2            DCD Reset_Handler ; Reset Handler
3            DCD NMI_Handler ; NMI Handler
4            DCD HardFault_Handler ; Hard Fault Handler
5            DCD MemManage_Handler ; MPU Fault Handler
6            DCD BusFault_Handler ; Bus Fault Handler
7            DCD UsageFault_Handler ; Usage Fault Handler
上⾯的这⼀段代码就节选⾃STM32F103的中断向量表,中断向量表都是链接在代码的最前⾯,⽐如ARM的处理器是从0x00000000开始执⾏代码,那么这个向量表就是从
0x00000000开始存放的,代码第⼀⾏的__initial_sp就是第⼀条中断向量,存放的是栈盯指针,下⾯依次是复位等函数的⼊⼝地址,⼀直到最后这个向量表就完成了。
中断向量偏移
在第⼀个通过汇编点亮LED等试验中,我们是把程序链接到0x87800000,那么就是从这个地址开始执⾏指令的,那么不就出错了么?Cortex-M架构引⼊了⼀个新的概念:中断向量偏移,通过这个偏移就可以把向量表存储到任何地址。中断向量偏移要在函数SystemInit中完成,通过向SCB_VTOR寄存器写⼊新的中断向量表的⾸地址就⾏了。
长安街 车祸内嵌向量中断控制器
内嵌向量中断控制器NVIC(Nested Vectored Interrupt Controller)是对STM32进⾏中断管理的机构,这个控制器负责协调各个中断的管理。具体原理这⾥不深究了,但是NVIC是针对Cortex-M内核的,我们这次使⽤的Cortex-A内核的管理机构不叫NVIC,⽽是GIC(General Interrupt Controller)。具体作⽤后⾯会降到。
中断使能
要想要使⽤某个外设引发的中断,必须要使能这个外设的中断,这个没什么可讲的,直接设置相关寄
存器就可以了。
中断服务函数
我们使⽤中断主要⽬的就是使⽤中断服务函数,当中断发⽣后当前程序被挂起,中断服务函数被调⽤,中断服务函数执⾏完毕后回到原流程。
上⾯就是STM32整个中断系统的⼤致说明,Cortex-M内核的中断处理过程和Cortex-A内核的处理流程⼤同⼩异,我们看看Cortex-A的中断处理是什么样的。
Cortex-A7中断系统简介
中断向量表
和STM32⼀样,Cortex-A也是有个中断向量表的,这个表也是在代码的最前⾯,Cortex-A7内核⾥只有8个异常中断,这8个异常中断等中断模式如下表(ARM Cortex-A(armV7)编程⼿册V4.0,第11.1.1章节)
Normal Vector offset High vector address Non-secure Secure Hypervisor Monitor
印务局
0x00xFFFF0000Not used Reset Reset Not used
0x40xFFFF0004Undefined instruction Undefined instruction Undefined instruction from Hyp mode Not used
0x80xFFFF0008Supervisor Call Supervisior Call Secure Monitor Call Secure Monitor Call
0xC0xFFFF000C Prefetch Abort Prefetch Abort Prefetch Abort from Hyp mode Prefetch Abort
0x100xFFFF0010Data Abort Data Abort Data Abort from Hyp mode Data Abort
0x140xFFFF0014Not used Not used Hyp mode entry Not used
0x180xFFFF0018IRQ Interrupt IRQ Interrupt IRQ Interrupt IRQ Interrupt
0x1C0xFFFF001C FIQ Interrupt FIQ Interrupt FIQ Interrupt FIQ Interrupt
我在编程⼿册上只到了上⾯这个中断模式的表,没有到对应的中断类型,教程上给出了相对应的中断类型,这⾥截个图
可以看出来,这个中断向量表只有8个中断,⽽且还有个未使⽤,也就是说Cortex-A7的要处理的中断要⽐Cortex-M少的多,明显不可能。区别就是,Cortex-M在中断向量表⾥列出了包括所有外设的所有
的中断向量,⽽对于Cortex-A7来说,把所有外部中断都给了IRQ中断。任何⼀个外部中断触发中断时都会触发IRQ中断,在IRQ中断服务中读取指定的寄存器来判定是什么中断,然后做出相应的中断处理。
异常中断介绍
从上⾯那个表可以看出来Cortex-A7⼀共⽤了7种异常中断,下⾯简单介绍⼀下这7种中断的作⽤
Reset,复位中断,CPU复位后就会进⼊复位中断,在中断服务函数中我们需要做⼀些初始化服务,例如初始化SP指针,DDR等⼯作。
Undef未定义指令中断,当指令不能识别的时候就会产⽣此中断。
SWI软中断,由软件指令引发的中断,Linux系统调⽤会⽤SWI指令引发中断。
PrefetchAbort指令预取中⽌中断,预取指令出错时会触发此中断。
Data Abort 数据访问中⽌中断,当访问数据出错时产⽣此中断。
IRQ外部中断,芯⽚内部的外设中断都会引发此中断。
FIQ快速中断,需要快速处理中断时可以使⽤此中断。
代码构成
由于整个代码⽐较长,我们把将其按照这⼏块分割下。下⾯就是中断向量表的创建
.global _start
_start:
@添加中断向量表
ldr pc,=Reset_Handler        //复位中断函数
ldr pc,=Undefined_Handler    //未定义指令中断函数
ldr pc,=SVC_Handler          //SVC
ldr pc,=PreAbort_Handler    //预取终⽌
ldr pc,=DataAbort_Handler    //数据终⽌
ldr pc,=NotUsed_Handler      //未使⽤
ldr pc,=IRQ_Handler          //IRQ中断
ldr pc,=FIQ_Handler          //FIQ中断
/*复位中断函数 */
Reset_Handler:
ldr r0,=Reset_Handler
bx r0
/*未定义指令中断服务函数 */
Undefined_Handler:
ldr r0,=Undefined_Handler
bx r0
/
*未定义指令中断服务函数 */
Undefined_Handler:
ldr r0,=Undefined_Handler
bx r0
/*SVC中断服务函数 */
SVC_Handler:
ldr r0,=SVC_Handler
bx r0
/*预取终值中断服务函数 */
PreAbort_Handler:
ldr r0,=PreAbort_Handler
bx r0
/*数据终值中断服务函数 */
DataAbort_Handler:
ldr r0,=DataAbort_Handler
bx r0
/*未使⽤中断服务函数 */
NotUsed_Handler:
ldr r0,=NotUsed_Handler
bx r0
/*IRQ中断服务函数 */
IRQ_Handler:
ldr r0,=IRQ_Handler
bx r0
/*FIQ中断服务函数 */
FIQ_Handler:
ldr r0,=FIQ_Handler
bx r0
可以看出来,中断向量表是从_start开始的,也就是程序的⼊⼝,向量表后⾯紧跟着各个中断服务函数。当CPU响应中断,就会从向量表⾥根据定义的函数执⾏相应的服务。上⾯的每个服务⾥都是没有写具体的程序,只是做了个⼀个死循环。
GIC控制器
前⾯已经提到过,STM32是通过NVIC来控制中断的,⽽I.MX6U是通过⼀个叫做GIC的中断控制器来控制中断的。有个⼿册是专门介绍这个控制器的(ARM Generic Interrupt Controller V2.0)。
GIC是ARM公司给Cortex-A/R内核提供的⼀个中断控制器,有4个版本(V1~V4)。各个版本⽀持的Soc如下表
卷取机
I.MXU6属于CortexA7架构,使⽤的是V2版,最多⽀持8个核。当GIC接受到外部中断后就会报给ARM内核,如下图所⽰
但是从上图中的CPU interface到Processor之间,内核只提供了4个通道⽤来提供信号通讯:VFIQ,VIRQ,FIQ和IRQ
上⾯图下标也说明了:VFIQ和VIRQ就是虚拟的FIQ和IRQ。所以我们现在只⽤处理FIQ和IRQ了。在这个教程⾥,我们只使⽤IRQ,相当于GIC最后只通过IRQ上报了中断的信息。那么,GIC是如何完成这个⼯作的呢?下⾯的图就是GICV2的逻辑⽰意图
上图中,左侧部分就是中断源,中间部分相当于GIC,右边的就是GIC向内核发送的中断信息。可以从上图⾥发现,GIC将外部中断分了3类:
SPI(Shared Peripheral Interrupt)共享中断(⼀定要注意这个SPI不是那个总线的SPI),就是所有Core都共享的中断,这个是最常见的,⽐如按键、串⼝等外部的中断都属于SPI中断,所有的Core都可以处理该中断。
PPI(Private Peripheral Interrupt)私有中断,我们上⾯说过,V2版本的GIC最多⽀持8个核,那么每个核都会有⼀些⾃⼰独有的中断,这些中断需要指定的核去处理。
SGI(Software-generated Interrupt)软件中断,有软件触发的中断,通过向寄存器GICD_SGIR写⼊数据来触发,系统会使⽤SGI中断来完成多核之间的通讯。
中断ID
因为所有的外部中断源最终都是引发的IRQ,那么为了区分不同的中断源需要给他们分配⼀个唯⼀的ID,这些ID就是中断ID。每⼀个CPU⽀持1020个中断ID,编号为
ID0~ID1019.这1020个ID包含了SGI、PPI和SPI,他们是如下分配的:
ID0~ID15:共16个,分配给SGI
ID16~ID31:共16个分配给PPI
ID32~ID1019:988个分配给SPI
这些ID是半导体⼚商事先定义好的,I.MX6ULL(注意这⾥是6ULL)我们要去参考⼿册⾥查,可以发现6
ULL使⽤了128个中断ID,所以加上前⾯的32个ID,⼀共有160个中断ID。这128个中断ID对应的中断源在参考⼿册第三章可以查到
注意IRQ的起始值不是从0开始的,是从32开始的,前⾯32个是预留给PPI和SPI的。我们前⾯移植了恩智浦官⽅的SDK,⾥⾯提供了⼀个枚举类型IRQn_Type,⾥⾯就枚举了所有的中断。(6UL和6ULL的中断描述不太⼀样,教程⾥截取的表格是6ULL的)
GIC的逻辑分区
GIC架构按照功能逻辑分了两个逻辑块:Distributor分发器和 CPU Interface,两个逻辑块作⽤是这样的
Distributor
负责处理各个中断时间段分发问题,决定中断事件发送到那个CPU Interface,分发器收集所有的中断源,控制每个控制的优先级并将优先级⾼的事件发送给CPU接⼝。其主要⼯作如下:
1. 全局中断使能控制
2. 控制每个中断的使能
3. 设置每个中断的优先级
4. 设置每个中断等⽬标处理器列表
5. 设置每个外部中断等触发模式:电平触发或边沿触发
6. 设置每个中断属于组0还是组1
CPU Interface
CPU端⼝是和CPU核相连接,所以每个CPU核都有个与其对应的CPU Interface。它是分发器和CPU核之间的桥梁,主要⼯作如下:
1. 使能或关闭发送到CPU核对终端请求信号
楚科奇人
2. 应答中断
李汝军3. 通知中断处理完成
4. 设置优先级掩码,通过掩码来设置哪些中断不需要上报给CPU核处理
5. 定义抢占策略
6. 当多个中断请求时,选择优先级⾼度中断通知CPU核。
下⾯的表《Cortex-A7 Technical ReferenceManua.pdf》8.2.1GIC memory-map就是Distributor和CPU接⼝的偏移地址
CP15协处理器
这⼀⼩节讲的⽐较散,因为教程上是直接跳到这⾥就修改寄存器的值达到关闭I Cache、D Cache和MMU的⽬的。只有过程,原因不是很清楚。
我们在GIC⼿册⾥看⼀下相关寄存器的地址
注意表格抬头,是个偏移地址。这个偏移量基于GICD的偏移地址这个GICD就是上⾯那个GIC Memory Map的Distributor的地址上偏移来的(0x1000~0x1FFF),但是GICD的基地址也是个基于GIC基地址偏移量,要获取GIC的基地址,就要⽤到⼀个叫做CP15协处理器的东西。
CP15协处理器⽤于系统存储管理,但是在中断中也能⽤到,CP15⼀共只⽤16个32位的寄存器,需要通过下⾯两条指令完成操作
MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>
MRC{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>
cond:指令执⾏的条件码,如果忽略的话就表⽰⽆条件执⾏。
opc1:协处理器要执⾏的操作码。
Rt:ARM 源寄存器,要写⼊到 CP15 寄存器的数据就保存在此寄存器中。
CRn:CP15 协处理器的⽬标寄存器。
CRm:协处理器中附加的⽬标寄存器或者源操作数寄存器
CRm 设置为 C0,否则结果不可预测。
opc2:可选的协处理器特定操作码,当不需要的时候要设置为 0。
CP15协处理器的操作要在《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf》第B3.17章节⾥和《Cortex-A7 Technical ReferenceManua.pdf》第四章⾥讲的有。
CP15⾥的16个32位寄存器为c0~c15,中断要⽤到c0、c1、c12和c15这四个寄存器,其他的寄存器作⽤可以在上⾯两个⽂档中查询。并且这个CP15⾥的16个寄存器,在opc1和opc2不同的话代表操作的
寄存器是不同的,这个⽐较有意思。
从上⾯的c0的图表可以看出来,即便是指令前⾯的参数都⼀样,在opc2不同的时候代表的参数是不⼀样的。当opc1=0,CRm=c0,opc2=0时c0就是主ID寄存器MIDR,这个就是c0的基本作⽤,⽽c1的基本作⽤是SCTLR寄存器,⽤来完成控制作⽤,⽐如使能MMU、I Cache、
D Cache等。所有的c0~15⼀共16个寄存器的操作全在《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf》第B3.17⾥给了说明(Page1481:Table3-42给了详细的说明),在《Cortex-A7 Technical ReferenceManua.pdf》第4.2节也对每个寄存器的不同bit的作⽤进⾏过介绍,并且每个操作寄存器⽤到的指令都有说明。
c12寄存器我们需要定义为CBAR寄存器
这个VBAR通⽤名字就可以看出来是GIC的基地址,通过定义这个寄存器的数据就可以设置中断向量偏移。
c15的说明在《Cortex-A7 Technical ReferenceManua.pdf》⾥,我们需要它定义为CBAR寄存器,这个寄存器⾥保存了GIC的基地址。
到这⾥,就接到前⾯讲到内容了,如果我们需要获取当前中断ID,当前的中断ID是保存在 GICC_IAR
中,⽽GICC_IAR属于CPU端⼝寄存器,地址偏移量为0xC,那么我们就从c15按照CBAR模式读取到GIC底基地址加上偏移量就是对应的值,再获取bit[9:0]就是中断ID
MRC p15,4,r1,c15,c0,0      @获取GIC基地址,保存⾄r1
ADD r1,r1,#0x2000          @GIC基地址加上0x2000得到CPU接⼝寄存器起始地址
LDR r0,[r1,#0xC]            @读取端⼝起始地址+0xC地址,也就是GICC_IAR的值
所以也就是说,我们要操作中断,就要需要修改c0,c1,c12和c15的参数。
中断使能
中断使能包括两部分:IRQ或者FIQ总使能,还有针对ID0~ID1019这1020个中断源的使能。
总中断使能
想要使⽤中断,必须打开IRQ或FIQ中断使能这个是短短是通过当前程序状态寄存器CPSR控制的bit[7](IRQ)和bit[6](FIQ)控制的(0时为使能),汇编提供了更简单的指令来对其进⾏操作
指令功能
cpsid i禁⽌IRQ中断
cpsie i使能IRQ中断
cpsid f禁⽌FIQ中断
cpsid f使能FIQ中断
中断ID使能
GIC寄存器GICD_ISENABLERn和GCID_ICENABLERn来完成外部中断等使能和禁⽌。Cortex-A7内核中断ID只使⽤了512个,那么就需要512/32=16个寄存器来完成中断的使能,同样需要16个GCID_ICENABLERn来禁⽌中断。其中,R0的bit[15:0]对应ID15~0的SGI中断,剩下的R1~R15就控制了区域的SPI中断。
优先级设置
和STM32,Cortex-A7分可以配置的抢占优先级和⼦优先级,并且⽀持256个优先级,数字越⼩优先级越⾼。I.MX6U提供了32个优先级,在使⽤中断等时候需要对GICC_PMR寄存器进⾏初始化,来定义使⽤⼏级优先级。该寄存器只有低8位有效,最多可以设置256个优先级
GICC_PMR的地址也是基于CPU Interface的地址偏移⾥0x4的。
要注意的是,这256个优先级不是按照8位2进制的数据模式换算的。对不同bit置零,可以指定定使⽤了多少个优先级。针对I.MX6U来说,⽀持32个优先级,所以GICC_BPR设置为0b11111000。
抢占优先级和⼦优先级
抢占优先级⽐⼦优先级的优先权更⾼,这意味抢占优先级更⾼的中断会先执⾏,⽽不管⼦优先级的优先权,数值越低优先级越⾼。同理,如果抢占优先级相同,那么就会⽐较⼦优先级,⼦优先级更⾼的中断将会先被执⾏,数值越低优先级越⾼。当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当⼀个中断到来后,如果正在处理另⼀个中断,这个后到来的中断就要等到前⼀个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级⾼低来决定先处理哪⼀个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪⼀个。抢占优先级和⼦优先级各占多少为是由寄存器GICC_BPR决定的,GICC_BPR的低三位决定了抢占优先级和⼦优先级的位数。
为了简单化,我们⼀般把所有的中断优先级都配置为抢占优先级,⽐如I.MX6U的优先级位数为5位所以可以把Binary Point设置为2,表⽰5个优先级位全部为抢占优先级。(说实话这⾥暂时还不太清楚!)
优先级设置
前⾯已经定了I.MX6U有32个抢占优先级,数字越⼩优先级越⾼,具体使⽤某个中断就可以将其优先级设置为0~31,某个中断的优先级配有⼀个优先级寄存器DIPRIORITYR来完成,所以512个中断就对应有512个D_IPRIORITRY寄存器,如果优先级个数为32的话,使⽤D_IPRIORITRY的bit[7:4]来设置优先级,也就是说实际的优先级要左移3位,⽐如要设置ID40的优先级为5,代码要这么写
GICD_IPRIORITYR[40]=5<<3
中断优先级的设置还是有些不清楚,⼤概先讲这么多,后⾯结合代码来看应该能清除⼀些!

本文发布于:2024-09-21 00:40:34,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/397502.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:中断   服务   函数   寄存器   地址   中断向量   指令
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议