【Linux 裸机篇(七)】I.MX6U 中断系统

2023-05-16

目录

  • 一、中断向量表
    • 1. 中断向量偏移
  • 二、中断系统简介
    • 1. 创建中断向量表
  • 三、GIC 控制器简介
    • 1. 中断 ID
  • 四、GIC 逻辑分块
    • 1. Distributor(分发器端)
    • 2. CPU Interface(CPU 接口端)
  • 五、CP15 协处理器
  • 六、中断使能
    • 1. IRQ 和 FIQ 总中断使能
    • 2. ID0~ID1019 中断使能和禁止
  • 七、中断优先级设置
    • 1. 优先级数配置
    • 2. 抢占优先级和子优先级位数设置
    • 3. 优先级设置
  • 八、中断程序注册

一、中断向量表

中断向量表是一个表,这个表里面存放的是中断向量。中断服务程序的入口地址或存放中断服务程序的首地址成为中断向量,因此中断向量表是一系列中断服务程序入口地址组成的表。这些中断服务程序(函数)在中断向量表中的位置是由半导体厂商定好的,当某个中断被触发以后就会自动跳转到中断向量表中对应的中断服务程序(函数)入口地址处。中断向量表在整个程序的最前面。

中断向量表都是链接到代码的最前面,比如一般 ARM 处理器都是从地址 0X00000000 开始执行指令的,那么中断向量表就是从 0X00000000 开始存放的。


1. 中断向量偏移

ARM 处理器都是从地址 0X00000000 开始运行的,但是代码是下载到 0X8000000 开始的存储区域中。因此中断向量表是存放到 0X8000000 地址处的,而不是 0X00000000,这样不是就出错了吗? ——— 中断向量表偏移,通过中断向量表偏移就可以将中断向量表存放到任意地址处。


二、中断系统简介

CortexA7 内核有 8 个异常中断,这 8 个异常中断的中断向量表如表所示:

在这里插入图片描述
  中断向量表里面都是中断服务函数的入口地址,因此一款芯片有什么中断都是可以从中断向量表看出来的。Cortex-A7 一共有 8 个中断,而且还有一个中断向量未使用,实际只有 7 个中断,其中Cortex-A 内核 CPU 的所有外部中断都放置在 IRQ 中断里,当任意一个外部中断发生的时候都会触发 IRQ 中断。在 IRQ 中断服务函数里面就可以读取指定的寄存器来判断发生的具体是什么中断,进而根据具体的中断做出相应的处理。这些外部中断和 IRQ 中断的关系如图所示:

在这里插入图片描述
  左侧的 Software0_IRQn~PMU_IRQ2_IRQ 这些都是 I.MX6U 的中断,他们都属于 IRQ 中断。左侧这些中断中任意一个发生的时候 IRQ 中断都会被触发,所以我们需要在 IRQ 中断服务函数中判断究竟是左侧的哪个中断发生了,然后再做出具体的处理。


图中一共有 7 个中断,简单介绍一下这 7 个中断:

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

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

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

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

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

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

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


1. 创建中断向量表

