13.Linux键盘按键驱动 (详解)
在上一节分析输入子系统内的intput_handler软件处理部分后,接下来我们开始写input_dev驱动
本节目标:
实现键盘驱动,让开发板的4个按键代表键盘中的L、S、空格键、回车键
1.先来介绍以下几个结构体使用和函数,下面代码中会用到
1)input_dev驱动设备结构体中常用成员如下:
struct input_dev {
void *private;
const char *name; //设备名字
const char *phys; //文件路径,比如 input/buttons
const char *uniq;
struct input_id id;
unsigned long evbit[NBITS(EV_MAX)]; //表示支持哪类事件,常用有以下几种事件(可以多选)
//EV_SYN 同步事件,当使用input_event()函数后,就要使用这个上报个同步事件
//EV_KEY 键盘事件
//EV_REL (relative)相对坐标事件,比如鼠标
//EV_ABS (absolute)绝对坐标事件,比如摇杆、触摸屏感应
//EV_MSC 其他事件,功能
//EV_LED LED灯事件
//EV_SND (sound)声音事件
//EV_REP 重复键盘按键事件
//(内部会定义一个定时器,若有键盘按键事件一直按下/松开,就重复定时,时间一到就上报事件)
//EV_FF 受力事件
//EV_PWR 电源事件
//EV_FF_STATUS 受力状态事件
unsigned long keybit[NBITS(KEY_MAX)]; //存放支持的键盘按键值
//键盘变量定义在:include/linux/input.h, 比如: KEY_L(按键L)
unsigned long relbit[NBITS(REL_MAX)]; //存放支持的相对坐标值
unsigned long absbit[NBITS(ABS_MAX)]; //存放支持的绝对坐标值
unsigned long mscbit[NBITS(MSC_MAX)]; //存放支持的其它事件,也就是功能
unsigned long ledbit[NBITS(LED_MAX)]; //存放支持的各种状态LED
unsigned long sndbit[NBITS(SND_MAX)]; //存放支持的各种声音
unsigned long ffbit[NBITS(FF_MAX)]; //存放支持的受力设备
unsigned long swbit[NBITS(SW_MAX)]; //存放支持的开关功能
... ...
2)函数如下:
struct input_dev *input_allocate_device(void); //向内核中申请一个input_dev设备,然后返回这个设备 input_unregister_device(struct input_dev *dev); //卸载/sys/class/input目录下的input_dev这个类设备, 一般在驱动出口函数写 input_free_device(struct input_dev *dev); //释放input_dev这个结构体, 一般在驱动出口函数写 set_bit(nr,p); //设置某个结构体成员p里面的某位等于nr,支持这个功能
/* 比如:
set_bit(EV_KEY,buttons_dev->evbit); //设置input_dev结构体buttons_dev->evbit支持EV_KEY
set_bit(KEY_S,buttons_dev->keybit); //设置input_dev结构体buttons_dev->keybit支持按键”S”
*/ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value); //上报事件
// input_dev *dev :要上报哪个input_dev驱动设备的事件
// type : 要上报哪类事件, 比如按键事件,则填入: EV_KEY
// code: 对应的事件里支持的哪个变量,比如按下按键L则填入: KEY_L
//value:对应的变量里的数值,比如松开按键则填入1,松开按键则填入0
input_sync(struct input_dev *dev); //同步事件通知
为什么使用了input_event()上报事件函数,就要使用这个函数?
因为input_event()函数只是个事件函数,所以需要这个input_sync()同步事件函数来通知系统,然后系统才会知道
input_sync()代码如下:
static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, ); //就是上报同步事件,告诉内核:input_event()事件执行完毕
}
2.然后开始写代码
1)向内核申请input_dev结构体
2)设置input_dev的成员
3)注册input_dev 驱动设备
4)初始化定时器和中断
5)写中断服务函数
6)写定时器超时函数
7)在出口函数中 释放中断函数,删除定时器,卸载释放驱动
具体代码如下(都加了注释):
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/gpio_keys.h>
#include <asm/gpio.h> struct input_dev *buttons_dev; // 定义一个input_dev结构体
static struct ping_desc *buttons_id; //保存dev_id,在定时器中用
static struct timer_list buttons_timer; //定时器结构体 struct ping_desc{ unsigned char *name; //中断设备名称
int pin_irq; //按键的外部中断标志位
unsigned int pin; //引脚
unsigned int irq_ctl; //触发中断状态: IRQ_TYPE_EDGE_BOTH
unsigned int button; //dev_id,对应键盘的 L , S, 空格, enter
}; // KEY1 -> L
// KEY2 -> S
// KEY3 -> 空格
// KEY4 -> enter
static struct ping_desc buttons_desc[]=
{
{"s1", IRQ_EINT0, S3C2410_GPF0, IRQ_TYPE_EDGE_BOTH,KEY_L},
{"s2", IRQ_EINT2, S3C2410_GPF2, IRQ_TYPE_EDGE_BOTH,KEY_S},
{"s3", IRQ_EINT11, S3C2410_GPG3 , IRQ_TYPE_EDGE_BOTH,KEY_SPACE},
{"s4", IRQ_EINT19, S3C2410_GPG11,IRQ_TYPE_EDGE_BOTH,KEY_ENTER},
}; /*5. 写中断服务函数*/
static irqreturn_t buttons_irq (int irq, void *dev_id) //中断服务函数
{
buttons_id=(struct ping_desc *)dev_id; //保存当前的dev_id
mod_timer(&buttons_timer, jiffies+HZ/ ); //更新定时器值 10ms
return ;
} /*6.写定时器超时函数*/
void buttons_timer_function(unsigned long i)
{
int val;
val=s3c2410_gpio_getpin(buttons_id->pin); //获取是什么电平
if(val) //高电平,松开
{
/*上报事件*/
input_event(buttons_dev,EV_KEY,buttons_id->button, ); //上报EV_KEY类型,button按键,0(没按下)
input_sync(buttons_dev); // 上传同步事件,告诉系统有事件出现
} else //低电平,按下
{
/*上报事件*/
input_event(buttons_dev, EV_KEY, buttons_id->button, ); //上报EV_KEY类型,button按键,1(按下)
input_sync(buttons_dev); // 上传同步事件,告诉系统有事件出现
}
} static int buttons_init(void) //入口函数
{
int i;
buttons_dev=input_allocate_device(); //1.向内核 申请input_dev结构体
/*2.设置input_dev , */
set_bit(EV_KEY,buttons_dev->evbit); //支持键盘事件
set_bit(EV_REP,buttons_dev->evbit); //支持键盘重复按事件 set_bit(KEY_L,buttons_dev->keybit); //支持按键 L
set_bit(KEY_S,buttons_dev->keybit); //支持按键 S
set_bit(KEY_SPACE,buttons_dev->keybit); //支持按键 空格
set_bit(KEY_ENTER,buttons_dev->keybit); //支持按键 enter /*3.注册input_dev */
input_register_device(buttons_dev); /*4. 初始化硬件:初始化定时器和中断*/
// KEY1 -> L
// KEY2 -> S
// KEY3 -> 空格
// KEY4 -> enter
init_timer(&buttons_timer);
buttons_timer.function=buttons_timer_function;
add_timer(&buttons_timer); for(i=;i<;i++)
request_irq(buttons_desc[i].pin_irq, buttons_irq, buttons_desc[i].irq_ctl, buttons_desc[i].name, &buttons_desc[i]); return ;
} static int buttons_exit(void) //出口函数
{
/*7.释放中断函数,删除定时器,卸载释放驱动*/
int i;
for(i=;i<;i++)
free_irq(buttons_desc[i].pin_irq,&buttons_desc[i]); //释放中断函数 del_timer(&buttons_timer); //删除定时器 input_unregister_device(buttons_dev); //卸载类下的驱动设备
input_free_device(buttons_dev); //释放驱动结构体
return ;
} module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL v2");
3.测试运行:
挂载键盘驱动后, 如下图,可以通过 ls -l /dev/event* 命令查看已挂载的设备节点:

