您当前的位置: 首页 >  嵌入式

正点原子

暂无认证

  • 3浏览

    0关注

    382博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【正点原子Linux连载】第五十一章 Linux中断实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

正点原子 发布时间:2021-09-06 10:55:40 ,浏览量:3

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             
关注
打赏
1665308814
查看更多评论
0.1629s