1 .global _start /* 全局标号 */
2 
3 _start:
4 ldr pc, =Reset_Handler /* 复位中断 */
5 ldr pc, =Undefined_Handler /* 未定义指令中断 */
6 ldr pc, =SVC_Handler /* SVC(Supervisor)中断 */
7 ldr pc, =PrefAbort_Handler /* 预取终止中断 */
8 ldr pc, =DataAbort_Handler /* 数据终止中断 */
9 ldr pc, =NotUsed_Handler /* 未使用中断 */
10 ldr pc, =IRQ_Handler /* IRQ 中断 */
11 ldr pc, =FIQ_Handler /* FIQ(快速中断)未定义中断 */
12
13 /* 复位中断 */
14 Reset_Handler:
15 /* 复位中断具体处理过程 */
16
17 /* 未定义中断 */
18 Undefined_Handler:
19 ldr r0, =Undefined_Handler
20 bx r0
21
22 /* SVC 中断 */
23 SVC_Handler:
24 ldr r0, =SVC_Handler
25 bx r0
26
27 /* 预取终止中断 */
28 PrefAbort_Handler:
29 ldr r0, =PrefAbort_Handler
30 bx r0
31
32 /* 数据终止中断 */
33 DataAbort_Handler:
34 ldr r0, =DataAbort_Handler
35 bx r0
36
37 /* 未使用的中断 */
38 NotUsed_Handler:
39
40 ldr r0, =NotUsed_Handler
41 bx r0
42
43 /* IRQ 中断!重点!!!!! */
44 IRQ_Handler:
45 /* 复位中断具体处理过程 */
46
47 /* FIQ 中断 */
48 FIQ_Handler:
49 ldr r0, =FIQ_Handler
50 bx r0

  第 4 到 11 行是中断向量表,当指定的中断发生以后就会调用对应的中断复位函数,比如复位中断发生以后就会执行第 4 行代码,也就是调用函数 Reset_Handler,函数 Reset_Handler就是复位中断的中断复位函数,其它的中断同理。

  第 14 到 50 行就是对应的中断服务函数,中断服务函数都是用汇编编写的,我们实际需要编写的只有复位中断服务函数 Reset_Handler 和 IRQ 中断服务函数 IRQ_Handler,其它的中断本教程没有用到,所以都是死循环。在编写复位中断复位函数和 IRQ 中断服务函数之前我们还需要了解一些其它的知识,否则的话就没法编写。


三、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 是针对虚拟化的,我们基本只使用 IRQ,所以相当于 GIC 最终向 ARM 内核就上报一个 IRQ信号。 GIC 总体框图如下:
在这里插入图片描述
左侧部分就是中断源,中间部分就是 GIC 控制器,最右侧就是中断控制器向处理器内核发送中断信息。我们重点要看的肯定是中间的 GIC 部分, GIC 将众多的中断源分为分为三类:

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

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

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


1. 中断 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 对应哪个中断那就由半导体厂商根据实际情况去定义了。比如 I.MX6U 的总共使用了 128 个中断 ID,加上前面属于 PPI 和 SGI 的 32 个 ID, I.MX6U 的中断源共有 128+32=160个,这 128 个中断 ID 对应的中断在《I.MX6ULL 参考手册》的“3.2 Cortex A7 interrupts”小节,中断源如表所示:
在这里插入图片描述

NXP 官方 SDK中的文件 MCIMX6Y2C.h,在此文件中定义了一个枚举类型 IRQn_Type,此枚举类型就枚举出了 I.MX6U 的所有中断,代码如下所示:

1 #define NUMBER_OF_INT_VECTORS 160 /* 中断源 160 个, SGI+PPI+SPI*/
2 
3 typedef enum IRQn {
4 /* Auxiliary constants */
5 NotAvail_IRQn = -128,
6 
7 /* Core interrupts */
8 Software0_IRQn = 0,
9 Software1_IRQn = 1,
10 Software2_IRQn = 2,
11 Software3_IRQn = 3,
12 Software4_IRQn = 4,
13 Software5_IRQn = 5,
14 Software6_IRQn = 6,
15 Software7_IRQn = 7,
16 Software8_IRQn = 8,
17 Software9_IRQn = 9,
18 Software10_IRQn = 10,
19 Software11_IRQn = 11,
20 Software12_IRQn = 12,
21 Software13_IRQn = 13,
22 Software14_IRQn = 14,
23 Software15_IRQn = 15,
24 VirtualMaintenance_IRQn = 25,
25 HypervisorTimer_IRQn = 26,
26 VirtualTimer_IRQn = 27,
27 LegacyFastInt_IRQn = 28,
28 SecurePhyTimer_IRQn = 29,
29 NonSecurePhyTimer_IRQn = 30,
30 LegacyIRQ_IRQn = 31,
31
32 /* Device specific interrupts */
33 IOMUXC_IRQn = 32,
34 DAP_IRQn = 33,
35 SDMA_IRQn = 34,
36 TSC_IRQn = 35,
37 SNVS_IRQn = 36,
…… ...... ......
151 ENET2_1588_IRQn = 153,
152 Reserved154_IRQn = 154,
153 Reserved155_IRQn = 155,
154 Reserved156_IRQn = 156,
155 Reserved157_IRQn = 157,
156 Reserved158_IRQn = 158,
157 PMU_IRQ2_IRQn = 159
158} IRQn_Type;

