linux驱动学习--中断
linux驱动学习--中断
子协linux驱动–基于platform和设备树下的按键中断
一、linux中断管理
首先先看一下几个概念
- 中断号:每个中断都有一个中断号,中断控制器根据中断号区分不同的中断(中断号由MCU厂商规定)
- 中断服务函数:发生中断时执行的函数
- 中断向量:中断号和中断服务函数的地址一一对应起来的一个记录,发生中断时根据中断号查找中断服务函数
- 中断向量表:多个中断向量组成的一张表,一款CPU可以支持多个中断,这些中断向量组成了中断向量表
在发生中断时,进入中断向量表,根据中断号查找对应的中断服务函数地址,进而去执行中断服务函数,执行完成后继续中断前的任务。
在一般的MCU中,中断号,中断服务函数名,中断向量表一般都是固定的,都在启动文件中定义,例如STM32,但是在linux中,中断服务函数名和中断向量表不是固定的,可以自定义中断服务函数名,所以就需要实现中断号和中断服务函数的绑定,进而构造出中断向量表,在linux中使用request_irq() 函数来实现这一需求,该函数可以用来实现中断号与中断服务函数的绑定,设置中断触发方式等功能,函数原型如下:
1 | int request_irq(unsigned int irq, |
irg为需要申请中断的中断号,可以通过查找芯片手册的中断表来查看,当然该值一般都在设备树节点中体现了,可以直接使用相关的API函数来获取,比如platform_get_irq函数,该函数从设备树节点中获取interrupts属性的值,该值即为中断号。
handler为中断服务函数,也就是发生中断后执行的函数,该参数是一个函数指针,类型为irq_handler_t,原型如下,第一个参数是中断号,第二个参数是个通用指针,需要与 request_irq 函数的 dev 参数保持一致。用于区分共享中断的不同设备。
1 | irqreturn_t (*irq_handler_t) (int, void *) |
flags为中断标志,可以设置中断的触发方式,可选的值如下所示,这些标志可以通过 “|” 来组合。
标志 | 描述 |
---|---|
IRQF_SHARED | 多个设备共享一个中断线,共享的所有中断都必须指定此标志。 |
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 的第二个参数。
中断申请成功后如果不再使用了需要注销掉这个中断,使用free_irq() 函数,该函数会解除中断号和中断服务函数之间的绑定关系,并且会禁止掉该中断,原型如下,参数irg表示需要注销中断的中断号,dev用来区分共享中断的设备。
1 | void free_irq(unsigned int irq,void *dev) |
二、其他API函数
1.获取中断号
在申请中断的时候需要使用到中断号,可以直接查找芯片手册上的中断描述,但是这样太麻烦了,现在都将所有的设备信息写到了设备树上了,所以可以直接去设备树节点上找,比如通过irq_of_parse_and_map() 函数,该函数从指定的节点中获取interrupts属性的值,该值描述了中断号,这个函数适合于使用了设备树的驱动,函数原型如下。
1 | unsigned int irq_of_parse_and_map(struct device_node *dev,int index) |
然后还有一个函数是 platform_get_irq() ,该函数也是从设备树节点中获取 interrupts 属性的值,不同的是这个函数需要基于 platform总线 使用,在编写platform_driver结构体的时候,有个probe成员,该成员指向一个函数,该函数的参数就是匹配到的设备树节点,platform的驱动和设备匹配成功后,就会向probe函数传递一个匹配到的设备树节点,我们可以直接使用这个参数,而 不用手动的获取某个节点信息 ,函数原型如下。
1 | int platform_get_irq(struct platform_device *dev, unsigned int num) |
如果使用 GPIO 的话,可以使用 gpio_to_irq 函数来获取 gpio 对应的中断号,函数原型如下,gpio即为要获取中断的 GPIO 编号。
1 | int gpio_to_irq(unsigned int gpio) |
设备树中关于中断的描述如下所示。
1 | key { |
综上所述,获取中断号的方法有以下几种。
方法 | 适用情况 |
---|---|
芯片手册 | 不太建议 |
irq_of_parse_and_map | 基于设备树 |
platform_get_irq | 基于设备树和platform总线 |
gpio_to_irq | 基于GPIO |
2.中断使能与禁能
1 | void enable_irq(unsigned int irq) |
enable_irq 和 disable_irq 用于使能和禁止指定的中断,irq 就是要禁止的中断号。disable_irq函数要等到当前正在执行的中断处理函数执行完才返回,因此使用者需要保证不会产生新的中断,并且确保所有已经开始执行的中断处理程序已经全部退出。
三、示例:基于platform下的按键中断
设备树节点:
设备树主要设备父中断控制器(interrupt-parent)和中断号(interrupts),这里的18其实不是中断号,是GPIO1_IO18中的18,因为按键接在了GPIO1上,所以中断号由父中断控制器管理,根据IO编号来选择对应的中断号,例如这里的按键接在了GPIO1_IO18,GPIO1的中断号有两个,0~15共用一个中断号,16~31共用一个中断号,则IO18的中断号就是16~31对应的中断号。
驱动代码链接:key_interuptDriver.c
加载步骤:
- 编译驱动文件,并复制到开发板中
- depmod , modprobe key_interuptDriver
实验现象:
按下按键时,会打印Thr INT is Triged