1 前言

项目上需要多个按键输入, 因此记录一下多个按键的中断如何做

2 修改设备树

     pinctrl_gpio_keys: gpio-keys {fsl,pins = <MX6UL_PAD_SNVS_TAMPER2__GPIO5_IO02    0x80000000  /* KEY1 */MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x80000000    /* KEY2 */MX6ULL_PAD_SNVS_TAMPER0__GPIO5_IO00   0x80000000  /* KEY3 */MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01   0x80000000  /* KEY4 */MX6ULL_PAD_SNVS_TAMPER4__GPIO5_IO04   0x80000000  /* KEY5 */>;};
    input_keys {#address-cells = <1>;#size-cells = <0>;compatible = "gpio-keys";pinctrl-names = "default";pinctrl-0 = <&pinctrl_gpio_keys>;status = "okay";key1@1 {label = "KEY1";interrupt-parent = <&gpio5>;interrupts = <2 IRQ_TYPE_EDGE_BOTH>;gpios = <&gpio5 2 GPIO_ACTIVE_LOW>;};key2@2 {label = "KEY2";interrupt-parent = <&gpio5>;interrupts = <8 IRQ_TYPE_EDGE_BOTH>;gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;};key3@3 {label = "KEY3";linux,code = <>;interrupt-parent = <&gpio5>;interrupts = <0 IRQ_TYPE_EDGE_BOTH>;gpios = <&gpio5 0 GPIO_ACTIVE_LOW>;};key4@4 {label = "KEY4";interrupt-parent = <&gpio5>;interrupts = <1 IRQ_TYPE_EDGE_BOTH>;gpios = <&gpio5 1 GPIO_ACTIVE_LOW>;};key5@5 {label = "KEY5";interrupt-parent = <&gpio5>;interrupts = <4 IRQ_TYPE_EDGE_BOTH>;gpios = <&gpio5 4 GPIO_ACTIVE_LOW>;};};

3  创建字符驱动

需要注意的是,在注册中断的时候, 对于每个中断都要有不同的中断函数和中断值, 不然就会注册失败 返回-22

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define KEY_NUM                5           /* 按键数量     */
#define IMX6UIRQ_NAME       "/input_keys" /* 名字       */
#define IMX6UIRQ_PROPETY_NAME   "gpios"
#define INVAKEY             0XFF        /* 无效的按键值 */
char key_value[] = {0x01,0x02,0x03,0x04,0x05};/* 中断IO描述结构体 */
struct irq_keydesc {int gpio;                               /* gpio */int irqnum;                               /* 中断号     */unsigned char value;                   /* 按键对应的键值 */char name[10];                         /* 名字 */irqreturn_t (*handler)(int, void *);    /* 中断服务函数 */
};/* imx6uirq设备结构体 */
struct imx6uirq_dev{dev_t devid;            /* 设备号   */ struct cdev cdev;       /* cdev     */                 struct class *class; /* 类        */struct device *device;    /* 设备    */int major;               /* 主设备号   */int minor;              /* 次设备号   */struct device_node  *nd; /* 设备节点 */ atomic_t keyvalue;      /* 有效的按键键值 */atomic_t releasekey;   /* 标记是否完成一次完成的按键,包括按下和释放 */struct timer_list timer;/* 定义一个定时器*/struct irq_keydesc irqkeydesc[KEY_NUM];   /* 按键init述数组 */unsigned char curkeynum;             /* 当前init按键号 */wait_queue_head_t r_wait;    /* 读等待队列头 */
};struct imx6uirq_dev imx6uirq; /* irq设备 *//* @description     : 中断服务函数,开启定时器       *                     定时器用于按键消抖。* @param - irq     : 中断号 * @param - dev_id    : 设备结构。* @return           : 中断执行结果*/
static irqreturn_t key0_handler(int irq, void *dev_id)
{struct imx6uirq_dev *dev = (struct imx6uirq_dev*)dev_id;dev->curkeynum = 0;dev->timer.data = (volatile long)dev_id;mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));    /* 10ms定时 */return IRQ_RETVAL(IRQ_HANDLED);
}static irqreturn_t key1_handler(int irq, void *dev_id)
{struct imx6uirq_dev *dev = (struct imx6uirq_dev*)dev_id;dev->curkeynum = 1;dev->timer.data = (volatile long)dev_id;mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));    /* 10ms定时 */return IRQ_RETVAL(IRQ_HANDLED);
}
static irqreturn_t key2_handler(int irq, void *dev_id)
{struct imx6uirq_dev *dev = (struct imx6uirq_dev*)dev_id;dev->curkeynum = 2;dev->timer.data = (volatile long)dev_id;mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));    /* 10ms定时 */return IRQ_RETVAL(IRQ_HANDLED);
}
static irqreturn_t key3_handler(int irq, void *dev_id)
{struct imx6uirq_dev *dev = (struct imx6uirq_dev*)dev_id;dev->curkeynum = 3;dev->timer.data = (volatile long)dev_id;mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));    /* 10ms定时 */return IRQ_RETVAL(IRQ_HANDLED);
}
static irqreturn_t key4_handler(int irq, void *dev_id)
{struct imx6uirq_dev *dev = (struct imx6uirq_dev*)dev_id;dev->curkeynum = 4;dev->timer.data = (volatile long)dev_id;mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));    /* 10ms定时 */return IRQ_RETVAL(IRQ_HANDLED);
}/* @description   : 定时器服务函数,用于按键消抖,定时器到了以后*               再次读取按键值,如果按键还是处于按下状态就表示按键有效。* @param - arg    : 设备结构变量* @return      : 无*/
void timer_function(unsigned long arg)
{unsigned char value;unsigned char num;struct irq_keydesc *keydesc;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;num = dev->curkeynum;keydesc = &dev->irqkeydesc[num];value = gpio_get_value(keydesc->gpio);   /* 读取IO值 */if(value == 0){                        /* 按下按键 */atomic_set(&dev->keyvalue, keydesc->value);}else{                                   /* 按键松开 */atomic_set(&dev->keyvalue, 0x80 | keydesc->value);atomic_set(&dev->releasekey, 1);   /* 标记松开按键,即完成一次完整的按键过程 */}               /* 唤醒进程 */if(atomic_read(&dev->releasekey)) {    /* 完成一次按键过程 *//* wake_up(&dev->r_wait); */wake_up_interruptible(&dev->r_wait);}
}/** @description  : 按键IO初始化* @param      : 无* @return       : 无*/
static int keyio_init(void)
{unsigned char i = 0;char name[10];int ret = 0;// 找节点imx6uirq.nd = of_find_node_by_path(IMX6UIRQ_NAME);if (imx6uirq.nd== NULL){printk("key node not find!\r\n");return -EINVAL;} // 中断函数imx6uirq.irqkeydesc[0].handler = key0_handler;imx6uirq.irqkeydesc[1].handler = key1_handler;imx6uirq.irqkeydesc[2].handler = key2_handler;imx6uirq.irqkeydesc[3].handler = key3_handler;imx6uirq.irqkeydesc[4].handler = key4_handler;/* 提取GPIO */struct device_node *cnp;struct device_node *prev = NULL;for(i=0;i<KEY_NUM;i++){cnp = of_get_next_child(imx6uirq.nd, prev);if(cnp==NULL){break;}prev = cnp;// 获取GPIO  imx6uirq.irqkeydesc[i].gpio = of_get_named_gpio(cnp, IMX6UIRQ_PROPETY_NAME, 0);memset(imx6uirq.irqkeydesc[i].name, 0, sizeof(name)); /* 缓冲区清零 */sprintf(imx6uirq.irqkeydesc[i].name, "KEY%d", i);      /* 组合名字 */gpio_request(imx6uirq.irqkeydesc[i].gpio, name);gpio_direction_input(imx6uirq.irqkeydesc[i].gpio);    // 获取中断号imx6uirq.irqkeydesc[i].irqnum = irq_of_parse_and_map(cnp, 0);// 设置中段值imx6uirq.irqkeydesc[i].value = key_value[i];/* 申请中断 */ret = request_irq(imx6uirq.irqkeydesc[i].irqnum, imx6uirq.irqkeydesc[i].handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, imx6uirq.irqkeydesc[i].name, &imx6uirq);if(ret < 0){printk("irq ret: %d %d request failed!\r\n", ret,imx6uirq.irqkeydesc[i].irqnum);return -EFAULT;}}// not calc allif(i!=KEY_NUM){printk("node find not enought!\r\n");return -1;}/* 创建定时器 */init_timer(&imx6uirq.timer);imx6uirq.timer.function = timer_function;/* 初始化等待队列头 */init_waitqueue_head(&imx6uirq.r_wait);return 0;
}/** @description      : 打开设备* @param - inode     : 传递给驱动的inode* @param - filp   : 设备文件,file结构体有个叫做private_data的成员变量*                       一般在open的时候将private_data指向设备结构体。* @return             : 0 成功;其他 失败*/
static int imx6uirq_open(struct inode *inode, struct file *filp)
{filp->private_data = &imx6uirq;    /* 设置私有数据 */return 0;
}/** @description     : 从设备读取数据 * @param - filp    : 要打开的设备文件(文件描述符)* @param - buf     : 返回给用户空间的数据缓冲区* @param - cnt     : 要读取的数据长度* @param - offt    : 相对于文件首地址的偏移* @return          : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int ret = 0;unsigned char keyvalue = 0;unsigned char releasekey = 0;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;if (filp->f_flags & O_NONBLOCK) { /* 非阻塞访问 */if(atomic_read(&dev->releasekey) == 0)    /* 没有按键按下,返回-EAGAIN */return -EAGAIN;} else {                            /* 阻塞访问 *//* 加入等待队列,等待被唤醒,也就是有按键按下 */ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey)); if (ret) {goto wait_error;}}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 0;wait_error:return ret;
data_error:return -EINVAL;
}/** @description     : poll函数,用于处理非阻塞访问* @param - filp    : 要打开的设备文件(文件描述符)* @param - wait    : 等待列表(poll_table)* @return          : 设备或者资源状态,*/
unsigned int imx6uirq_poll(struct file *filp, struct poll_table_struct *wait)
{unsigned int mask = 0;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;poll_wait(filp, &dev->r_wait, wait);    /* 将等待队列头添加到poll_table中 */if(atomic_read(&dev->releasekey)) {        /* 按键按下 */mask = POLLIN | POLLRDNORM;          /* 返回PLLIN */}return mask;
}/* 设备操作函数 */
static struct file_operations imx6uirq_fops = {.owner = THIS_MODULE,.open = imx6uirq_open,.read = imx6uirq_read,.poll = imx6uirq_poll,
};/** @description : 驱动入口函数* @param       : 无* @return       : 无*/
static int __init imx6uirq_init(void)
{/* 1、构建设备号 */if (imx6uirq.major) {imx6uirq.devid = MKDEV(imx6uirq.major, 0);register_chrdev_region(imx6uirq.devid, KEY_NUM, IMX6UIRQ_NAME);} else {alloc_chrdev_region(&imx6uirq.devid, 0, KEY_NUM, IMX6UIRQ_NAME);imx6uirq.major = MAJOR(imx6uirq.devid);imx6uirq.minor = MINOR(imx6uirq.devid);}/* 2、注册字符设备 */cdev_init(&imx6uirq.cdev, &imx6uirq_fops);cdev_add(&imx6uirq.cdev, imx6uirq.devid, KEY_NUM);/* 3、创建类 */imx6uirq.class = class_create(THIS_MODULE, IMX6UIRQ_NAME);if (IS_ERR(imx6uirq.class)) { return PTR_ERR(imx6uirq.class);}/* 4、创建设备 */imx6uirq.device = device_create(imx6uirq.class, NULL, imx6uirq.devid, NULL, IMX6UIRQ_NAME);if (IS_ERR(imx6uirq.device)) {return PTR_ERR(imx6uirq.device);}/* 5、始化按键 */atomic_set(&imx6uirq.keyvalue, INVAKEY);atomic_set(&imx6uirq.releasekey, 0);keyio_init();return 0;
}/** @description  : 驱动出口函数* @param       : 无* @return       : 无*/
static void __exit imx6uirq_exit(void)
{unsigned i = 0;/* 删除定时器 */del_timer_sync(&imx6uirq.timer);    /* 删除定时器 *//* 释放中断 */   for (i = 0; i < KEY_NUM; i++) {free_irq(imx6uirq.irqkeydesc[i].irqnum, &imx6uirq);}cdev_del(&imx6uirq.cdev);unregister_chrdev_region(imx6uirq.devid, KEY_NUM);device_destroy(imx6uirq.class, imx6uirq.devid);class_destroy(imx6uirq.class);
}   module_init(imx6uirq_init);
module_exit(imx6uirq_exit);
MODULE_LICENSE("GPL");

