GPIO中断实验2

Cortex-A7中断系统

跟STM32一样,Cortex-A7也有中断向量表,中断向量表也是在代码的最前面。Cortex-A7内核有8个异常中断,这8个异常中断的中断向量表如表

向量地址中断地址中断模式
0X00复位中断(Rest)特权模式(SVC)
0X04未定义指令中断(Undefined Instruction)未定义指令中止模式(Undef)
0X08软中断(Software Interrupt,SWI)特权模式(SVC)
0X0C指令预取中止中断(Prefetch Abort)中止模式
0X10数据访问中止中断(Data Abort)中止模式
0X14未使用(Not Used)未使用
0X18IRQ中断(IRQ Interrupt)外界中断模式(IRQ)
0X1CFIQ中断(FiQ Interrupt)快速中断模式(FIQ)

中断向量表里面都是中断服务函数的入口地址,因此一款芯片有什么中断都是可以从中断向量表看出来的。从表中可以看出,Cortex-A7一共有8个中断,而且还有一个中断向量未使用,实际只有7个中断。和“STM32F103中断向量表比起来少了很多!难道一个能跑Linux的芯片只有这7个中断?明显不可能的!那类似STM32中的EXTI9_5_IRQHandler、TIM2_IRQHandler这样的中断向量在哪里?I2C、SPI、定时器等等的中断怎么处理呢?这个就是Cortex-A和Cotex-M在中断向量表这一块的区别,对于Cortex-M内核来说,中断向量表列举出了一款芯片所有的中断向量,包括芯片外设的所有中断。对于Cotex-A内核来说并没有这么做,在表中有个IRQ中断, Cortex-A内核CPU的所有外部中断都属于这个IRQ中断,当任意一个外部中断发生的时候都会触发IRQ中断。在IRQ中断服务函数里面就可以读取指定的寄存器来判断发生的具体是什么中断,进而根据具体的中断做出相应的处理。

在表中一共有7个中断:

①、复位中断(Rest),CPU复位以后就会进入复位中断,我们可以在复位中断服务函数里面做一些初始化工作,比如初始化SP指针、DDR等等。

②、未定义指令中断(Undefined Instruction),如果指令不能识别的话就会产生此中断。

③、软中断(Software Interrupt,SWI),由SWI指令引起的中断,Linux的系统调用会用SWI指令来引起软中断,通过软中断来陷入到内核空间。

④、指令预取中止中断(Prefetch Abort),预取指令的出错的时候会产生此中断。

⑤、数据访问中止中断(Data Abort),访问数据出错的时候会产生此中断。

⑥、IRQ中断(IRQ Interrupt),外部中断,前面已经说了,芯片内部的外设中断都会引起此中断的发生。

⑦、FIQ中断(FIQ Interrupt),快速中断,如果需要快速处理中断的话就可以使用此中断。

在上面的7个中断中,我们常用的就是复位中断和IRQ中断。

GIC控制器

STM32(Cortex-M)的中断控制器叫做NVIC,I.MX6U(Cortex-A)的中断控制器叫做GIC。

GIC是ARM公司给Cortex-A/R内核提供的一个中断控制器,类似Cortex-M内核中的NVIC。目前GIC有4个版本:V1~V4,V1是最老的版本,已经被废弃了。V2~V4目前正在大量的使用。GIC V2是给ARMv7-A架构使用的,比如Cortex-A7、Cortex-A9、Cortex-A15等,V3和V4是给ARMv8-A/R架构使用的,也就是64位芯片使用的。I.MX6U是Cortex-A内核的,因此我们主要讲解GIC V2。GIC V2最多支持8个核。ARM会根据GIC版本的不同研发出不同的IP核,那些半导体厂商直接购买对应的IP核即可,比如ARM针对GIC V2就开发出了GIC400这个中断控制器IP核。当GIC接收到外部中断信号以后就会报给ARM内核,但是ARM内核只提供了四个信号给GIC来汇报中断情况:VFIQ、VIRQ、FIQ和IRQ,他们之间的关系如图所示:

