之采用GPIO扩展口自定义键盘Input驱动+QT界面键盘输入测试
【创龙AM4379 Cortex-A9试用体验】之采用GPIO扩展口自定义键盘Input驱动+QT界面键盘输入测试
尽管现在的大部分移动终端基本上都采用了触摸屏的人机交互方式,但是在有些场合并不是适合采用触摸式的用户输入,比如粉尘密度较大的车间、煤矿井下作业环境等。去年遇到过一个煤矿瓦斯浓度分析仪的小项目,要求LCD屏显示采集的数据,拟合称曲线,可以人工输入一些设置参数,明确要求不能用触摸屏,必须采用机械按键的方式输入数据。我们目前的项目多数与铸造车间有关,在铸造熔炼侧,空气中粉尘密度大,并且多为铁粉,如果数据采集仪放在熔炼侧,基本上半天时间LCD屏上就会落满灰尘,如果频繁触摸LCD表面,势必会过早划伤LCD屏,并且操作灵敏度也会有一定的影响。这篇使用报告我们就来介绍一下如何开发满足车间小量数字输入的需求,功能简单的,又满足Linux Input框架模型的键盘驱动程序。
1. 硬件设置
和上一篇试用报告一下,我们依然采用TL-4379开发板GPIO扩展接口提供的GPIO3_14,GPIO3_16,GPIO4_3和GPIO4_2来模拟键盘的1,2,TAB和ENTER键。如图所示:
按键与键盘扫描码的对应关系为:
GPIO3_14 —— 1
GPIO3_16 —— 2
GPIO4_3 —— TAB
GPIO4_2 —— ENTER
硬件连接原理图:
硬件连接实物图:
2. 驱动程序
驱动程序采用Linux标准的Input架构,申请Input device结构体,设置、注册该Input设备处理的输入事件类型、按键扫描码等。我们定义中断处理程序,对机械按键进行消抖处理,确定按键有效后想Linux Input核心上报按键事件及其码值。
2.1 头文件
由于Linux3.0之后的一些相关头文件与Linux2.6版本有较大不同,一定要注意头文件,否则在编译驱动时会报错。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/irq.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/semaphore.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <linux/irq.h>
#include <linux/poll.h>
2.2 I/O口定义及全局变量
宏定义与硬件相关的I/O口、中断号及按键信息统一管理相关的结构体,如下所示:
#define GPIO_KEY1_PIN_NUM (3*32 + 14) /*gpio 3_14 */
#define GPIO_KEY2_PIN_NUM (3*32 + 16) /*gpio 3_16 */
#define GPIO_KEY3_PIN_NUM (4*32 + 3) /*gpio 4_3 */
#define GPIO_KEY4_PIN_NUM (4*32 + 2) /*gpio 4_2 */
#define KEY1_IRQ gpio_to_irq(GPIO_KEY1_PIN_NUM) /*KEY1对应的中断号*/
#define KEY2_IRQ gpio_to_irq(GPIO_KEY2_PIN_NUM) /*KEY2对应的中断号*/
#define KEY3_IRQ gpio_to_irq(GPIO_KEY3_PIN_NUM) /*KEY3对应的中断号*/
#define KEY4_IRQ gpio_to_irq(GPIO_KEY4_PIN_NUM) /*KEY4对应的中断号*/
struct pin_desc{
intirq;
char*name;
unsignedint pin;
unsignedint key_val;
};
struct pin_desc pins_desc[4] = {
{-1,"K1", GPIO_KEY1_PIN_NUM, KEY_1}, /*按键1*/
{-1,"K2", GPIO_KEY2_PIN_NUM, KEY_2}, /*按键2*/
{-1,"K3", GPIO_KEY3_PIN_NUM, KEY_TAB}, /*按键TAB*/
{-1,"K4", GPIO_KEY4_PIN_NUM, KEY_ENTER}, /*按键ENTER,即回车键*/
};
static struct input_dev *buttons_dev; /*输入设备结构体指针,作为全局变量为其他函数使用*/
static struct pin_desc *irq_pd;
static struct timer_list buttons_timer; /*定义消抖定时器*/
2.3 按键中断处理函数
当按键被按下时,触发系统中断,我们在中断函数中启动定时器,对按键进行去抖处理,当判断出确实是机械按键被按下了时,去抖定时器上报按键的键码值到Input核心层。
static irqreturn_t buttons_irq(int irq,void *dev_id)
{
/*10ms后启动定时器 */
irq_pd= (struct pin_desc *)dev_id;
mod_timer(&buttons_timer,jiffies+HZ/100);
returnIRQ_RETVAL(IRQ_HANDLED);
}
static void buttons_timer_function(unsignedlong data)
{
structpin_desc * pindesc = irq_pd;
unsignedint pinval;
if(!pindesc)
return;
pinval= gpio_get_value(pindesc->pin);
if(pinval)
{
/*松开 : 最后一个参数: 0-松开, 1-按下 */
input_event(buttons_dev,EV_KEY, pindesc->key_val, 0);
input_sync(buttons_dev);
}
else
{
/*按下 */
input_event(buttons_dev,EV_KEY, pindesc->key_val, 1);
input_sync(buttons_dev);
}
}
2.4 驱动模块初始化函数
在该函数中,申请GPIO资源,设置GPIO为输入,申请GPIO对应的中断,同时分配Input device结构体,设置按键码值等。
static int buttons_init(void)
{
inti;
intret;
/*设置按键中断*/
pins_desc[0].irq= KEY1_IRQ;
pins_desc[1].irq= KEY2_IRQ;
pins_desc[2].irq= KEY3_IRQ;
pins_desc[3].irq= KEY4_IRQ;
ret= gpio_request_one(pins_desc[0].pin, GPIOF_IN, "KEY1 IRQ"); /* 申请 IO ,为输入*/
if(ret < 0) {
printk(KERN_ERR"Failed to request GPIO for KEY1\n");
}
ret= gpio_request_one(pins_desc[1].pin, GPIOF_IN, "KEY2 IRQ"); /* 申请 IO ,为输入*/
if(ret < 0) {
printk(KERN_ERR"Failed to request GPIO for KEY2\n");
}
ret= gpio_request_one(pins_desc[2].pin, GPIOF_IN, "KEY3 IRQ"); /* 申请 IO ,为输入*/
if(ret < 0) {
printk(KERN_ERR"Failed to request GPIO for KEY3\n");
}
ret= gpio_request_one(pins_desc[3].pin, GPIOF_IN, "KEY4 IRQ"); /* 申请 IO ,为输入*/
if(ret < 0) {
printk(KERN_ERR"Failed to request GPIO for KEY4\n");
}
/*设置 GPIO 为输入 */
for(i=0;i<4;i++)
gpio_direction_input(pins_desc.pin);
/*为GPIO3_14,GPIO3_16,GPIO4_2,GPIO4_3申请中断 */
if(request_irq(KEY1_IRQ,buttons_irq, IRQ_TYPE_EDGE_BOTH, "K1", &pins_desc[0]))
{
printk(KERN_ERR"Failed to request IRQ for KEY1\n");
}
if(request_irq(KEY2_IRQ,buttons_irq, IRQ_TYPE_EDGE_BOTH, "K2", &pins_desc[1]))
{
printk(KERN_ERR"Failed to request IRQ for KEY2\n");
}
if(request_irq(KEY3_IRQ,buttons_irq, IRQ_TYPE_EDGE_BOTH, "K3", &pins_desc[2]))
{
printk(KERN_ERR"Failed to request IRQ for KEY3\n");
}
if(request_irq(KEY4_IRQ,buttons_irq, IRQ_TYPE_EDGE_BOTH, "K4", &pins_desc[3]))
{
printk(KERN_ERR"Failed to request IRQ for KEY4\n");
}
/*1. 分配一个input_dev结构体 */
buttons_dev= input_allocate_device();;
/*2. 设置 */
/*2.1 能产生哪类事件 */
set_bit(EV_KEY,buttons_dev->evbit);
set_bit(EV_REP,buttons_dev->evbit);
/*2.2 能产生这类操作里的哪些事件: 0,1,TAB,ENTER */
set_bit(KEY_1,buttons_dev->keybit);
set_bit(KEY_2,buttons_dev->keybit);
set_bit(KEY_ENTER,buttons_dev->keybit);
set_bit(KEY_TAB,buttons_dev->keybit);
/*3. 注册 */
input_register_device(buttons_dev);
/*4. 硬件相关的操作 */
init_timer(&buttons_timer);
buttons_timer.function= buttons_timer_function;
add_timer(&buttons_timer);
return0;
}
2.5 驱动模块退出函数
释放初始化时申请的系统资源。
static void buttons_exit(void)
{
inti;
for(i = 0; i < 4; i++)
{
free_irq(pins_desc.irq,&pins_desc);
}
for(i = 0; i < 4; i++)
{
gpio_free(pins_desc.pin);
}
del_timer(&buttons_timer);
input_unregister_device(buttons_dev);
input_free_device(buttons_dev);
}
2.6 驱动模块注册、注销及授权申明
module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL");
2.7编译驱动模块
执行命令:
make ARCH=armCROSS_COMPILE=arm-linux-gnueabihf-
编译结果如图所示:
将key_board.ko拷贝到NFS共享目录:
cp ./key_board.ko /nfsshare
3. 创建QT测试程序
创建QT测试程序qt_keyboard_test,设计界面如图所示:
设置“=”按钮的命令处理函数,如图所示:
我们模拟键盘的ENTER功能,所以我们必须将“=”按钮设置为默认按钮,在Widget的构造函数中设置如下:
编译工程,结果如下图所示:
将qt_keyboard_test拷贝到NFS共项目目录。
4. TL-4379开发板上电测试
4.1 给TL-4379开发板上电
TL-4379上电后,执行命令:
mount -t nfs 192.168.1.103:/nfsshare /mnt-o nolock
cd /mnt
/etc/init.d/matrix-gui-2.0 stop
4.2 加载key_board.ko
执行命令加载key_board.ko驱动模块:
insmod key_board.ko
如果所示:
查看当前嵌入式Linux系统下的Input设备:
ls /dev/input
如图所示:
执行命令,测试一下4个按键的事件是否传递到了应用层:
依次按下KEY1,KEY2,KEY3和KEY4,说明4个按钮的事件消息已经上传到了应用层。
4.3 加载QT测试程序
执行如下命令启动QT测试程序:
./qt_keyboard_test -plugintslib:/dev/input/touchscreen0
启动界面如图所示:
连续按下KEY1键及KEY_1,输入结果如图所示:
按下KEY3键即KEY_TAB,将输入焦点切换到第二个输入框,连续按两次KEY2键,结果如图所示:
按下KEY3键即KEY_TAB键,光标切换到“=”按钮处,按键四周多了一个虚线矩形框,如图所示:
按下KEY4键,即KEY_ENTER回车键,触发加法运算,结果如图所示:
5. 小结
通过设计KEY键盘驱动程序,我们成功的模拟了普通键盘的一般功能,这样我们就可以在对空间要求紧凑,而又必须用机械式键盘的场合,比照上面的设计,完成通过GPIO模拟通用键盘的功能。