目录
一、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 0
dev->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 0
dev->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;
}