在图中,GIC接收众多的外部中断,然后对其进行处理,最终就只通过四个信号报给ARM内核,这四个信号的含义如下:

VFIQ:虚拟快速FIQ。

VIRQ:虚拟外部IRQ。

FIQ:快速中断IRQ。

IRQ:外部中断IRQ。

VFIQ和VIRQ是针对虚拟化的,不讨论虚拟化,剩下的就是FIQ和IRQ了,本教程我们只使用IRQ,所以相当于GIC最终向ARM内核就上报一个IRQ信号。那么GIC是如何完成这个工作的呢?GICV2的逻辑图如图所示:

图中左侧部分就是中断源,中间部分就是GIC控制器,最右侧就是中断控制器向处理器内核发送中断信息。我们重点要看的肯定是中间的GIC部分,GIC将众多的中断源分为分为三类:

①、SPI(Shared Peripheral Interrupt),共享中断,顾名思义,所有Core共享的中断,这个是最常见的,那些外部中断都属于SPI中断(注意!不是SPI总线那个中断) 。比如按键中断、串口中断等等,这些中断所有的Core都可以处理,不限定特定Core。

②、PPI(Private Peripheral Interrupt),私有中断,我们说了GIC是支持多核的,每个核肯定有自己独有的中断。这些独有的中断肯定是要指定的核心处理,因此这些中断就叫做私有中断。

③、SGI(Software-generated Interrupt),软件中断,由软件触发引起的中断,通过向寄存器GICD_SGIR写入数据来触发,系统会使用SGI中断来完成多核之间的通信。

中断ID

中断源有很多,为了区分这些不同的中断源肯定要给他们分配一个唯一ID,这些ID就是中断ID。每一个CPU最多支持1020个中断ID,中断ID号为ID0~ID1019。这1020个ID包含了PPI、SPI和SGI,那么这三类中断是如何分配这1020个中断ID的呢?这1020个ID分配如下:

ID0~ID15:这16个ID分配给SGI。

ID16~ID31:这16个ID分配给PPI。

ID32~ID1019:这988个ID分配给SPI,像GPIO中断、串口中断等这些外部中断,至于具体到某个ID对应哪个中断那就由半导体厂商根据实际情况去定义了。

GIC逻辑分块

 GIC架构分为了两个逻辑块:Distributor和CPU Interface。

Distributor(分发器端):此逻辑块负责处理各个中断事件的分发问题,也就是中断事件应该发送到哪个CPU Interface上去。分发器收集所有的中断源,可以控制每个中断的优先级,它总是将优先级最高的中断事件发送到CPU接口端。分发器端要做的主要工作如下:

①、全局中断使能控制。

②、控制每一个中断的使能或者关闭。

③、设置每个中断的优先级。

④、设置每个中断的目标处理器列表。

⑤、设置每个外部中断的触发模式:电平触发或边沿触发。

⑥、设置每个中断属于组0还是组1。

CPU Interface(CPU接口端):CPU接口端听名字就知道是和CPU Core相连接的,每个CPU Core都可以在GIC中找到一个与之对应的CPU Interface。CPU接口端就是分发器和CPU Core之间的桥梁,CPU接口端主要工作如下:

①、使能或者关闭发送到CPU Core的中断请求信号。

②、应答中断。

③、通知中断处理完成。

④、设置优先级掩码,通过掩码来设置哪些中断不需要上报给CPU Core。

⑤、定义抢占策略。

⑥、当多个中断到来的时候,选择优先级最高的中断通知给CPU Core。

寄存器定义代码:

示例代码17.1.3.2 GIC控制器结构体
/*
 * GIC寄存器描述结构体,
 * GIC分为分发器端和CPU接口端
 */
