Linux 中断实验
目录
一、Linux 中断简介
上半部与下半部
二、添加设备树
三、编写驱动
1、定义宏
2、编写一个key结构体
3、imx6uirq设备添加成员
编辑4、按键中断处理函数
5、按键初始化
6、在驱动入口添加初始化
7、 驱动出口函数
代码如下
四、利用定时器进行消抖处理
1、添加定时器
2、修改中断处理函数
3、添加定时器处理函数
4、在按键初始化函数最后添加初始化定时器
5、驱动出口函数添加删除定时器
五、编写测试app
1、添加原子操作
2、初始化原子变量
3、按下和释放操作
4、读函数操作
总体代码如下
一、Linux 中断简介
在 Linux 内核中要想使用某个中断是需要申请的,使用完成以后就要释放掉相应的中断,申请中断的时候需要设置中断处理函数,还有设置使能中断与禁止
上半部与下半部
有些资料中也将上半部和下半部称为顶半部和底半部。
上半部:上半部就是中断处理函数,那些处理过程比较快,不会占用很长时间的处理就可以放在上半部完成。
下半部:如果中断处理过程比较耗时,那么就将这些比较耗时的代码提出来,交给下半部去执行,这样中断处理函数就会快进快出。
至于哪些代码属于上半部,哪些代码属于下半部并没有明确的规定,如果要处理的内容不希望被其他中断打断、处理的任务对时间敏感或处理的任务与硬件有关,那么可以放到上半部,其余以外的其他任务,优先考虑放到下半部
二、添加设备树
打开原理图找到KEY
可以看到KEY0是接到UART1 CTS上
打开参考手册,找到IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_B
看到是可以复用为GPIO1_IO18的
根据之前key驱动实验编写的key节点,添加 184-185两行
184行, 指定父中断,也就是中断控制器,因为key复用为GPIO1_IO18,所以为gpio1
185行,设置中断源,指定中断号,触发方式。因为复用GPIO1_IO18,所以为18,
IRQ_TYPE_EDGE_BOTH 定义在文件 include/linux/irq.h 中
IRQ_TYPE_EDGE_BOTH 表示上升沿和下降沿同时有效,相当于KEY0 按下和释放都会触发中断
添加完成之后编译设备树,用此设备树启动
三、编写驱动
导入模板,修改makefile
1、定义宏
2、编写一个key结构体
irq_desc为中断描述符,这里是key,所以用irq_keydesc
3、imx6uirq设备添加成员
在imx6uirq设备中使用,用数组的形式主要是方便添加多个按键
4、按键中断处理函数
实现key结构体中的按键中断处理函数
71行,中断处理函数格式如下
irqreturn_t (*irq_handler_t) (int, void *)
第一个参数是要中断处理函数要相应的中断号。第二个参数是一个指向 void 的指针,也就是个通用指针,用于区分共享中断的不同设备,也可以指向设备数据结构。中断处理函数的返回值为 irqreturn_t 类型。
73行,dev_id指向dev的设备,也就是imx6uirq
74行,获取gpio的值
5、按键初始化
88行,通过路径查找设备树节点
93-94行,通过循环来进行获取关于节点属性
100-103行,通过循环初始化名字数组;sprintf()用来作格式化的输出;申请使用gpio
109行,设置gpio输出
111行和113行作用一样,都是获取中断号,前者只能利用gpio获取,后者利用通过属性索引获取
116行,设置key0的中断处理函数
117行,设置key0的按键值
120行,在 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 里面查看所有的中断标志。这里使用上升沿和下降沿触发
name:中断名字,设置以后可以在/proc/interrupts 文件中看到对应的中断名字。
dev: 如果将 flags 设置为 IRQF_SHARED 的话, dev 用来区分不同的中断,一般情况下将dev 设置为设备结构体, dev 会传递给中断处理函数 irq_handler_t 的第二个参数。
返回值: 0 中断申请成功,其他负值 中断申请失败,如果返回-EBUSY 的话表示中断已经
被申请了
6、在驱动入口添加初始化
7、 驱动出口函数
代码如下
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/atomic.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/string.h>
#include <linux/irq.h>
#include <linux/interrupt.h>#define IMX6UIRQ_CNT 1
#define IMX6UIRQ_NAME "imx6uirq"
#define KEY_NUM 1 /* 按键数量 */
#define KEY0VALUE 0X01 /* KEY0按键值 */
#define INVAKEY 0XFF /* 无效的按键值 *//*key结构体*/
struct irq_keydesc{int gpio; /*io编号*/int irqnum; /*中断号*/unsigned char value; /*键值*/char name[10]; /*名字*/irqreturn_t(*handler) (int ,void *); /*中断处理函数*/
};struct imx6uirq_dev{dev_t devid;int major;int minor;struct cdev cdev;struct class *class;struct device *device;struct device_node *nd;struct irq_keydesc irqkey[KEY_NUM];
}imx6uirq;
static int imx6uirq_open(struct inode *inode, struct file *filp){filp->private_data = &imx6uirq;return 0;
}
static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}
static ssize_t imx6uirq_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}
static int imx6uirq_release(struct inode *inode, struct file *filp){return 0;
}static const struct file_operations imx6uirq_fops = {.owner = THIS_MODULE,.open = imx6uirq_open,.read = imx6uirq_read,.write = imx6uirq_write,.release = imx6uirq_release,
};
/*按键中段处理函数*/
static irqreturn_t key0_handler(int irq,void *dev_id){int value = 0;struct imx6uirq_dev * dev = dev_id;value = gpio_get_value(dev->irqkey[0].gpio);if(value == 0){/*按下*/printk("KEY0 push\r\n");}else if(value == 1){/*释放*/printk("KEY0 release\r\n");}return IRQ_HANDLED;
}
/*按鍵初始化*/
static int keyio_init(struct imx6uirq_dev *dev){int i,ret =0 ;/*按鍵初始化*/dev->nd = of_find_node_by_path("/key");if(dev->nd == NULL){ret = -EINVAL;goto fail_nd;}for(i=0;i<KEY_NUM;i++){dev->irqkey[i].gpio = of_get_named_gpio(dev->nd,"key-gpios",i);if(dev->irqkey[i].gpio < 0){ret = -EINVAL;goto fail_gpio;}}for(i=0;i<KEY_NUM;i++){memset(dev->irqkey[i].name,0,sizeof(dev->irqkey[i].name));sprintf(dev->irqkey[i].name,"KEY%d",i);ret = gpio_request(dev->irqkey[i].gpio , dev->irqkey[i].name);if(ret){ret = -EBUSY;printk("IO %d can't request\r\n",dev->irqkey[i].gpio);goto fail_request;}gpio_direction_input(dev->irqkey[i].gpio);/*获取中断号*/dev->irqkey[i].irqnum = gpio_to_irq(dev->irqkey[i].gpio);
#if 0dev->irqkey[i].irqnum = irq_of_parse_and_map(dev->nd,i);#endif}dev->irqkey[0].handler = key0_handler;dev->irqkey[0].value = KEY0VALUE;/*按键中断初始化*/for(i=0;i<KEY_NUM;i++){ret = request_irq(dev->irqkey[i].irqnum,dev->irqkey[i].handler,IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING,dev->irqkey[i].name,&imx6uirq);if(ret){printk("irq %d request failed!\r\n",dev->irqkey[i].irqnum);goto fail_irq;}}return 0;
fail_irq:for(i=0;i<KEY_NUM;i++){gpio_free(dev->irqkey[i].gpio);}
fail_request:
fail_gpio:
fail_nd:return ret;
}static int __init imx6uirq_init(void){int ret = 0;imx6uirq.major = 0;if(imx6uirq.major){imx6uirq.devid =MKDEV(imx6uirq.major,0);ret = register_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT,IMX6UIRQ_NAME);if(ret < 0){goto fail_devid;}}else{ret = alloc_chrdev_region(&imx6uirq.devid,0,IMX6UIRQ_CNT,IMX6UIRQ_NAME);if(ret < 0){goto fail_devid;}imx6uirq.major = MAJOR(imx6uirq.devid);imx6uirq.minor = MINOR(imx6uirq.devid);printk("imx6uirq major = %d, minor = %d\r\n", imx6uirq.major, imx6uirq.minor);}imx6uirq.cdev.owner = THIS_MODULE;cdev_init(&imx6uirq.cdev, &imx6uirq_fops);ret = cdev_add(&imx6uirq.cdev,imx6uirq.devid,IMX6UIRQ_CNT);if(ret){goto fail_cdevadd;}imx6uirq.class = class_create(THIS_MODULE,IMX6UIRQ_NAME);if(IS_ERR(imx6uirq.class)){ret = PTR_ERR(imx6uirq.class);goto fail_class;}imx6uirq.device = device_create(imx6uirq.class,NULL,imx6uirq.devid,NULL,IMX6UIRQ_NAME);if(IS_ERR(imx6uirq.device)){ret = PTR_ERR(imx6uirq.device);goto fail_device;}/*初始化IO*/ret = keyio_init(&imx6uirq);if(ret < 0){goto fail_keyinit;}return 0;
fail_keyinit:device_destroy(imx6uirq.class,IMX6UIRQ_CNT);
fail_device:class_destroy(imx6uirq.class);
fail_class:cdev_del(&imx6uirq.cdev);
fail_cdevadd:unregister_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT);
fail_devid:return ret;
}
static void __exit imx6uirq_exit(void){int i=0;/*释放中断*/for(i=0;i<KEY_NUM;i++){free_irq(imx6uirq.irqkey[i].irqnum,&imx6uirq);}/*释放IO*/for(i=0;i<KEY_NUM;i++){gpio_free(imx6uirq.irqkey[i].gpio);}device_destroy(imx6uirq.class,imx6uirq.devid);class_destroy(imx6uirq.class);cdev_del(&imx6uirq.cdev);unregister_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT);printk("imx6uirq_exit\r\n");
}module_init(imx6uirq_init);
module_exit(imx6uirq_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("ba che kai qi lai");
编译到开发板上,按下按键0,就会打印相关内容,释放也会,注意此处没做消抖
四、利用定时器进行消抖处理
1、添加定时器
2、修改中断处理函数
在进入到中断处理后,定时器处理函数会周期延时20毫秒作消抖处理
3、添加定时器处理函数
消抖后判断是否按下按键
4、在按键初始化函数最后添加初始化定时器
5、驱动出口函数添加删除定时器
加载驱动进行测试之后,按键按下和释放都会相对准确,在按下和释放速度过快就会打印相同信息的情况
五、编写测试app
1、添加原子操作
2、初始化原子变量
3、按下和释放操作
4、读函数操作
总体代码如下
驱动
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/atomic.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/string.h>
#include <linux/irq.h>
#include <linux/interrupt.h>#define IMX6UIRQ_CNT 1
#define IMX6UIRQ_NAME "imx6uirq"
#define KEY_NUM 1 /* 按键数量 */
#define KEY0VALUE 0X01 /* KEY0按键值 */
#define INVAKEY 0XFF /* 无效的按键值 *//*key结构体*/
struct irq_keydesc{int gpio; /*io编号*/int irqnum; /*中断号*/unsigned char value; /*键值*/char name[10]; /*名字*/irqreturn_t(*handler) (int ,void *); /*中断处理函数*/
};struct imx6uirq_dev{dev_t devid;int major;int minor;struct cdev cdev;struct class *class;struct device *device;struct device_node *nd;struct irq_keydesc irqkey[KEY_NUM];struct timer_list timer;atomic_t keyvalue;atomic_t releasekey;
}imx6uirq;
static int imx6uirq_open(struct inode *inode, struct file *filp){filp->private_data = &imx6uirq;return 0;
}
static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int ret = 0;unsigned char keyvalue;unsigned char releasekey;struct imx6uirq_dev *dev = filp->private_data;keyvalue = atomic_read(&dev->keyvalue);releasekey = atomic_read(&dev->releasekey);if(releasekey){/*有效按键*/if(keyvalue & 0x80){keyvalue &= ~0x80;ret=__copy_to_user(buf,&keyvalue,sizeof(keyvalue));}else{goto data_error;}atomic_set(&dev->releasekey,0);/*按下标志清零*/}else{goto data_error;}return ret;
data_error:return -EINVAL;
}
static ssize_t imx6uirq_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}
static int imx6uirq_release(struct inode *inode, struct file *filp){return 0;
}static const struct file_operations imx6uirq_fops = {.owner = THIS_MODULE,.open = imx6uirq_open,.read = imx6uirq_read,.write = imx6uirq_write,.release = imx6uirq_release,
};
/*按键中断处理函数*/
static irqreturn_t key0_handler(int irq,void *dev_id){struct imx6uirq_dev * dev = dev_id;dev->timer.data = (volatile unsigned long)dev_id;mod_timer(&dev->timer,jiffies + msecs_to_jiffies(20));/*10ms*/return IRQ_HANDLED;
}
/*定时器处理函数*/
static void timer_func(unsigned long arg){int value = 0;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;value = gpio_get_value(dev->irqkey[0].gpio);if(value == 0){/*按下*/atomic_set(&dev->keyvalue,dev->irqkey[0].value);}else if(value == 1){/*释放*/atomic_set(&dev->keyvalue,0x80 | (dev->irqkey[0].value));atomic_set(&dev->releasekey,1);}}
/*按鍵初始化*/
static int keyio_init(struct imx6uirq_dev *dev){int i,ret =0 ;/*按鍵初始化*/dev->nd = of_find_node_by_path("/key");if(dev->nd == NULL){ret = -EINVAL;goto fail_nd;}for(i=0;i<KEY_NUM;i++){dev->irqkey[i].gpio = of_get_named_gpio(dev->nd,"key-gpios",i);if(dev->irqkey[i].gpio < 0){ret = -EINVAL;goto fail_gpio;}}for(i=0;i<KEY_NUM;i++){memset(dev->irqkey[i].name,0,sizeof(dev->irqkey[i].name));sprintf(dev->irqkey[i].name,"KEY%d",i);ret = gpio_request(dev->irqkey[i].gpio , dev->irqkey[i].name);if(ret){ret = -EBUSY;printk("IO %d can't request\r\n",dev->irqkey[i].gpio);goto fail_request;}gpio_direction_input(dev->irqkey[i].gpio);/*获取中断号*/dev->irqkey[i].irqnum = gpio_to_irq(dev->irqkey[i].gpio);
#if 0dev->irqkey[i].irqnum = irq_of_parse_and_map(dev->nd,i);#endif}dev->irqkey[0].handler = key0_handler;dev->irqkey[0].value = KEY0VALUE;/*按键中断初始化*/for(i=0;i<KEY_NUM;i++){ret = request_irq(dev->irqkey[i].irqnum,dev->irqkey[i].handler,IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING,dev->irqkey[i].name,&imx6uirq);if(ret){printk("irq %d request failed!\r\n",dev->irqkey[i].irqnum);goto fail_irq;}}/*初始化定时器*/init_timer(&imx6uirq.timer);imx6uirq.timer.function = timer_func;return 0;
fail_irq:for(i=0;i<KEY_NUM;i++){gpio_free(dev->irqkey[i].gpio);}
fail_request:
fail_gpio:
fail_nd:return ret;
}static int __init imx6uirq_init(void){int ret = 0;imx6uirq.major = 0;if(imx6uirq.major){imx6uirq.devid =MKDEV(imx6uirq.major,0);ret = register_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT,IMX6UIRQ_NAME);if(ret < 0){goto fail_devid;}}else{ret = alloc_chrdev_region(&imx6uirq.devid,0,IMX6UIRQ_CNT,IMX6UIRQ_NAME);if(ret < 0){goto fail_devid;}imx6uirq.major = MAJOR(imx6uirq.devid);imx6uirq.minor = MINOR(imx6uirq.devid);printk("imx6uirq major = %d, minor = %d\r\n", imx6uirq.major, imx6uirq.minor);}imx6uirq.cdev.owner = THIS_MODULE;cdev_init(&imx6uirq.cdev, &imx6uirq_fops);ret = cdev_add(&imx6uirq.cdev,imx6uirq.devid,IMX6UIRQ_CNT);if(ret){goto fail_cdevadd;}imx6uirq.class = class_create(THIS_MODULE,IMX6UIRQ_NAME);if(IS_ERR(imx6uirq.class)){ret = PTR_ERR(imx6uirq.class);goto fail_class;}imx6uirq.device = device_create(imx6uirq.class,NULL,imx6uirq.devid,NULL,IMX6UIRQ_NAME);if(IS_ERR(imx6uirq.device)){ret = PTR_ERR(imx6uirq.device);goto fail_device;}/*初始化IO*/ret = keyio_init(&imx6uirq);if(ret < 0){goto fail_keyinit;}/*初始化原子变量*/atomic_set(&imx6uirq.keyvalue,INVAKEY);atomic_set(&imx6uirq.releasekey,0);return 0;
fail_keyinit:device_destroy(imx6uirq.class,IMX6UIRQ_CNT);
fail_device:class_destroy(imx6uirq.class);
fail_class:cdev_del(&imx6uirq.cdev);
fail_cdevadd:unregister_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT);
fail_devid:return ret;
}
static void __exit imx6uirq_exit(void){int i=0;/*释放中断*/for(i=0;i<KEY_NUM;i++){free_irq(imx6uirq.irqkey[i].irqnum,&imx6uirq);}/*释放IO*/for(i=0;i<KEY_NUM;i++){gpio_free(imx6uirq.irqkey[i].gpio);}del_timer_sync(&imx6uirq.timer);device_destroy(imx6uirq.class,imx6uirq.devid);class_destroy(imx6uirq.class);cdev_del(&imx6uirq.cdev);unregister_chrdev_region(imx6uirq.devid,IMX6UIRQ_CNT);printk("imx6uirq_exit\r\n");
}module_init(imx6uirq_init);
module_exit(imx6uirq_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("ba che kai qi lai");
APP
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>#define CLOSE_CMD _IO(0XEF ,1) /*关闭命令*/
#define OPEN_CMD _IO(0XEF,2) /*打开命令*/
#define SETPERIOD_CMD _IO(0XEF,3) /*设置周期*/int main(int argc, char *argv[])
{int fd,ret;char *filename;unsigned char data;/*判断命令行输入参数是否正确*/if(argc != 2){printf("error usage!\r\n");return -1;}/*用指针指向文件*/filename = argv[1];/*打开文件*/fd = open(filename , O_RDWR);if(fd < 0){printf("file open failed\r\n",filename);return -1;}/*循环读取*/while(1){ret = read(fd,&data,sizeof(data));if(ret<0){}else{if(data)printf(" key vaule = %d\r\n",data);}}/*关闭文件*/close(fd);return 0;
}