四、GIC 逻辑分块

GIC 架构分为了两个逻辑块: Distributor 和 CPU Interface,也就是分发器端和 CPU 接口端。这两个逻辑块的含义如下:

1. Distributor(分发器端)

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

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

2. CPU Interface(CPU 接口端)

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

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

文件 core_ca7.h 定义了 GIC 结构体,此结构体里面的寄存器分为了分发器端和 CPU 接口端,寄存器定义如下所示:

/*
* 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 接口段寄存器。


五、CP15 协处理器

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

在这里插入图片描述


六、中断使能

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


1. IRQ 和 FIQ 总中断使能

IRQ 和 FIQ 分别是外部中断和快速中断的总开关,就类似家里买的进户总电闸,然后ID0~ID1019 这 1020 个中断源就类似家里面的各个电器开关。要想开电视,那肯定要保证进户总电闸是打开的,因此要想使用 I.MX6U 上的外设中断就必须先打开 IRQ 中断,寄存器 CPSR 的 I=1 禁止 IRQ,当 I=0 使能 IRQ; F=1 禁止 FIQ, F=0 使能 FIQ。我们还有更简单的指令来完成 IRQ 或者 FIQ 的使能和禁止,图表所示:

在这里插入图片描述

2. ID0~ID1019 中断使能和禁止

GIC 寄存器 GICD_ISENABLERn 和 GICD_ ICENABLERn 用来完成外部中断的使能和禁止,对于 Cortex-A7 内核来说中断 ID 只使用了 512 个。一个 bit 控制一个中断 ID 的使能,那么就需要 512/32=16 个 GICD_ISENABLER 寄存器来完成中断的使能。同理,也需要 16 个GICD_ICENABLER 寄存器来完成中断的禁止。其中 GICD_ISENABLER0 的 bit[15:0]对应ID15~0 的 SGI 中断, GICD_ISENABLER0 的 bit[31:16]对应 ID31~16 的 PPI 中断。剩下的GICD_ISENABLER1~GICD_ISENABLER15 就是控制 SPI 中断的。


七、中断优先级设置

1. 优先级数配置

学过 STM32 都知道 Cortex-M 的中断优先级分为抢占优先级和子优先级,两者是可以配置
的。同样的 Cortex-A7 的中断优先级也可以分为抢占优先级和子优先级,两者同样是可以配置
的。 GIC 控制器最多可以支持 256 个优先级,数字越小,优先级越高! Cortex-A7 选择了 32 个
优先级。在使用中断的时候需要初始化 GICC_PMR 寄存器,此寄存器用来决定使用几级优先
级,寄存器结构如图所示:

在这里插入图片描述
GICC_PMR 寄存器只有低 8 位有效,这 8 位最多可以设置 256 个优先级,其他优先级数设置如表所示:
在这里插入图片描述
  I.MX6U 是 Cortex-A7内核,所以支持 32 个优先级, 因此 GICC_PMR 要设置为 0b11111000。


2. 抢占优先级和子优先级位数设置

抢占优先级和子优先级各占多少位是由寄存器 GICC_BPR 来决定的, GICC_BPR 寄存器结
构如图所示:

在这里插入图片描述
寄存器 GICC_BPR 只有低 3 位有效,其值不同,抢占优先级和子优先级占用的位数也不同,配置如表所示:
在这里插入图片描述
  为了简单起见,一般将所有的中断优先级位都配置为抢占优先级,比如 I.MX6U 的优先级位数为 5(32 个优先级),所以可以设置 Binary point 为 2,表示 5 个优先级位全部为抢占优先级。


3. 优先级设置

前面已经设置好了 I.MX6U 一共有 32 个抢占优先级,数字越小优先级越高。具体要使用某个中断的时候就可以设置其优先级为 0~31。某个中断 ID 的中断优先级设置由寄存器 D_IPRIORITYR 来完成,前面说了 Cortex-A7 使用了 512 个中断 ID,每个中断 ID 配有一个优先级寄存器,所以一共有 512 个 D_IPRIORITYR 寄存器。如果优先级个数为 32 的话,使用寄存器 D_IPRIORITYR 的 bit7:4 来设置优先级,也就是说实际的优先级要左移 3 位。比如要设置 ID40 中断的优先级为 5,示例代码如下:

GICD_IPRIORITYR[40] = 5 << 3;

优先级设置主要有三部分:

  ①:设置寄存器 GICC_PMR,配置优先级个数,比如 I.MX6U 支持 32 级优先级。

  ②:设置抢占优先级和子优先级位数,一般为了简单起见,会将所有的位数都设置为抢占优先级。

  ③:设置指定中断 ID 的优先级,也就是设置外设优先级。


八、中断程序注册

#ifndef _BSP_INT_H
#define _BSP_INT_H

/* 中断处理函数形式 */
typedef void (*system_irq_handler_t) (unsigned int giccIar, void *param);