1 typedef struct
2 {
3 /* 分发器端寄存器 */
4 uint32_t RESERVED0[1024];
5 __IOM uint32_t D_CTLR;  /* Offset: 0x1000 (R/W) */
6 __IM uint32_t D_TYPER;  /* Offset: 0x1004 (R/ ) */
7 __IM uint32_t D_IIDR;  /* Offset: 0x1008 (R/ ) */
8 uint32_t RESERVED1[29];
9 __IOM uint32_t D_IGROUPR[16]; /* Offset: 0x1080 - 0x0BC (R/W) */
10 uint32_t RESERVED2[16];
11 __IOM uint32_t D_ISENABLER[16];/* Offset: 0x1100 - 0x13C (R/W) */
12 uint32_t RESERVED3[16];
13 __IOM uint32_t D_ICENABLER[16];/* Offset: 0x1180 - 0x1BC (R/W) */
14 uint32_t RESERVED4[16];
15 __IOM uint32_t D_ISPENDR[16]; /* Offset: 0x1200 - 0x23C (R/W) */
16 uint32_t RESERVED5[16];
17 __IOM uint32_t D_ICPENDR[16]; /* Offset: 0x1280 - 0x2BC (R/W) */
18 uint32_t RESERVED6[16];
19 __IOM uint32_t D_ISACTIVER[16];/* Offset: 0x1300 - 0x33C (R/W) */
20 uint32_t RESERVED7[16];
21 __IOM uint32_t D_ICACTIVER[16];/* Offset: 0x1380 - 0x3BC (R/W) */
22 uint32_t RESERVED8[16];
23 __IOM uint8_t D_IPRIORITYR[512];/* Offset: 0x1400 - 0x5FC (R/W) */
24 uint32_t RESERVED9[128];
25 __IOM uint8_t D_ITARGETSR[512];/* Offset: 0x1800 - 0x9FC (R/W) */
26 uint32_t RESERVED10[128];
27 __IOM uint32_t D_ICFGR[32]; /* Offset: 0x1C00 - 0xC7C (R/W) */
28 uint32_t RESERVED11[32];
29 __IM uint32_t D_PPISR;  /* Offset: 0x1D00 (R/ ) */
30 __IM uint32_t D_SPISR[15];  /* Offset: 0x1D04 - 0xD3C (R/ ) */
31 uint32_t RESERVED12[112];
32 __OM uint32_t D_SGIR;   /* Offset: 0x1F00 ( /W) */
33 uint32_t RESERVED13[3];
34 __IOM uint8_t D_CPENDSGIR[16];/* Offset: 0x1F10 - 0xF1C (R/W) */
35 __IOM uint8_t D_SPENDSGIR[16];/* Offset: 0x1F20 - 0xF2C (R/W) */
36 uint32_t RESERVED14[40];
37 __IM uint32_t D_PIDR4;  /* Offset: 0x1FD0 (R/ ) */
38 __IM uint32_t D_PIDR5;  /* Offset: 0x1FD4 (R/ ) */
39 __IM uint32_t D_PIDR6;  /* Offset: 0x1FD8 (R/ ) */
40 __IM uint32_t D_PIDR7;  /* Offset: 0x1FDC (R/ ) */
41 __IM uint32_t D_PIDR0;  /* Offset: 0x1FE0 (R/ ) */
42 __IM uint32_t D_PIDR1;  /* Offset: 0x1FE4 (R/ ) */
43 __IM uint32_t D_PIDR2;  /* Offset: 0x1FE8 (R/ ) */
44 __IM uint32_t D_PIDR3;  /* Offset: 0x1FEC (R/ ) */
45 __IM uint32_t D_CIDR0;  /* Offset: 0x1FF0 (R/ ) */
46 __IM uint32_t D_CIDR1;  /* Offset: 0x1FF4 (R/ ) */
47 __IM uint32_t D_CIDR2;  /* Offset: 0x1FF8 (R/ ) */
48 __IM uint32_t D_CIDR3;  /* Offset: 0x1FFC (R/ ) */
49 
50 /* CPU接口端寄存器 */
51 __IOM uint32_t C_CTLR;  /* Offset: 0x2000 (R/W) */
52 __IOM uint32_t C_PMR;  /* Offset: 0x2004 (R/W) */
53 __IOM uint32_t C_BPR;  /* Offset: 0x2008 (R/W) */
54 __IM uint32_t C_IAR;  /* Offset: 0x200C (R/ ) */
55 __OM uint32_t C_EOIR;  /* Offset: 0x2010 ( /W) */
56 __IM uint32_t C_RPR;  /* Offset: 0x2014 (R/ ) */
57 __IM uint32_t C_HPPIR;  /* Offset: 0x2018 (R/ ) */
58 __IOM uint32_t C_ABPR;  /* Offset: 0x201C (R/W) */
59 __IM uint32_t C_AIAR;  /* Offset: 0x2020 (R/ ) */
60 __OM uint32_t C_AEOIR;  /* Offset: 0x2024 ( /W) */
61 __IM uint32_t C_AHPPIR;  /* Offset: 0x2028 (R/ ) */
62 uint32_t RESERVED15[41];
63 __IOM uint32_t C_APR0;  /* Offset: 0x20D0 (R/W) */
64 uint32_t RESERVED16[3];
65 __IOM uint32_t C_NSAPR0;  /* Offset: 0x20E0 (R/W) */
66 uint32_t RESERVED17[6];
67 __IM uint32_t C_IIDR;  /* Offset: 0x20FC (R/ ) */
68 uint32_t RESERVED18[960];
69 __OM uint32_t C_DIR;  /* Offset: 0x3000 ( /W) */
70 } GIC_Type;

