31.1 前言

按键是设备中是最常见的人机交互方式,本节中将学习两部分。

(1)如何4个GPIO 16个按键的实现;

(2)Linux input按键驱动开发实例编程;

31.2 4个IO驱动16按键原理

在常见的按键驱动中,我们可以设计一个按键矩阵,给按键设定一个坐标。如由8个GPIO驱动的4x4矩阵按键,便是通过水平、垂直两个维度对按键进行坐标化。本节讲到的由4GPIO驱动16按键的设计,是GPIO按键驱动按键的一种优化,该方案需要软硬结合,在按键矩阵中添加晶体管,以完成4GPIO驱动16按键的方案。硬件实现如下示:

如上图示,4个GPIO默认都是上拉输入(即默认在高电平状态),当有按键按下时,4个GPIO的某个就会被拉为低电平。读取数值的原理为通过扫描4个GPIO进行按键判断。

扫描方法如下:

(1)KEY1-4 设置为上拉输入,程序依次读取这四个IO的值,此时读取的到值是对应上图E列的按键,当发现某个IO为低电平时,返回此时扫描的序号,该序号可以辨别是哪个按键按下的;

(2)设KEY1输出低电平,KEY2-4设置为上拉输入,程序读取KEY2-4IO的值,此时对应的是A列IO

(3)设KEY2输出低电平,KEY1、KEY3-4设置为上拉输入,程序读取KEY1、KEY3-4IO的值,此时对应的是B列IO

(4)设KEY3输出低电平,KEY1-2、KEY-4设置为上拉输入,程序读取KEY1-2、KEY-4IO的值,此时对应的是C列IO

(5)设KEY4输出低电平,KEY1-3设置为上拉输入,程序读取KEY1-3IO的值,此时对应的是D列IO

读取按键代码实现:

static int read_kbd_key(void)
{  int key_num = 0;  int i, j;  /*设GPIO全为上拉输入*/  if (gpio_direction_set(0x00) == -1)  goto err_direction_set;  /*读取E列IO*/  for (j = 0; j < 4; j++) {  if ((gpio_get_value(gpio_pin[j]) & 0x01) == 0)  return key_num;  key_num++;  }  /*读取A-D列IO*/  for (i = 0; i < 4; i++) {  for (j = 0; j < 4; j++) {  if (j == i)  continue;  if ((gpio_get_value(gpio_pin[j]) & 0x01) == 0)  return key_num;  key_num++;  }  }
err_direction_set:  return -1;
}

31.3 Linux Input按键驱动实现

Linux驱动编程包含两个部分,第一个是对硬件设备初始化;第二个是根据Linux驱动框架填充驱动代码。

对于Linux 3.x有引入设备树及gpiolib的内核,一般会在编译内核的时候已经配置好处理器的所有IO,并使用GPIOLib对所有GPIO进行统一管理,因此如需操作某个GPIO那么可以直接调用内核gpiolib库提供的操作函数库,申请对某IO的控制权。引入GPIOLib的目的是避免多个驱动控制一个IO所带来的混乱,gpiolib需要在编译内核的时候选上支持gpiolib。

输入子系统由驱动层、输入子系统核心、事件处理层三部分组成。一个输入事件,如鼠标移动、键盘按下等通过Driver->Inputcore->Event handler->userspace的顺序到达用户控件的应用程序。

struct input_dev 结构数据说明:

struct input_dev {

const char *name

设备名

char *phys;

设备文件节点名

char *uniq

全球唯一的ID号

struct input_id id

设备id;用于匹配事件处理层handler

unsigned long evbit

表示设备支持的事件类型

unsigned long keybit

表示支持的按键类型

unsigned long relbit

支持相对坐标的值

unsigned long absbit

支持绝对坐标的值

………

相关函数说明

struct input_dev *input_allocate_device

动态分配一个struct input_dev 结构;返回一个指针

input_register_device(*dev)

注册input设备;dev为inputDev指针

input_unregister_device(*dev)

注销input设备;dev为inputDev指针

set_bit(event,bit)

设置input子系统支持哪些事件;参数为事件类型,设置位。如设置支持按键:set_bit(EV_KEY,input_dev->evbit);

input_report_key(*dev, code, value)

上报按键事件及值

input_report_rel(*dev, code, value)

上报相对值

input_report_abs (*dev, code, value)

上报绝对值

input_sync(*dev)

同步上报事件

31.4 Linux input子系统驱动示例

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/time.h>
#include <linux/slab.h> //kmalloc头文件
#include <linux/input.h>
#include <linux/sys_config.h>
#include <linux/gpio.h>
#include <asm/io.h>struct input_dev *inputKeyDev;     /*input设备指针*///上报示例处理
static int inputDev_IRQHandler()
{input_report_key(inputKeyDev,KEY_1, !gpio_get_value(KEY_1));input_sync(inputKeyDev);       return 0;
}static int __init inputDev_Init(void)
{int  nRet = -1;/*分配一个input结构*/inputKeyDev = input_allocate_device();if (!inputKeyDev) {nRet = -ENOMEM;goto iExit0;}/*设置支持事件类型*/set_bit(EV_SYN,inputKeyDev->evbit);set_bit(EV_KEY,inputKeyDev->evbit);set_bit(KEY_1,inputKeyDev->keybit);/*注册到输入子系统中*/nRet = input_register_device(inputKeyDev);if(nRet)goto iExit0;return nRet;
iExit0: if(inputKeyDev!=NULL)  input_free_device(inputKeyDev);return nRet;
}static void __exit inputDev_Exit(void)
{/*注销输入子系统设备*/input_unregister_device(inputKeyDev);/*释放申请的内存*/input_free_device(inputKeyDev);
}module_init(inputDev_Init);
module_exit(inputDev_Exit);
MODULE_LICENSE("GPL");

31.5 Linux 16按键驱动实例

/*****************************************************************
* 包含头文件
******************************************************************/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/time.h>
#include <linux/slab.h> //kmalloc头文件
#include <linux/input.h>
#include <linux/sys_config.h>
#include <linux/gpio.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/hrtimer.h>
#include <linux/sched.h>/*****************************************************************
* 宏定义(仅在当前C文件使用的宏定义写在当前C文件中,否则需写在H文件中)
******************************************************************/
#define IKEY_PIN_1 (GPIOD(16))
#define IKEY_PIN_2 (GPIOD(17))
#define IKEY_PIN_3 (GPIOD(19))
#define IKEY_PIN_4 (GPIOD(22))#define INPUT_IKEY_NAME "iKey"
#define IKEY_TIMEOUT_MS (80)            /*80ms扫描一次按键*/
/*****************************************************************
* 结构定义(仅在当前C文件使用的结构体写在当前C文件中,否则需写在H文件中)
******************************************************************/
struct _iKeyInfoSt{int iKeyNum;                     /*保存按键值*/   int iKeyUpFlag;                     /*是否上报*/int nQueWaiting;                    /*等待队列条件*/wait_queue_head_t iwaitQue;           /*等待队列*/struct hrtimer ihrtimer;            /*高精度定时器*/struct timer_list iKeyTimer;      /*轮询定制器,每隔50ms轮询一次键盘*/   struct work_struct iKeyDectWork;    /*键盘检测工作队列*/struct delayed_work iKeyDelayWork;/*键盘检测工作队列*/struct input_dev *inputKeyDev;        /*input设备指针*/
};/*****************************************************************
* 全局变量定义
******************************************************************/
struct _iKeyInfoSt *piKeyInfoSt = NULL;static unsigned int igpio_pin[] = {IKEY_PIN_1, IKEY_PIN_2, IKEY_PIN_3, IKEY_PIN_4,
};/*键盘对应的上报的键值*/
static u32 iKeycodes[16] = {KEY_DOWN,KEY_F1,KEY_HOME,KEY_UP,KEY_8,KEY_0,KEY_1,KEY_5,KEY_BACKSPACE,KEY_2,KEY_6,KEY_9,KEY_3,KEY_4,KEY_7,KEY_KPASTERISK
};
/*****************************************************************
* 静态变量定义
******************************************************************//*****************************************************************
* 外部变量声明(如果全局变量没有在其它的H文件声明,引用时需在此处声明,
*如果已在其它H文件声明,则只需包含此H文件即可)
******************************************************************//*****************************************************************
* 函数原型声明
******************************************************************/
static enum hrtimer_restart iKey_hrtimerHander(struct hrtimer *timer)
{piKeyInfoSt->nQueWaiting = 1;wake_up(&piKeyInfoSt->iwaitQue);return HRTIMER_NORESTART;
}static int iKey_gpioSetDirection(unsigned int ndir)
{unsigned int i = 0;   for (i = 0; i < 4; i++) {if (ndir & BIT(i)) {if (gpio_direction_output(igpio_pin[i], 0)) {return -1;}} else {if (gpio_direction_input(igpio_pin[i])) {return -1;}}}/*启动高精度定时器,结合等待队列延时1ms*/    hrtimer_start(&piKeyInfoSt->ihrtimer,ktime_set(0,1500*1000),HRTIMER_MODE_REL);wait_event(piKeyInfoSt->iwaitQue,piKeyInfoSt->nQueWaiting);piKeyInfoSt->nQueWaiting = 0;return 0;
}static int iKey_readValue(void)
{int key_num = 0;int i, j;if (iKey_gpioSetDirection(0x00) == -1)goto iExit;for (j = 0; j < 4; j++){if ((gpio_get_value(igpio_pin[j]) & 0x01) == 0)return key_num;key_num++;}for (i = 0; i < 4; i++) {if (iKey_gpioSetDirection(BIT(i)) == -1)goto iExit;for (j = 0; j < 4; j++){if (j == i)continue;if ((gpio_get_value(igpio_pin[j]) & 0x01) == 0)return key_num;key_num++;}}iExit:return -1;
}static void iKey_delayWorkCallBack(struct work_struct *data)
{int key_num = 0;key_num = iKey_readValue();if (key_num == piKeyInfoSt->iKeyNum){input_report_key(piKeyInfoSt->inputKeyDev, iKeycodes[piKeyInfoSt->iKeyNum], 1);input_sync(piKeyInfoSt->inputKeyDev);piKeyInfoSt->iKeyUpFlag = 1;}
}static void iKey_dectWorkCallBack(struct work_struct *data)
{int key_num = 0;/*扫描读取按键值*/key_num = iKey_readValue();if (key_num != -1) {if(piKeyInfoSt->iKeyUpFlag!=1) piKeyInfoSt->iKeyNum = key_num;/*延时20ms消抖*/schedule_delayed_work(&piKeyInfoSt->iKeyDelayWork, msecs_to_jiffies(20));}else if(piKeyInfoSt->iKeyUpFlag == 1){input_report_key(piKeyInfoSt->inputKeyDev, iKeycodes[piKeyInfoSt->iKeyNum], 0);input_sync(piKeyInfoSt->inputKeyDev);       piKeyInfoSt->iKeyUpFlag = 0;}mod_timer(&piKeyInfoSt->iKeyTimer, jiffies + msecs_to_jiffies(IKEY_TIMEOUT_MS));
}void iKey_timerCallBack(unsigned long arg)
{schedule_work(&piKeyInfoSt->iKeyDectWork);
}static int iKey_gpioInit()
{int i = 0;char chlabel[16] ={0};for (i = 0; i < 4; i++){if (gpio_is_valid(igpio_pin[i])){memset(chlabel, 0, 16);sprintf(chlabel, "igpio_%d", i);if (gpio_request(igpio_pin[i], chlabel)){pr_err("gpio_request failed [%d]\n", igpio_pin[i]);goto iExit;}}else {pr_err("wrong gpio num [%d]\n", igpio_pin[i]);goto iExit;}}return 0;
iExit:pr_err("gpio init failed\n");for (; i > 0; i--) {gpio_free(igpio_pin[i - 1]);}return -1;
}static void iKey_gpioExit(void)
{unsigned int i = 0;for (i = 0; i < 4; i++){gpio_free(igpio_pin[i]);}
}static int __init iKey_Init(void)
{int  ni = 0;int  nRet = -1;/*分配内存保存结构*/piKeyInfoSt = kzalloc(sizeof(struct _iKeyInfoSt), GFP_KERNEL);if (!piKeyInfoSt){nRet = -ENOMEM;goto iExit0;}/*分配一个input结构*/piKeyInfoSt->inputKeyDev = input_allocate_device();if (!piKeyInfoSt->inputKeyDev) {nRet = -ENOMEM;goto iExit0;}/*填充对应结构的数据*/piKeyInfoSt->inputKeyDev->name         = INPUT_IKEY_NAME;piKeyInfoSt->inputKeyDev->phys         = "iKeyPhys";piKeyInfoSt->inputKeyDev->id.bustype  = BUS_HOST;piKeyInfoSt->inputKeyDev->id.vendor       = 0x0001;piKeyInfoSt->inputKeyDev->id.product    = 0x0001;piKeyInfoSt->inputKeyDev->id.version    = 0x0100;/*设置支持事件类型*/set_bit(EV_SYN,piKeyInfoSt->inputKeyDev->evbit);set_bit(EV_KEY,piKeyInfoSt->inputKeyDev->evbit);/*设置支持哪些按键*/for (ni = 0; ni < 16; ni++)set_bit(iKeycodes[ni], piKeyInfoSt->inputKeyDev->keybit);/*注册到输入子系统中*/nRet = input_register_device(piKeyInfoSt->inputKeyDev);if(nRet)goto iExit0;/*使用高精度定时器+等待队列进行延时*/piKeyInfoSt->iKeyUpFlag  = 0;piKeyInfoSt->nQueWaiting = 0;init_waitqueue_head(&piKeyInfoSt->iwaitQue);hrtimer_init(&piKeyInfoSt->ihrtimer,CLOCK_MONOTONIC,HRTIMER_MODE_REL);  piKeyInfoSt->ihrtimer.function = iKey_hrtimerHander;    /* 设置回调函数 */  /*初始化工作队列*/INIT_WORK(&piKeyInfoSt->iKeyDectWork, iKey_dectWorkCallBack);INIT_DELAYED_WORK(&piKeyInfoSt->iKeyDelayWork, iKey_delayWorkCallBack);/*Linux内核定时器初始化*/init_timer(&piKeyInfoSt->iKeyTimer);piKeyInfoSt->iKeyTimer.function= iKey_timerCallBack;piKeyInfoSt->iKeyTimer.expires = jiffies + msecs_to_jiffies(IKEY_TIMEOUT_MS);add_timer(&piKeyInfoSt->iKeyTimer);/*初始化GPIO*/nRet = iKey_gpioInit();if(nRet<0)goto iExit0;return 0;
iExit0: if(piKeyInfoSt->inputKeyDev!=NULL)  input_free_device(piKeyInfoSt->inputKeyDev);if(piKeyInfoSt!=NULL) kfree(piKeyInfoSt);return nRet;
}static void __exit iKey_Exit(void)
{/*注销输入子系统设备*/input_unregister_device(piKeyInfoSt->inputKeyDev);/*删除定时器*/del_timer(&piKeyInfoSt->iKeyTimer);  /*释放已申请的GPIO*/iKey_gpioExit();/*删除工作队列*/cancel_work_sync(&piKeyInfoSt->iKeyDectWork);cancel_delayed_work(&piKeyInfoSt->iKeyDelayWork);/*删除高精度定时器*/hrtimer_cancel(&piKeyInfoSt->ihrtimer);/*释放申请的内存*/if(piKeyInfoSt->inputKeyDev!=NULL)  input_free_device(piKeyInfoSt->inputKeyDev);if(piKeyInfoSt!=NULL) kfree(piKeyInfoSt);
}module_init(iKey_Init);
module_exit(iKey_Exit);
MODULE_LICENSE("GPL");

31 Linux input子系统按键驱动--4IO驱动16按键相关推荐