/* 中断处理函数结构体*/
typedef struct _sys_irq_handle
{
	system_irq_handler_t irqHandler; 	/* 中断处理函数 */
	void *userParam; 					/* 中断处理函数参数 */
} sys_irq_handle_t;

void int_init(void);

void system_irqtable_init(void);

void system_register_irqhandler(IRQn_Type irq, system_irq_handler_t handler, void *userParam);

void system_irqhandler(unsigned int giccIar);

void default_irqhandler(unsigned int giccIar, void *userParam);

#endif
#include "bsp_int.h"

#define NUMBER_OF_INT_VECTORS 60

/* 中断嵌套计数器 */
static unsigned int irqNesting;

/* 中断服务函数表 */
static sys_irq_handle_t irqTable[NUMBER_OF_INT_VECTORS];

/*
 * @description : 中断初始化函数
 * @param : 无
 * @return : 无
 */
void int_init(void)
{
	GIC_Init(); /* 初始化 GIC */
	system_irqtable_init(); /* 初始化中断表 */
	__set_VBAR((uint32_t)0x87800000); /* 中断向量表偏移 */
}

/*
 * @description : 初始化中断服务函数表
 * @param : 无
 * @return : 无
 */
void system_irqtable_init(void)
{
	unsigned int i = 0;
	irqNesting = 0;
	
	/* 先将所有的中断服务函数设置为默认值 */
	for(i = 0; i < NUMBER_OF_INT_VECTORS; i++)
	{
		system_register_irqhandler( (IRQn_Type)i, default_irqhandler, NULL);
	}
}

/*
 * @description : 给指定的中断号注册中断服务函数
 * @param - irq : 要注册的中断号
 * @param - handler : 要注册的中断处理函数
 * @param - usrParam : 中断服务处理函数参数
 * @return : 无
 */
void system_register_irqhandler(IRQn_Type irq, system_irq_handler_t handler, void *userParam)
{
	irqTable[irq].irqHandler = handler;
	irqTable[irq].userParam = userParam;
}

/*
 * @description : C 语言中断服务函数, irq 汇编中断服务函数会
 *调用此函数,此函数通过在中断服务列表中查
 *找指定中断号所对应的中断处理函数并执行。
 * @param - giccIar : 中断号
 * @return : 无
 */
void system_irqhandler(unsigned int giccIar)
{
	uint32_t intNum = giccIar & 0x3FFUL;

	/* 检查中断号是否符合要求 */
	if ((intNum == 1020) || (intNum >= NUMBER_OF_INT_VECTORS))
	{
	return;
	}

	irqNesting++; /* 中断嵌套计数器加一 */

	/* 根据传递进来的中断号,在 irqTable 中调用确定的中断服务函数*/
	irqTable[intNum].irqHandler(intNum, irqTable[intNum].userParam);

	irqNesting--; /* 中断执行完成,中断嵌套寄存器减一 */
}

