CH3-中断程序的编写
linux中断程序编写
中断的API函数
request_irq
1  | int request_irq(unsigned int irq,irq_handler_t handler,unsigned long flags,const char *name,void *dev)  | 
**irq:**要申请中断的中断号。
**handler:**中断处理函数,当中断发生以后就会执行此中断处理函数。
**flags:**中断标志,可以在文件include/linux/interrupt.h 里面查看所有的中断标志
| 标志 | 描述 | 
|---|---|
| IRQF_SHARED | 多个设备共享一个中断线,共享的所有中断都必须指定此标志。如果使用共享中断的话,request_irq 函数的dev参数就是唯一区分他们的标志。 | 
| IRQF_ONESHOT | 单次中断,中断执行一次就结束。 | 
| IRQF_TRIGGER_NONE | 无触发。 | 
| IRQF_TRIGGER_RISING | 上升沿触发。 | 
| IRQF_TRIGGER_FALLING | 下降沿触发。 | 
| IRQF_TRIGGER_HIGH | 高电平触发。 | 
| IRQF_TRIGGER_LOW | 低电平触发。 | 
**name:**中断名字,设置以后可以在/proc/interrupts 文件中看到对应的中断名字。
**dev:**如果将flags设置为IRQF_SHARED的话,dev用来区分不同的中断,一般情况下将dev设置为设备结构体,dev会传递给中断处理函数irq_handler_t的第二个参数。
**返回值:**0中断申请成功,其他负值中断申请失败,如果返回-EBUSY的话表示中断已经被申请了。
free_irq
1  | void free_irq(unsigned int irq,void *dev)  | 
**irq:**要释放的中断。
**dev:**如果中断设置为共享(IRQF_SHARED)的话,此参数用来区分具体的中断。共享中断只有在释放最后中断处理函数的时候才会被禁止掉
**返回值:**无。
中断服务函数
1  | irqreturn_t (*irq_handler_t) (int, void *)  | 
第一个参数:是要中断处理函数要相应的中断号。
**第二个参数:**是一个指向void 的指针,也就是个通用指针,需要与request_irq 函数的dev 参数保持一致。
**返回值:**中断处理函数的返回值为irqreturn_t 类型
1  | enum irqreturn {  | 
一般使用:return IRQ_RETVAL(IRQ_HANDLED)
中断使能函数
控制当前 CPU 的全局中断状态(修改 CPSR 寄存器):
函数 作用 关键实现 local_irq_enable()使能当前 CPU 的 IRQ 中断(全局) asm volatile("cpsie i" ::: "memory", "cc")local_irq_disable()禁止当前 CPU 的 IRQ 中断(全局) asm volatile("cpsid i" ::: "memory", "cc")local_fiq_enable()使能当前 CPU 的 FIQ 中断(全局) asm volatile("cpsie f" ::: "memory", "cc")local_fiq_disable()禁止当前 CPU 的 FIQ 中断(全局) ‘asm volatile asm volatile("cpsid f" ::: "memory", "cc")local_irq_save(flags)保存当前中断状态到 ,并 flags禁止IRQ 中断mrs %0, cpsr+cpsid i(保存 CPSR 后关中断)local_irq_restore(flags)恢复之前保存的中断状态(通过 ) flagsmsr cpsr_c, %0(恢复 CPSR 原始值)‘本地 local_save_flags(flags)仅保存当前中断状态到 (中断状态) flags不改变mrs %0, cpsr(仅读取 CPSR)特定 IRQ 线控制函数(中断控制器级)
函数 作用 关键实现 enable_irq(unsigned int irq)使能指定中断线(全局生效) 调用中断控制器驱动使能该 IRQ;若之前被 禁止,会重新触发挂起中断 disable_irqdisable_irq(unsigned int irq)禁止指定中断线(全局生效),并等待中断处理完成(同步) 禁止 IRQ + 等待处理完成(可能睡眠) synchronize_irq()disable_irq_nosync(unsigned int irq)禁止指定中断线(全局生效),不等待中断处理完成(异步) 仅禁止 IRQ,不等待处理完成(不可在中断上下文调用) 
设备树
Linux 内核通过读取设备树中的中断属性信息来配置中断,参考文档:Documentation/devicetree/bindings/arm/gic.txt imx6ull.dtsi文件找到
顶层中断控制器intc–gic
1  | intc: interrupt-controller@00a01000 {  | 
第三行:#interrupt-cells 和#address-cells、#size-cells 一样。,#interrupt-cells 描述了interrupts 属性的cells 大小,也就是一条信息有几个cells。每个cells都是32位整形值,对于ARM处理的==GIC 来说,一共有3 个cells==,这三个cells 的含义如下:
- 第一个cells:中断类型,0表示SPI中断,1表示PPI中断。
 - 第二个cells:中断号,对于SPI中断来说中断号的范围为0
987,对于PPI中断来说中断号的范围为 015。 - 第三个cells:标志,bit[3:0]表示中断触发类型,为1的时候表示上升沿触发,为2的时候表示下降沿触发,为4的时候表示高电平触发,为8的时候表示低电平触发。bit[15:8]为PPI 中断的CPU掩码。
 
第五行:表示当前节点是中断控制器。
一级子中断控制器
1  | gpc: gpc@020dc000 {  | 
次级中断控制器–gpio控制器
GPIO控制器作为次级中断控制器,其中断源是GPIO引脚。每个引脚的中断配置只需:
- 引脚号:标识具体的GPIO引脚(例如引脚9)。
 - 触发类型:定义中断的触发方式(如边沿、电平)。
 
因此,不需要像GIC(主中断控制器)那样需要3个单元格(类型、全局中断号、标志),而是简化为 2 个单元格。
1  | soc{  | 
第4 行,interrupts 描述中断源信息,对于gpio5 来说一共有两条信息,中断类型都是SPI,触发电平都是IRQ_TYPE_LEVEL_HIGH。GPIO5一共用了2个中断号,一个是74,一个是75。其中74 对应GPIO5_IO00~GPIO5_IO15 这低16个IO,75 对GPIO5_IO16~GPIOI5_IO31 这高16 位IO。
GPIO 引脚与 GIC 中断的映射:
- GPIO5 的引脚范围被分为两组:
- 引脚 0~15 映射到 GIC 的 SPI 中断 74。
 - 引脚 16~31 映射到 GIC 的 SPI 中断 75。
 
 - 当 GPIO5 的某个引脚触发中断时,硬件会将对应组的 GIC 中断(74 或 75)上报。
 

第8 行,interrupt-controller 表明了gpio5 节点也是个中断控制器,用于控制gpio5 所有IO的中断。
第9 行,将#interrupt-cells 修改为2。
子中断控制器
1  | gt9147:gt9147@14 {  | 
关键参数详解
interrupt-parent = <&gpio1>;
- 作用:指定该设备的中断信号连接到哪个中断控制器。
 - 参数:
gpio1表示此设备的中断信号由 GPIO 控制器 1 管理。 - 背景:在 SoC 中,GPIO 控制器通常也承担中断控制器的角色,负责将 GPIO 引脚的中断信号转发给主中断控制器(如 GIC)。
 
interrupts = <9 0>;
- 作用:定义中断信号的引脚和触发方式。
 - 参数:
- 第一个参数(9):在 
interrupt-parent指定的控制器(GPIO1)中,使用 第9号引脚 作为中断信号线。 - 第二个参数(0):中断触发类型,此处 
0表示 低电平触发(IRQ_TYPE_LEVEL_LOW)。
其他常见值:1:上升沿触发(IRQ_TYPE_EDGE_RISING)2:下降沿触发(IRQ_TYPE_EDGE_FALLING)3:双边沿触发(IRQ_TYPE_EDGE_BOTH)4:高电平触发(IRQ_TYPE_LEVEL_HIGH)
 
 - 第一个参数(9):在 
 
interrupt-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
- 作用:指定中断引脚的具体 GPIO 控制器、引脚号和电平极性。
 - 参数:
&gpio1:GPIO 控制器 1。9:GPIO1 的 9 号引脚。GPIO_ACTIVE_LOW:低电平有效(即引脚变为低电平时触发中断)
 
中断有关的设备树属性信息:
#interrupt-cells,指定中断源的信息cells 个数。
interrupt-controller,表示当前节点为中断控制器。
interrupts,指定中断号,触发方式等。
interrupt-parent,指定父中断,也就是中断控制器。
两者区别
| 特性 | GT9147(GPIO中断) | gpc(GIC中断) | 
|---|---|---|
| 中断控制器 | GPIO控制器(如 gpio1) | 
GIC(通用中断控制器) | 
| 中断号来源 | GPIO引脚号(如 9) | 
GIC分配的硬件中断号(如 42) | 
| 触发类型编码 | 数字编码(0表示上升沿) | 
直接使用宏(如 IRQ_TYPE_EDGE_RISING) | 
| 适用场景 | 外部设备通过GPIO引脚触发中断(如触摸屏) | 片内外设直接连接到GIC(如DMA、USB) | 
- GT9147节点:使用GPIO引脚作为中断源,硬件中断号是GPIO引脚号(需通过SoC文档或DTSI文件映射到GIC中断号)。
 - gpc节点:直接使用GIC分配的硬件中断号,无需二次映射。
 
实验一
本次实验我们只使用按键中断,并通过驱动注册一个按键的中断服务函数
设备树添加节点:
1  | key{  | 
驱动代码:
1  | 
  | 
实验二
共享中断实验,本次实验进一步来了解共享中断,我们首先来学习理论知识:
理论知识:
当在
request_irq函数中设置IRQF_SHARED标志时,表示我们希望共享中断线,即多个设备可以使用同一个硬件中断号。在这种情况下,dev参数变得至关重要。dev参数必须满足以下要求:- 必须是唯一的:每个共享同一中断线的设备必须提供不同的 
dev值 - 不能为 NULL:必须指向一个设备特定的数据结构
 - 用于标识中断源:当中断发生时,内核会使用这个值来确定是哪个设备触发了中断
 
- 必须是唯一的:每个共享同一中断线的设备必须提供不同的 
 
伪代码:
1  | 
  | 