4 应用测试

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "poll.h"
#include "sys/select.h"
#include "sys/time.h"
#include "linux/ioctl.h"
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名     : noblockApp.c
作者      : 左忠凯
版本      : V1.0
描述      : 非阻塞访问测试APP
其他      : 无
使用方法    :./blockApp /dev/blockio 打开测试App
论坛      : www.openedv.com
日志      : 初版V1.0 2019/9/8 左忠凯创建
***************************************************************//** @description       : main主程序* @param - argc   : argv数组元素个数* @param - argv    : 具体参数* @return            : 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int fd;int ret = 0;char *filename;struct pollfd fds;fd_set readfds;struct timeval timeout;unsigned char data;if (argc != 2) {printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR | O_NONBLOCK);  /* 非阻塞访问 */if (fd < 0) {printf("Can't open file %s\r\n", filename);return -1;}while (1) { FD_ZERO(&readfds);FD_SET(fd, &readfds);/* 构造超时时间 */timeout.tv_sec = 0;timeout.tv_usec = 500000; /* 500ms */ret = select(fd + 1, &readfds, NULL, NULL, &timeout);switch (ret) {case 0:   /* 超时 *//* 用户自定义超时处理 */break;case -1:   /* 错误 *//* 用户自定义错误处理 */break;default:  /* 可以读取数据 */if(FD_ISSET(fd, &readfds)) {ret = read(fd, &data, sizeof(data));if (ret < 0) {/* 读取错误 */} else {if (data)printf("key value=%d\r\n", data);}}break;}   }close(fd);return ret;
}

