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)
恢复之前保存的中断状态(通过 ) flags
msr cpsr_c, %0
(恢复 CPSR 原始值)‘本地 local_save_flags(flags)
仅保存当前中断状态到 (中断状态) flags
不改变mrs %0, cpsr
(仅读取 CPSR)特定 IRQ 线控制函数(中断控制器级)
函数 作用 关键实现 enable_irq(unsigned int irq)
使能指定中断线(全局生效) 调用中断控制器驱动使能该 IRQ;若之前被 禁止,会重新触发挂起中断 disable_irq
disable_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 |
|