  1. platform框架--Linux MISC杂项框架--Linux INPUT子系统框架--串行集成电路总线I2C设备驱动框架--串行外设接口SPI 设备驱动框架---通用异步收发器UART驱动框架

    platform框架 input. pinctrl. gpio 子系统都是 Linux 内核针对某一类设备而创建的框架, input子系统是管理输入的子系统 pinctrl 子系统重点是设置 PIN( ...

  2. linux 驱动 (2)---Linux input子系统最清晰、详尽的分析

    Linux input子系统最清晰.详尽的分析 Linux input分析之二:解构input_handler.input_core.input_device 输入输出是用户和产品交互的手段,因此输入 ...

  3. 三个子系统_「正点原子Linux连载」第五十八章Linux INPUT子系统实验(一)

    1)实验平台:正点原子Linux开发板 2)摘自<正点原子I.MX6U嵌入式Linux驱动开发指南> 关注官方微信号公众号,获取更多资料:正点原子 第五十八章Linux INPUT子系统实 ...

  4. Linux input子系统 io控制字段【转】

    转自:http://www.cnblogs.com/leaven/archive/2011/02/12/1952793.html http://blog.csdn.net/guoshaobei/arc ...

  5. linux input子系统分析--概述与数据结构

    linux input子系统分析--概述与数据结构 Input子系统处理输入事务,任何输入设备的驱动程序都可以通过Input输入子系统提供的接口注册到内核,利用子系统提供的功能来与用户空间交互.输入设 ...

  6. Linux input子系统上报键值失败问题

    昨天在做有关Linux input子系统实验的时候,被一个问题困扰了很久,到第二天才发现原因,最后的问题是一个小细节导致实验的失败. 当时的实验代码如下: static int key_probe(s ...

  7. Linux input 子系统详解

    1. 模块概述 1.1.相关资料和代码研究 drivers/input/ include/uapi/linux/input-event-codes.h 2. 模块功能 linux核心的输入框架 3. ...

  8. Linux input子系统分析之一:软件层次

    输入输出是用户和产品交互的手段,因此输入驱动开发在Linux驱动开发中很常见.同时,input子系统的分层架构思想在Linux驱动设计中极具代表性和先进性,因此对Linux input子系统进行深入分 ...

  9. linux input子系统 pdf,Linux下的Input子系统.pdf

    计 算 机 系 统 应 用 ||ww.c·S-a.org.cn 2013年 第 22卷 第 l2期 Linux下的Input子系统① 朱银瑞,吴庆洪,吴华玲 (辽宁科技大学 电子与信息工程学院,鞍山) ...

最新文章

  1. python3语法错误-python-使用Python 3打印时出现语法错误
  2. IO流操作-图片操作(二)
  3. 量产 php是什么,php文件怎么打开?下错的?
  4. php 以-截取剩余的字符串_10分钟从PHP到Python
  5. HTML5 多图片上传(前端+后台详解)
  6. 计算机科学与技术范文,计算机科学与技术专业(范文).doc
  7. 转:卷积神经网络_(1)卷积层和池化层学习
  8. linux用命令行运行matlab的.mat文件
  9. Python字符串isalpha()
  10. TFS集群间数据迁移任务总结
  11. Rearchitect Your Web Applications for Microsoft ASP.NET 2.0
  12. html木马制作教程,利用Internet Explorer Object Data漏洞制做全新网页木马
  13. 服务器文件mdf,升级 .mdf 文件 - Visual Studio (Windows) | Microsoft Docs
  14. 计算机系统维护要不要自动更新,电脑自动更新功能开启还是关闭,到底要不要关闭...
  15. 35岁技术人如何转型做管理?mysql删除数据语句
  16. C++常函数的用法详解
  17. 解密微信小程序用户敏感数据获取用户信息
  18. 漫谈程序员系列:软件开发的十八般乐趣
  19. windows系统克隆虚拟机
  20. 开心,终于突破 1000 啦

热门文章

  1. python线程延时函数_详解Python 多线程 Timer定时器/延迟执行、Event事件
  2. 网络存储光盘镜像服务器术语解释
  3. 使用Jpype1的坑
  4. c语言中反斜线的作用,C语言中反斜杠的使用
  5. 运行jar包的通用shell脚本
  6. mysql 手动 建库_手动建库流程
  7. win7网络中能看到计算机但无法连接,将win7系统计算机连接到无线网络但无法访问Internet的解决方案...
  8. 你以为技术圈只有女汉子 我们用颜值和才艺征服你
  9. python-pyecharts绘图-坐标轴标签格式化formatter+刻度调整+批量输出为pdf
  10. 上海交通大学2017年复试题:FibonacciRepresentation