5 测试结果

正点原子IMX6UL 多路按键中断实际案例相关推荐

  1. 正点原子IMX6UL 多个按键中断 输入子系统实战

    1 前言 前面把按键中断做了之后, 目前要添加到输入子系统中, 这样就可以制作矩阵键盘之类的 https://blog.csdn.net/a2267542848/article/details/119 ...

  2. 正点原子IMX6UL底板硬件设计指南

    自己买了正点原子核心板, 准备自己做一块底板出来 1 关键点 和单片机一样, 最关键是把最小系统弄出来, 因此就知道对imx6ul来说最小系统是什么 1 boot启动 imx6ul需要从USB启动烧录 ...

  3. 正点原子IMX6UL I2C驱动AT24C512

    参考: https://blog.csdn.net/zlsh007/article/details/21600759 1 需求 在imx6ul上完成EEROM驱动 2 修改设备树 查询数据手册得地址为 ...

  4. 正点原子IMX6UL ADC采集

    1 前言 项目需要用到IMX6UL ADC功能, 12位ADC 2 修改设备树 因为用到通道2和通道5, 所以一共有0-5个, 一共是6个通道 imx6ull.dtsi adc1: adc@02198 ...

  5. 正点原子IMX6UL 检测I2C上设备地址

    需要检测I2C上设备的地址, 原理图如下 &i2c1 {clock-frequency = <100000>;pinctrl-names = "default" ...

  6. hal库开启中断关中断_「正点原子NANO STM32开发板资料连载」第十章 外部中断实验...

    1)实验平台:ALIENTEK NANO STM32F411 V1开发板 2)摘自<正点原子STM32F4 开发指南(HAL 库版>关注官方微信号公众号,获取更多资料:正点原子 第十章 外 ...

  7. 【正点原子STM32连载】 第三十八章 红外遥控实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

    1)实验平台:正点原子MiniPro H750开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560 3)全套实验源码+手册+视频 ...

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

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  9. 32 串口波特率_「正点原子FPGA连载」第五章串口中断实验

    1)实验平台:正点原子达芬奇FPGA开发板 2) 摘自[正点原子]达芬奇之Microblaze 开发指南 3)购买链接:https://detail.tmall.com/item.htm?id=624 ...

