正点原子IMX6UL 多路按键中断实际案例
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 多路按键中断实际案例相关推荐
- 正点原子IMX6UL 多个按键中断 输入子系统实战
1 前言 前面把按键中断做了之后, 目前要添加到输入子系统中, 这样就可以制作矩阵键盘之类的 https://blog.csdn.net/a2267542848/article/details/119 ...
- 正点原子IMX6UL底板硬件设计指南
自己买了正点原子核心板, 准备自己做一块底板出来 1 关键点 和单片机一样, 最关键是把最小系统弄出来, 因此就知道对imx6ul来说最小系统是什么 1 boot启动 imx6ul需要从USB启动烧录 ...
- 正点原子IMX6UL I2C驱动AT24C512
参考: https://blog.csdn.net/zlsh007/article/details/21600759 1 需求 在imx6ul上完成EEROM驱动 2 修改设备树 查询数据手册得地址为 ...
- 正点原子IMX6UL ADC采集
1 前言 项目需要用到IMX6UL ADC功能, 12位ADC 2 修改设备树 因为用到通道2和通道5, 所以一共有0-5个, 一共是6个通道 imx6ull.dtsi adc1: adc@02198 ...
- 正点原子IMX6UL 检测I2C上设备地址
需要检测I2C上设备的地址, 原理图如下 &i2c1 {clock-frequency = <100000>;pinctrl-names = "default" ...
- hal库开启中断关中断_「正点原子NANO STM32开发板资料连载」第十章 外部中断实验...
1)实验平台:ALIENTEK NANO STM32F411 V1开发板 2)摘自<正点原子STM32F4 开发指南(HAL 库版>关注官方微信号公众号,获取更多资料:正点原子 第十章 外 ...
- 【正点原子STM32连载】 第三十八章 红外遥控实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1
1)实验平台:正点原子MiniPro H750开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560 3)全套实验源码+手册+视频 ...
- 【正点原子Linux连载】第十七章GPIO中断试验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0
1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...
- 32 串口波特率_「正点原子FPGA连载」第五章串口中断实验
1)实验平台:正点原子达芬奇FPGA开发板 2) 摘自[正点原子]达芬奇之Microblaze 开发指南 3)购买链接:https://detail.tmall.com/item.htm?id=624 ...
最新文章
- 小程序统一服务消息_[miniblog]小程序订阅消息踩坑记
- vue router传参_新手使用vue-router传参时注意事项
- 泛型类的定义与实例化 c#
- Flash开发iOS应用全攻略(四)——如何为iTunes Connect准备应用
- vb查询xml中特定节点下的标签_Python 标准库之XML
- Android开发中Ant命令编译和APK签名的一些心得
- python解析properties文件
- Linux上安装JDK
- Linux下的经典软件
- 华为网络模拟器eNSP安装教程
- VS2012下安装GDAL库
- 在ghost时加载smartdrv.exe是否会提高速度
- vue项目退出登录清除 store 数据
- 计算机信息管理调查报告模板,精选市场调查报告模板锦集九篇
- BIGO全球音视频技术解决方案
- python跳出双循环break图例
- 生成对抗网络 Generative Adversarial Nets(GAN)详解
- StrandHogg漏洞修复
- win8dns服务器没响应,win8笔记本dns服务器未响应怎么办
- 微信小程序时间显示几分钟前、几小时前、几天前....
热门文章
- cmd修改本机永久路由地址
- 相机基础知识讲解:CMOS和CCD
- 小练习----春夏秋冬,回文数,不死神兔,去掉最高最低求和,数组反转,简单加密,switch语句
- 侠客风云传未能连接到服务器,《侠客风云传:前传》无法启动解决方法
- 移动数字图书馆,基于UniApp,Android实现图书管理系统
- asm (“ NOP 5“)详解
- 还不明白中国股市现状的人请进(转天涯)
- vim快捷键的使用案例
- 【UE4 附源工程】GGJ 2020极限48小时开发的逆向解谜游戏
- 【LeetCode】860. 柠檬水找零(C语言)