1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html 3)对正点原子Linux感兴趣的同学可以加群讨论:935446741 4)关注正点原子公众号,获取最新资料更新
第五十一章 Linux中断实验
不管是裸机实验还是Linux下的驱动实验,中断都是频繁使用的功能,关于I.MX6U的中断原理已经在第十七章做了详细的讲解,在裸机中使用中断我们需要做一大堆的工作,比如配置寄存器,使能IRQ等等。Linux内核提供了完善的中断框架,我们只需要申请中断,然后注册中断处理函数即可,使用非常方便,不需要一系列复杂的寄存器配置。本章我们就来学习一下如何在Linux下使用中断。
51.1 Linux中断简介 51.1.1 Linux中断API函数 先来回顾一下裸机实验里面中断的处理方法: ①、使能中断,初始化相应的寄存器。 ②、注册中断服务函数,也就是向irqTable数组的指定标号处写入中断服务函数 ②、中断发生以后进入IRQ中断服务函数,在IRQ中断服务函数在数组irqTable里面查找具体的中断处理函数,找到以后执行相应的中断处理函数。 在Linux内核中也提供了大量的中断相关的API函数,我们来看一下这些跟中断有关的API函数: 1、中断号 每个中断都有一个中断号,通过中断号即可区分不同的中断,有的资料也把中断号叫做中断线。在Linux内核中使用一个int变量表示中断号,关于中断号我们已经在第十七章讲解过了。 2、request_irq函数 在Linux内核中要想使用某个中断是需要申请的,request_irq函数用于申请中断,request_irq函数可能会导致睡眠,因此不能在中断上下文或者其他禁止睡眠的代码段中使用request_irq函数。request_irq函数会激活(使能)中断,所以不需要我们手动去使能中断,request_irq函数原型如下:
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里面查看所有的中断标志,这里我们介绍几个常用的中断标志,如表51.1.1.1所示: 标志 描述 IRQF_SHARED 多个设备共享一个中断线,共享的所有中断都必须指定此标志。如果使用共享中断的话,request_irq函数的dev参数就是唯一区分他们的标志。 IRQF_ONESHOT 单次中断,中断执行一次就结束。 IRQF_TRIGGER_NONE 无触发。 IRQF_TRIGGER_RISING 上升沿触发。 IRQF_TRIGGER_FALLING 下降沿触发。 IRQF_TRIGGER_HIGH 高电平触发。 IRQF_TRIGGER_LOW 低电平触发。 表51.1.1.1 常用的中断标志 比如I.MX6U-ALPHA开发板上的KEY0使用GPIO1_IO18,按下KEY0以后为低电平,因此可以设置为下降沿触发,也就是将flags设置为IRQF_TRIGGER_FALLING。表51.1.1.1中的这些标志可以通过“|”来实现多种组合。 name:中断名字,设置以后可以在/proc/interrupts文件中看到对应的中断名字。 dev:如果将flags设置为IRQF_SHARED的话,dev用来区分不同的中断,一般情况下将dev设置为设备结构体,dev会传递给中断处理函数irq_handler_t的第二个参数。 返回值:0 中断申请成功,其他负值 中断申请失败,如果返回-EBUSY的话表示中断已经被申请了。 3、free_irq函数 使用中断的时候需要通过request_irq函数申请,使用完成以后就要通过free_irq函数释放掉相应的中断。如果中断不是共享的,那么free_irq会删除中断处理函数并且禁止中断。free_irq函数原型如下所示: void free_irq(unsigned int irq, void *dev) 函数参数和返回值含义如下: irq:要释放的中断。 dev:如果中断设置为共享(IRQF_SHARED)的话,此参数用来区分具体的中断。共享中断只有在释放最后中断处理函数的时候才会被禁止掉。 返回值:无。 4、中断处理函数 使用request_irq函数申请中断的时候需要设置中断处理函数,中断处理函数格式如下所示: irqreturn_t (*irq_handler_t) (int, void *) 第一个参数是要中断处理函数要相应的中断号。第二个参数是一个指向void的指针,也就是个通用指针,需要与request_irq函数的dev参数保持一致。用于区分共享中断的不同设备,dev也可以指向设备数据结构。中断处理函数的返回值为irqreturn_t类型,irqreturn_t类型定义如下所示: 示例代码51.1.1.1 irqreturn_t结构
10 enum irqreturn {
11 IRQ_NONE = (0 curkeynum;
93 keydesc = &dev->irqkeydesc[num];
94
95 value = gpio_get_value(keydesc->gpio); /* 读取IO值 */
96 if(value == 0){ /* 按下按键 */
97 atomic_set(&dev->keyvalue, keydesc->value);
98 }
99 else{ /* 按键松开 */
100 atomic_set(&dev->keyvalue, 0x80 | keydesc->value);
101 atomic_set(&dev->releasekey, 1); /* 标记松开按键 */
102 }
103 }
104
105 /*
106 * @description : 按键IO初始化
107 * @param : 无
108 * @return : 无
109 */
110 static int keyio_init(void)
111 {
112 unsigned char i = 0;
113
114 int ret = 0;
115
116 imx6uirq.nd = of_find_node_by_path("/key");
117 if (imx6uirq.nd== NULL){
118 printk("key node not find!\r\n");
119 return -EINVAL;
120 }
121
122 /* 提取GPIO */
123 for (i = 0; i releasekey, 0); /* 按下标志清零 */
200 } else {
201 goto data_error;
202 }
203 return 0;
204
205 data_error:
206 return -EINVAL;
207 }
208
209 /* 设备操作函数 */
210 static struct file_operations imx6uirq_fops = {
211 .owner = THIS_MODULE,
212 .open = imx6uirq_open,
213 .read = imx6uirq_read,
214 };
215
216 /*
217 * @description : 驱动入口函数
218 * @param : 无
219 * @return : 无
220 */
221 static int __init imx6uirq_init(void)
222 {
223 /* 1、构建设备号 */
224 if (imx6uirq.major) {
225 imx6uirq.devid = MKDEV(imx6uirq.major, 0);
226 register_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT,
IMX6UIRQ_NAME);
227 } else {
228 alloc_chrdev_region(&imx6uirq.devid, 0, IMX6UIRQ_CNT,
IMX6UIRQ_NAME);
229 imx6uirq.major = MAJOR(imx6uirq.devid);
230 imx6uirq.minor = MINOR(imx6uirq.devid);
231 }
232
233 /* 2、注册字符设备 */
234 cdev_init(&imx6uirq.cdev, &imx6uirq_fops);
235 cdev_add(&imx6uirq.cdev, imx6uirq.devid, IMX6UIRQ_CNT);
236
237 /* 3、创建类 */
238 imx6uirq.class = class_create(THIS_MODULE, IMX6UIRQ_NAME);
239 if (IS_ERR(imx6uirq.class)) {
240 return PTR_ERR(imx6uirq.class);
241 }
242
243 /* 4、创建设备 */
244 imx6uirq.device = device_create(imx6uirq.class, NULL,
imx6uirq.devid, NULL, IMX6UIRQ_NAME);
245 if (IS_ERR(imx6uirq.device)) {
246 return PTR_ERR(imx6uirq.device);
247 }
248
249 /* 5、初始化按键 */
250 atomic_set(&imx6uirq.keyvalue, INVAKEY);
251 atomic_set(&imx6uirq.releasekey, 0);
252 keyio_init();
253 return 0;
254 }
255
256 /*
257 * @description : 驱动出口函数
258 * @param : 无
259 * @return : 无
260 */
261 static void __exit imx6uirq_exit(void)
262 {
263 unsigned int i = 0;
264 /* 删除定时器 */
265 del_timer_sync(&imx6uirq.timer);
266
267 /* 释放中断 */
268 for (i = 0; i
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?