最新文章

  1. 小程序统一服务消息_[miniblog]小程序订阅消息踩坑记
  2. vue router传参_新手使用vue-router传参时注意事项
  3. 泛型类的定义与实例化 c#
  4. Flash开发iOS应用全攻略(四)——如何为iTunes Connect准备应用
  5. vb查询xml中特定节点下的标签_Python 标准库之XML
  6. Android开发中Ant命令编译和APK签名的一些心得
  7. python解析properties文件
  8. Linux上安装JDK
  9. Linux下的经典软件
  10. 华为网络模拟器eNSP安装教程
  11. VS2012下安装GDAL库
  12. 在ghost时加载smartdrv.exe是否会提高速度
  13. vue项目退出登录清除 store 数据
  14. 计算机信息管理调查报告模板,精选市场调查报告模板锦集九篇
  15. BIGO全球音视频技术解决方案
  16. python跳出双循环break图例
  17. 生成对抗网络 Generative Adversarial Nets(GAN)详解
  18. StrandHogg漏洞修复
  19. win8dns服务器没响应,win8笔记本dns服务器未响应怎么办
  20. 微信小程序时间显示几分钟前、几小时前、几天前....

热门文章

  1. cmd修改本机永久路由地址
  2. 相机基础知识讲解:CMOS和CCD
  3. 小练习----春夏秋冬,回文数,不死神兔,去掉最高最低求和,数组反转,简单加密,switch语句
  4. 侠客风云传未能连接到服务器,《侠客风云传:前传》无法启动解决方法
  5. 移动数字图书馆,基于UniApp,Android实现图书管理系统
  6. asm (“ NOP 5“)详解
  7. 还不明白中国股市现状的人请进(转天涯)
  8. vim快捷键的使用案例
  9. 【UE4 附源工程】GGJ 2020极限48小时开发的逆向解谜游戏
  10. 【LeetCode】860. 柠檬水找零(C语言)