/*
* @description : 默认中断服务函数
* @param - giccIar : 中断号
* @param - usrParam : 中断服务处理函数参数
* @return : 无
*/
void default_irqhandler(unsigned int giccIar, void *userParam)
{
	while(1)
	{
	}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Linux 裸机篇(七)】I.MX6U 中断系统 的相关文章

  • 是否从页面缓存中的脏页面进行文件读取?

    当字节写入文件时 内核不会立即将这些字节写入磁盘 而是将这些字节存储在页缓存中的脏页中 回写缓存 问题是 如果在脏页刷新到磁盘之前发出文件读取 则将从缓存中的脏页提供字节 还是首先将脏页刷新到磁盘 然后进行磁盘读取以提供字节 将它们存储在进
  • CentOS目录结构是树形的吗?

    CentOS 上有相当于树的东西吗 如果你的 Centos 系统上没有安装 tree 无论如何我通常建议服务器设置使用最小安装磁盘 你应该在命令行中输入以下内容 yum install tree y 如果没有安装 那是因为您没有正确的存储库
  • 操作系统什么时候清除进程的内存

    进程在某些操作系统上成功或异常终止 操作系统何时决定擦除分配给该进程的内存 数据 代码等 在退出时或当它想为新进程分配内存时 这个清除内存分配过程在所有操作系统 winXP Win7 linux Mac 上都相同吗 据我了解 页表具有该进程
  • 如何用X11复制到剪贴板?

    使用 OS X 上的框架 我可以使用以下命令将 PNG 复制到粘贴板 在 C 中 显然我可以将 NSPasteboard 与 Cocoa 一起使用 include
  • 如何使用AWK脚本检查表的所有列数据类型? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 在这里 我正在检查表中第一列的数据类型 但我想知道AWK中表的所有列数据类型 我尝试过 但只能获得一列数据类型 例如 Column 1
  • 如何让 clangd 转向 c++20

    当没有其他信息时 如何让 clangd 回退到 c 20 例如 在第一次构建之前 cmake 可以生成一个 这是在带有最新 LLVM 的 Arch Linux 上 这是通过 Emacs LSP 运行的 但这应该没有什么区别 你可以加 Com
  • 构建 makefile 依赖/继承树

    如果我解释得不好或者问了一些明显的问题 我很抱歉 但我是 Linux 内核的新手 而且有点深入 我们有一个嵌入式 Linux 系统 它附带一个 文档非常糟糕的 SDK 其中包含数百个文件夹stuff 大多数文件夹包含rules make m
  • 伊迪德信息

    重新定义问题 有什么方法可以获取所连接显示器的序列号吗 我想收集显示器的Eid信息 当我使用 logverbose 选项运行 X 时 我可以从 xorg 0 log 文件中获取它 但问题是 如果我切换显示器 拔出当前显示器 然后插入另一个显
  • 在 LINUX 上使用 Python 连接到 OLAP 多维数据集

    我知道如何在 Windows 上使用 Python 连接到 MS OLAP 多维数据集 嗯 至少有一种方法 通常我使用 win32py 包并调用 COM 对象进行连接 import win32com client connection wi
  • 为什么在 Linux 上字符串文字的内存地址与其他字符串文字的内存地址如此不同?

    我注意到字符串文字在内存中的地址与其他常量和变量 Linux 操作系统 非常不同 它们有许多前导零 未打印 Example const char h Hi int i 1 printf p n void h printf p n void
  • 无法执行'x86_64-conda_cos6-linux-gnu-gcc':没有这样的文件或目录(pysam安装)

    我正在尝试安装 pysam 执行后 python path to pysam master setup py build 这个错误的产生是 unable to execute x86 64 conda cos6 linux gnu gcc
  • Inotify linux 监视子目录

    是否可以以这种模式监视目录 storage data usernames Download gt storage data Download 我需要监视每个用户的下载文件夹中是否进行了更改 也许我需要创建所有路径的列表 将其放入数组中 并在
  • 在Linux中断上下文中运行用户线程

    我正在编写一些定制的应用程序 并允许更改 Linux 内核中的中断处理程序代码 我有一个用户线程正在等待中断发生 如果发生中断 那么我要做的第一件事就是执行该用户线程 有什么办法让它发挥作用吗 Thanks 创建一个字符设备 这就是内核所做
  • C 程序从连接到系统的 USB 设备读取数据

    我正在尝试从连接到系统 USB 端口的 USB 设备 例如随身碟 获取数据 在这里 我可以打开设备文件并读取一些随机原始数据 但我想获取像 minicom teraterm 这样的数据 请让我知道我可以使用哪些方法和库来成功完成此操作以及如
  • Linux 桌面快捷方式和安装图标

    我需要添加什么到我的 spec文件来创建桌面快捷方式并在安装过程中为快捷方式分配一个图标 rpm 如果需要脚本 一个示例将非常有帮助 您在 Linux 下使用 desktop 文件作为图标 图标放置的位置取决于您使用的发行版和桌面环境 由于
  • 如何使用 sed 仅删除双空行?

    我找到了这个问题和答案 https stackoverflow com questions 4651591 howto use sed to remove only triple empty lines关于如何删除三重空行 但是 我只需要对
  • 在主目录中安装库

    在 Linux Ubuntu 中 我尝试运行一个工具 但它显示错误 库丢失 我无权在系统中安装任何内容 或者根本无法从我的用户帐户执行 sudo 是否可以在我的主目录 没有 sudo 中安装缺少的库 在我的例子中为 libstdc so 6
  • 信号处理程序有单独的堆栈吗?

    信号处理程序是否有单独的堆栈 就像每个线程都有单独的堆栈一样 这是在 Linux C 环境中 来自 Linux 手册页signal 7 http kernel org doc man pages online pages man7 sign
  • 如何更改 Ubuntu 14.04 上的 php-cli 版本?

    我是 Linux 新手 在篡改时破坏了一些 php 设置 如果我执行一个包含以下内容的 php 脚本 phpinfo 它显示 php 版本为 5 6 但通过命令行 如果我运行php v它返回 7 0 版本 我想让两个版本匹配 我怎样才能修复
  • 嵌入式Linux poll()不断返回

    我有一个特别的问题 当我知道没有什么可读时 民意调查不断返回 因此设置如下 我有 2 个文件描述符 它们构成fd设置民意调查监视 一种用于引脚从高到低的变化 GPIO 另一个用于代理输入 代理输入出现问题 处理的顺序是 启动main函数 然

随机推荐

  • Linux网络编程 5 - select模式的TCP服务器

    为了同时处理多个客户端的连接 xff0c 上一篇介绍了利用多线程的方法实现 xff0c 每个连接新建一个线程 xff0c 然后各自去处理 这是最简单也是最容易想到的方式 客户端的连接存在 xff0c 线程就存在 但是 xff0c 对于每一个
  • c标准库—参考手册

    1 lt errno h gt 2 lt float h gt 3 lt limits h gt 4 lt locale h gt 5 lt math h gt 6 lt signal h gt 7 lt setjmp h gt 8 lt
  • MPU6050

    1 个人总结 常用的MPU6050有八个针脚 xff0c VCC 跟GND 给模块供电 xff0c 模块通讯方式采用IIC通讯 xff0c SCL跟SDA为信号传递通道 xff0c XDA 跟 XCl是用来外接电磁传感器 xff0c 玩过M
  • 自学 Python 第一天

    总结 xff1a 感觉Python 前边学起来 跟c差不多 xff0c 之前学习过c语言 xff0c 但是并没有学这么细 xff0c 刚好学python xff0c 把当时忽略的知识点 重新减一下 打算花费两周学完Python为后续学习Op
  • 多功能悬赏任务平台app+小程序源码开源版搭建开发

    悬赏任务app源码 xff0c 从名字本身就可以理解这个PHP项目的流程 通过在线管理员工任务 即使它也可以在Intranet中工作 MySQL数据库是此源代码的最终部分 它易于实施和遵循 它是所有企业公司的主要项目应用程序 IT公司的任务
  • usart串口发送与接收问题

    项目场景 xff1a 串口通信可以说很常用的一种通信方式 xff0c 例如 蓝牙通信 openmv 与串口 通信 等等 问题描述 1 我们在进行数据传输过程中数据不时出现丢失的情况 xff0c 偶尔会丢失一部分数据 xff0c 导致数据部分
  • 基于51单片机光照强度检测系统

    介绍 本设计采用单片机作为数据处理与控制单元 xff0c 为了进行数据处理 xff0c 通过光敏电阻来感应光强弱变化 xff0c 经过ADC0804转换 xff0c 直接将数字信号送入到单片机中进行数据处理 单片机数据处理之后 xff0c
  • 基于51单片机控制的波形输出

    介绍 本模块采用PCF8591 xff0c 它是一款AD DA集成芯片 所以本节对iic通信协议不做过多的介绍 xff0c 重心放在iic的rtl建模 xff0c 本次通过iic控制PCF8591实现DAC输出功能 及输出波形 将数字量转为
  • 基于51单片机通过译码器控制系统

    用二极管 开关 译码器 单片机 数码管 xff08 点阵显示器 xff09 等器件设计仿真电路 xff0c 实现的功能 xff0c 用红黄绿二极管分别连接开关作为3 8译码器的输入 xff0c 译码器的输出连接到单片机的端口 xff0c 单
  • 基于51单片机串口通信的实验

    串口介绍 串口是一种应用十分广泛的通讯接口 xff0c 串口成本低 容易使用 通信线路简单 xff0c 可实现两个设备的互相通信 单片机的串口可以使单片机与单片机 单片机与电脑 单片机与各式各样的模块互相通信 xff0c 极大的扩展了单片机
  • 【QT】设置子窗口显示位置

    QT通过setGeoment来设置子窗口的位置 QWidget span class token operator span widget test span class token operator 61 span new span cl
  • 【散文诗】C语言的本质(基于ARM深入分析C程序)

    文章目录 1 ARM架构ARM通用寄存器及其别名基本汇编指令LDR xff1a STR xff1a ADD xff1a SUB xff1a BL xff1a PUSH xff1a POP xff1a MOV xff1a 2 局部变量的分配与
  • 【MQTT基础篇(三)】连接MQTT服务端

    文章目录 连接MQTT服务端1 CONNECT 连接服务端1 1 clientId 客户端ID1 2 cleanSession 清除会话1 3 keepAlive 心跳时间间隔 2 CONNACK 确认连接请求2 1 returnCode
  • 【MQTT基础篇(五)】发布、订阅和取消订阅

    文章目录 发布 订阅和取消订阅1 PUBLISH 发布消息1 1 topicName 主题名1 2 QoS 服务质量等级1 3 packetId 报文标识符1 4 retainFlag 保留标志1 5 Payload 有效载荷1 6 dup
  • 【FreeRTOS(六)】队列

    文章目录 队列创建队列 xQueueCreate发送消息至队列 xQueueSend接受队列消息 xQueueReceive获取队列信息数目 uxQueueMessagesWaiting代码示例创建队列集 xQueueCreateSet将队
  • 2021-09-29 使用安卓手机Camera和IMU信息运行ORBSLAM3

    目的 安卓手机具备camera imu gps等SLAM技术所需要的传感器 xff0c 且安卓手机很普及 如果能使用安卓设备作为ros的sensor xff0c 通过安卓设备节点传输到计算机 xff0c 进行实时定位与建图分析 xff0c
  • 【ESP32 WiFi篇(五)】ESP32 HTTP

    文章目录 1 HTTP概述1 1 超文本1 2 请求 响应1 3 TCP 2 HTTP请求和响应2 1 HTTP请求响应过程2 2 客户端请求消息2 2 1 请求行2 2 1 1 请求方法2 2 1 2 URL2 2 1 3 HTTP版本
  • 【Linux 裸机篇(五)】I.MX6ULL BSP工程管理下的 Makefile编写、链接脚本

    目录 一 BSP 工程二 Makefile三 链接脚本 Makefile 的静态模式和函数1 静态模式2 函数2 1 patsubst2 2 dir2 3 notdir2 4 foreach 一 BSP 工程 文件夹描述bsp存放驱动文件i
  • 【Linux 裸机篇(六)】I.MX6U 主频和时钟配置

    目录 一 时钟系统详解1 系统时钟来源2 7路 PLL 时钟源2 1 ARM PLL PLL1 2 2 528 PLL PLL2 2 3 USB1 PLL PLL3 2 4 USB2 PLL PLL7 2 5 ENET PLL PLL6 2
  • 【Linux 裸机篇(七)】I.MX6U 中断系统

    目录 一 中断向量表1 中断向量偏移 二 中断系统简介1 创建中断向量表 三 GIC 控制器简介1 中断 ID 四 GIC 逻辑分块1 Distributor 分发器端 2 CPU Interface CPU 接口端 五 CP15 协处理器