在上一节输入子系统里分析到:输入子系统的主设备号为13,其中event驱动本身的此设备号是从64开始的,如上图,内核启动时,会加载自带触摸屏驱动,所以我们的键盘驱动的次设备号=64+1
3.1测试运行有两种,一种是直接打开/dev/tyy1,第二种是使用exec命令
(exec命令详解入口地址: http://www.cnblogs.com/lifexy/p/7553228.html)
方法1:
cat /dev/tty1 //tty1:LCD终端,就会通过tty_io.c来访问键盘驱动,然后打印在tty1终端上
方法2:
exec 0</dev/tty1 //将/dev/tty1挂载到-sh进程描述符0下,此时的键盘驱动就会直接打印在tty1终端上
3.2 调试:
若测试不成功,板子又在QT下进行的:
1)可以使用vi命令,在记事本中按按键试
2)或者删除/etc/init.d/rcS 里面有关QT自启动的命令,然后重启
若板子没在QT下进行,也无法测试成功:
1)可以使用hexdump命令来调试代码
(hexdump命令调试代码详解地址:http://www.cnblogs.com/lifexy/p/7553550.html)
接下来开始学习:
14.linux-platform机制实现驱动层分离(详解)
13.Linux键盘按键驱动 (详解)的更多相关文章
- Linux块设备驱动详解
<机械硬盘> a:磁盘结构 -----传统的机械硬盘一般为3.5英寸硬盘,并由多个圆形蝶片组成,每个蝶片拥有独立的机械臂和磁头,每个堞片的圆形平面被划分了不同的同心圆,每一个同心圆称为一个 ...
- 很好的linux下GPIO驱动详解文章
原文地址 http://blog.csdn.net/llxmedici/article/details/6282372 打算跟着友善之臂的<mini2440 linux移植开发指南>来做 ...
- 【转】草根老师的 linux字符设备驱动详解
Linux 驱动 之 模块化编程 Linux 驱动之模块参数和符号导出 Linux 设备驱动之字符设备(一) Linux 设备驱动之字符设备(二) Linux 设备驱动之字符设备(三)
- linux usb 驱动详解
linux usb 驱动详解 USB 设备驱动代码通过urb和所有的 USB 设备通讯.urb用 struct urb 结构描述(include/linux/usb.h ). urb 以一种异步的方式 ...
- Linux下top命令详解
Linux下top命令详解 top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器.top是一个动态显示过程,即可以通过用户按键来不断刷 ...
- 【转帖】Linux定时任务Crontab命令详解
Linux定时任务Crontab命令详解 https://www.cnblogs.com/intval/p/5763929.html 知道有crontab 以及 at 命令 改天仔细学习一下 讲sys ...
- 16.Linux-LCD驱动(详解)
在上一节LCD层次分析中,得出写个LCD驱动入口函数,需要以下4步: 1) 分配一个fb_info结构体: framebuffer_alloc(); 2) 设置fb_info 3) 设置硬件相关的操作 ...
- Linux定时任务Crontab命令详解_转
转自:Linux定时任务Crontab命令详解 (部分修改) linux 定时系统则是由 cron (crond) 这个系统服务来控制的.Linux 系统上面原本就有非常多的计划性工作,因此这个系统服 ...
- 【转】linux中inittab文件详解
原文网址:http://www.2cto.com/os/201108/98426.html linux中inittab文件详解 init的进程号是1(ps -aux | less),从这一点就能看出, ...
随机推荐
- Unity3D之物理射线
射线:从一个点往一个方向,发射一根无限长的射线,这根射线会与场景中带有 Collider 组件的物体发生碰撞. 射线的使用: public class Test { private void Star ...
- Reading | 《Linux就该这么学》
目录 一.前言 1.Linux概念 2.RPM(红帽软件包管理器) 3.Yum软件仓库 二.常用Linux命令 1.Shell 2.命令基本格式和man命令 3.常用系统工作命令 echo命令 dat ...
- freeRTOSConfig.h文件对FreeRTOS进行系统配置
FreeRTOS内核是高度可定制的,使用配置文件FreeRTOSConfig.h进行定制.每个FreeRTOS应用都必须包含这个头文件,用户根据实际应用来裁剪定制FreeRTOS内核.这个配置文件是针 ...
- css3简单旋转
<!DOCTYPE html><html><head> <meta charset="utf-8"> <title>&l ...
- 深度学习框架caffe/CNTK/Tensorflow/Theano/Torch的对比
在单GPU下,所有这些工具集都调用cuDNN,因此只要外层的计算或者内存分配差异不大其性能表现都差不多. Caffe: 1)主流工业级深度学习工具,具有出色的卷积神经网络实现.在计算机视觉领域Caff ...
- SqlSessionFactoryBean的构建流程
目的 此文的主旨在于梳理SqlSessionFactoryBean的初始流程,不拘泥于实现细节. 使用 SqlSessionFactoryBean的主要作用便是用来创建SqlSessionFactor ...
- Spring 中初始化一个Bean对象时依赖其他Bean对象空指针异常
1. Bean依赖关系 一个配置类的Bean,一个实例Bean: 实例Bean初始化时需要依赖配置类的Bean: 1.1 配置类Bean @ConfigurationProperties(prefix ...
- 机器学习技法笔记:01 Linear Support Vector Machine
Roadmap Course Introduction Large-Margin Separating Hyperplane Standard Large-Margin Problem Support ...
- Python函数——装饰器
前言 给下面的函数加上运行时间 def fib(n): a, b = 0, 1 for i in range(n): print(b) a, b = b, a+b return b a = fib(5 ...
- PHP使用Zend Opcache之优化加速和缓存清理总结
简介 字节码缓存不是php的新特性,有很多独立性的扩展可以实现缓存,比如PHP Cache(APC),eAccelerator,ionCube和XCache等等.但是到目前为止,这些独立的扩展并没有集 ...