示例代码中的结构体GIC_Type就是GIC控制器,列举出了GIC控制器的所有寄存器,可以通过结构体GIC_Type来访问GIC的所有寄存器。

第5行是GIC的分发器端相关寄存器,其相对于GIC基地址偏移为0X1000,因此我们获取到GIC基地址以后只需要加上0X1000即可访问GIC分发器端寄存器。

第51行是GIC的CPU接口端相关寄存器,其相对于GIC基地址的偏移为0X2000,同样的,获取到GIC基地址以后只需要加上0X2000即可访问GIC的CPU接口段寄存器。

那么问题来了?GIC控制器的寄存器基地址在哪里呢?这个就需要用到Cortex-A的CP15协处理器了。

CP15协处理器

CP15协处理器一般用于存储系统管理,但是在中断中也会使用到,CP15协处理器一共有16个32位寄存器。CP15协处理器的访问通过如下另个指令完成:

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

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

MRC就是读CP15寄存器,MCR就是写CP15寄存器,MCR指令格式如下:

MCR{cond} p15, <opc1>, <Rt>, <CRn>, <CRm>, <opc2>

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

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

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

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

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

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

MRC的指令格式和MCR一样,只不过在MRC指令中Rt就是目标寄存器,也就是从CP15指定寄存器读出来的数据会保存在Rt中。而CRn就是源寄存器,也就是要读取的写处理器寄存器。

假如我们要将CP15中C0寄存器的值读取到R0寄存器中,那么就可以使用如下命令:

MRC p15, 0, r0, c0, c0, 0
CP15协处理器有16个32位寄存器,c0~c15,这里只看看一下c0、c1、c12和c15这四个寄存器,因为实验只用到这四个寄存器。

中断使能

中断使能包括两部分,一个是IRQ或者FIQ总中断使能,另一个就是ID0~ID1019这1020个中断源的使能。

1、IRQ和FIQ总中断使能

IRQ和FIQ分别是外部中断和快速中断的总开关,就类似家里买的进户总电闸,然后ID0~ID1019这1020个中断源就类似家里面的各个电器开关。要想开电视,那肯定要保证进户总电闸是打开的,因此要想使用I.MX6U上的外设中断就必须先打开IRQ中断(本教程不使用FIQ)。在“6.3.2 程序状态寄存器”小节已经讲过了,寄存器CPSR的 I=1禁止IRQ,当I=0使能IRQ;F=1禁止FIQ,F=0使能FIQ。我们还有更简单的指令来完成IRQ或者FIQ的使能和禁止:

cpsid i

禁止IRQ中断。

cpsie i

使能IRQ中断。

cpsid f

禁止FIQ中断。

cpsie f

使能FIQ中断。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值