41 linux标准输入设备之矩阵键盘驱动的实现
来源:互联网 发布:思念诗词 知乎 编辑:程序博客网 时间:2024/06/10 18:59
准备知识点:
原子位操作 , linux输入设备的应用程序编程
在linux内核里用struct input_dev的一个对象来表示一个输入设备. 用一位二进制表示是否支持相应的功能, 多种功能需要多位, 用数组来表示所需的多位二进制数.
#include <linux/input.h>struct input_dev { //这部分内容可"cat /proc/bus/input/device"来查看 const char *name; //输入设备名 const char *phys; const char *uniq; struct input_id id; //设备id, 可指定厂家号,产品型号 ... unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //表示此输入设备所支持的事件类型. 支持的事件在相应的位设1. unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //表示此输入设备所支持的键码。键码共有768个,需要768位来表示,支持的键码需在相应位设1. 查KEY_* unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; //表示支持的相对坐标的数据类型。如x, y, z轴的数据. 查 REL_* unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];//表示支持的绝对坐标的数据类型. 如x, y坐标. 查ABS_* ... unsigned int repeat_key; struct timer_list timer; //此定时是用于重复提交键数据使用. 我们只需设置事件EV_REP就可以了, 内核里会设置此定时器 ...}
事件类型有:
//注意宏的值表示在第几位设1就是支持此事件#define EV_SYN 0x00 //同步,表示设备驱动的数据已更新. 所有的输入设备都需要此功能。所以这功能会在输入设备注册时自动在evbit[0]的第0位设1#define EV_KEY 0x01 //表示此输入设备有按键功能#define EV_REL 0x02 //相对坐标, 鼠标#define EV_ABS 0x03 //绝对坐标, 触摸屏#define EV_MSC 0x04 ...#define EV_REP 0x14 //支持按键重复提交。也就支持按着键不动时,自动按间隔时间提交按键数据. ...#define EV_CNT (EV_MAX+1) //事件总数,也就是共需多少位. BITS_TO_LONGS把位数转成需要多少个long数组的元素个数.
/////////////////////////////////////////////
在linux内核源码文档目录里的”input-programming.txt”提供一个最简单的按键的输入设备驱动范例.
总结步骤:
(1) struct input_dev *dev; dev = input_allocate_device(); //动态分配空间,并部分成员初始化. (2) //初始化成员 dev->name = "mykeys"; dev->evbit[0] = BIT_MASK(EV_KEY); //设置支持的事件类型, BIT_MASK(EV_KEY)就是表示在第EV_KEY位设1. dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); //设置支持的键码 (3) //注册输入设备 input_register_device(dev); (4) //请求按键对应的IO口中断 request_irq(gpio_to_irq(KEY_IO), button_interrupt, 双边沿触发, "button", NULL)) (5) //在按键的中断处理函数里,提交键码数据 input_report_key(dev, BTN_0, !gpio_get_value(KEY_IO)); //注意最后的一个参数值非0时表示键按下, 0时表示松手 input_sync(dev); //提交数据
//////////////////////////////////////////////////////////////////////////////////////
把前面的矩阵键盘的驱动改造成标准的输入设备驱动:
#include <linux/init.h>#include <linux/module.h>#include <linux/interrupt.h>#include <mach/gpio.h>#include <linux/gpio.h>#include <linux/timer.h>#include <linux/input.h>int rows[] = {GPIOA(7), GPIOA(8), GPIOA(9), GPIOA(10)};int cols[] = {GPIOA(20), GPIOA(21), GPIOC(4), GPIOC(7)};struct timer_list mytimer;//用一个数组记录哪个按键按下,当松手中断发生时,就用此数组里记录的按键作松手的按键char keys[4][4];int key_codes[4][4] = { {KEY_L, KEY_S, KEY_SPACE, KEY_ENTER}, {KEY_A, KEY_UP, KEY_B, KEY_ESC}, {KEY_LEFT, KEY_DOWN, KEY_RIGHT, KEY_DOT}, {KEY_C, KEY_D, KEY_E, KEY_F},}; //按键对应的键码表struct input_dev *dev;#define NAME "mykeypad"void all_cols_output_level(int level){ int i; for (i = 0; i < ARRAY_SIZE(cols); i++) gpio_set_value(cols[i], level);}void timer_func(unsigned long data){ int r = data; enable_irq(gpio_to_irq(rows[r]));}irqreturn_t irq_func(int irqno, void *arg){ int r = (int )arg; //r表示哪一行,由request_irq时指定 int i; int stat = gpio_get_value(rows[r]); disable_irq_nosync(irqno);/////检查列//// if (stat) //行线为高电平, 则是有按键松手 { for (i = 0; i < ARRAY_SIZE(cols); i++) { if (keys[r][i]) //根据数据里存放的记录来判断是哪个按键松手 {// printk("key[%d][%d] released\n", r, i); input_report_key(dev, key_codes[r][i], 0); //松手的键码 keys[r][i] = 0; break; } } } else //行线为低电平,则是有按键按下 { for (i = 0; i < ARRAY_SIZE(cols); i++) { all_cols_output_level(1); //所有的列输出高电平 gpio_set_value(cols[i], 0); //逐一改变一列线的电平 if (!(gpio_get_value(rows[r])) && !(gpio_get_value(rows[r]))) //判断行线电平是否随着列线改变, 如改变则是按下 { keys[r][i] = 1;// printk("key[%d][%d] pressed\n", r, i); input_report_key(dev, key_codes[r][i], 1); //按下的键码 break; } } all_cols_output_level(0); //所有的列输出低电平 } input_sync(dev); //提交键码数据////////////// mytimer.data = r; mod_timer(&mytimer, jiffies+HZ*50/1000); // 50ms后重新打开中断 return IRQ_HANDLED;}static int __init test_init(void){ int ret, i, j, k; init_timer(&mytimer); mytimer.function = timer_func; //输入设备的初始化//////// dev = input_allocate_device(); dev->name = NAME; set_bit(EV_KEY, dev->evbit); //设置支持的事件 set_bit(EV_REP, dev->evbit); //支持重复提交按键数据。即按下不动时,会自动定时重复提交按下的键码 for (i = 0; i < ARRAY_SIZE(key_codes); i++) { for (j = 0; j < ARRAY_SIZE(key_codes[0]); j++) set_bit(key_codes[i][j], dev->keybit); //设置支持的键码 } input_register_device(dev); //注册输入设备 //////////////////////// //申请行线的中断 for (j = 0, k = 0; j < ARRAY_SIZE(rows); k++, j++) { ret = gpio_request(rows[j], NAME); if (ret < 0) goto err1; ret = request_irq(gpio_to_irq(rows[k]), irq_func, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, NAME, (void *)k); if (ret < 0) goto err1; } //把列全部输出低电平 for (i = 0; i < ARRAY_SIZE(cols); i++) { ret = gpio_request(cols[i], NAME); if (ret < 0) goto err0; gpio_direction_output(cols[i], 0); } return 0;err1: while (k-- > 0) free_irq(gpio_to_irq(rows[k]), (void *)k); while (j > 0) gpio_free(rows[--j]);err0: while (i > 0) gpio_free(cols[--i]); return ret;}static void __exit test_exit(void){ int i, j; del_timer(&mytimer); for (i = 0; i < ARRAY_SIZE(cols); i++) gpio_free(cols[i]); for (j = 0; j < ARRAY_SIZE(rows); j++) { gpio_free(rows[j]); free_irq(gpio_to_irq(rows[j]), (void *)j); } input_unregister_device(dev);}module_init(test_init);module_exit(test_exit);MODULE_LICENSE("GPL");
在linux内核在输入设备驱动注册后,会自动生成设备驱动对应的设备文件,并提供了字符设备驱动接口供用户进程调用(输入设备驱动里无需再实现字符设备驱动). 可用”cat /proc/bus/input/devices”查出输入设备驱动对应的设备文件.
找出设备文件后,就可以用linux输入设备的应用程序编程的方法来获取键码数据。也可以移植qt应用程序,在qt程序里直接用keyPressEvent来捕捉键值.
也可以把输入设备作shell的标准输入来测试, shell命令"exec 0</dev/tty1"(所有输入设备驱动提交的键码数据都会有一份提供到tty1对应的驱动里,所以可以从tty1里获取到输入设备驱动的键码). 执行命令后, 按下矩阵键盘的键,终端就可以出现相应的键值
阅读全文
0 0
- 41 linux标准输入设备之矩阵键盘驱动的实现
- linux 2.6 输入子系统之键盘驱动的实现
- LINUX驱动之矩阵键盘
- LINUX设备驱动之键盘驱动
- 40 矩阵键盘在linux内核里的驱动实现
- Linux的input输入子系统:设备驱动之按键驱动
- linux 2.6 输入子系统 键盘驱动的实现
- linux设备驱动之API的实现
- LINUX设备驱动之输入子系统(二)
- LINUX设备驱动之输入子系统(三)
- LINUX设备驱动之输入子系统(二)
- LINUX设备驱动之输入子系统(三)
- LINUX设备驱动之输入子系统(三)
- LINUX设备驱动之输入子系统(二)
- Linux设备驱动之Hello world驱动的实现
- 【Linux设备驱动】Linux输入子系统之底层驱动
- linux下矩阵键盘驱动
- android系统PS2全键盘驱动(上)-使用linux的标准接口实现
- Android杂谈(23)Service+BroadcastReceiver+数据库+HttpURLConnection实现断点续传(上)
- Spring中Bean及@Bean的理解
- arm板子写spiflash
- 面试题3_查找二维数组中的指定整数
- sklearn 中的算法选择图(中文)
- 41 linux标准输入设备之矩阵键盘驱动的实现
- FZU 2140
- Dtection:PVA-net
- TLB工作原理
- error: could not lock config file E:/git/Git/%USERPROFILE%/.gitconfig: No such file or directory
- asp.net 入门
- (观后感)阿里妈妈首次公开自研CTR预估核心算法MLR
- Linux共享内存的使用(一)
- MyQueue Async