发 帖  
张飞软硬开源基于STM32 BLDC直流无刷电机驱动器开发视频套件, 👉戳此立抢👈

[经验] 嵌入式系统的键盘接口设计

2019-5-22 05:01:15  116 接口 嵌入式 Linux
分享
0
引言

随着嵌入式系统的不断发展,特别是嵌入式处理器运算能力的不断增强,嵌入式系统被广泛应用于信息家电、移动通信、手持信息设备以及工业控制等众多领域。与此同时,用户对于嵌入式系统图形用户界面的需求也不断提高。嵌入式Linux作为一种流行的嵌入式系统平台,它所具备的稳定、高效、易裁剪、易移植、硬件支持广泛等优点,结合其源码开放的特征,使得Linux在嵌入式操作系统中的地位日益重要。Qt/Embedded是一个完整的自包含GUI和基于 Linux的嵌入式平台开发工具,因其面向对象、跨平台、界面设计更美观和友好而得到广泛的应用。Qt/Embedded具有客户/服务器模型,直接向帧缓冲写入数据,摒弃了X窗口系统,节省了内存。同时,将外部输入设备抽象为键盘和鼠标输入事件,底层接口支持键盘、GPM鼠标、触摸屏,以及用户自己定义的设备等。

1 硬件设计
电路采用三星S3C2440处理器,实现了4×4矩阵键盘的输入。矩阵键盘使用了处理器的4个GPIO和4个中断,以中断方式获取键值,对应的中断引脚分别是EINT3、EINT9、EINT11、EINT13。GPIO引脚与矩阵键盘的行相连接作为输入端,中断引脚与矩阵键盘的列相连作为矩阵键盘的输出端。开始时GPIO端输出为低电平,当有按键被按下时,按键所在列输出低电平产生中断,这时可以判断按键所在的列。然后向每一行依次输入高电平,如果列的输出端由低电平变成高电平,则可以确定按键所在的行,这时键值被唯一锁定。具体电路如图1所示。

2 LinuX下键盘接口驱动
键盘设备属于字符设备,键盘驱动应该符合字符设备驱动的编写模式。Linux采用内核模块机制,当系统运行的时候驱动程序可以以模块的形式动态地加载和卸载,既方便了驱动的调试,又缩短了开发周期。在驱动中必须实现static int_init my_kb_init(void)函数和stat-
ic void_exit my_kb_exit(void)函数。static int_init my_kb_init(void)函数在内核加载键盘驱动时被调用,注册模块为以后调用模块函数预先做准备,同时完成字符设备的注册,分配主设备号,设置中断类型,安装中断函数,并且将所有中断禁止。static void_exit my_kb_ exit(void)函数在卸载模块时被调用,用于撤销初始化函数所做的一切,否则在系统重新引导之前一些东西会残留在系统中,导致模块重新加载失败。

键盘驱动中主要包括以下几个子模块:中断处理子模块、键盘扫描子模块、消抖处理和组合键子模块、重复按键子模块等。驱动工作流程如图2所示。

按键的识别主要是在中断处理子模块中完成的。当系统有按键被按下时,驱动程序先关掉中断,然后扫描键盘,确定哪个键按下,键盘按下和抬起都有中断发生,这样可以为用户提供按下和抬起标志,以判断按键是单键按下还是多键齐按。在消抖处理和组合键子模块中,加入Linux内核定时器,键盘定时扫描,消除抖动得到稳定键值。重复按键子模块是根据Linux内部的定时器,设置自动重复开始延时和自动重复延时,键盘按下后根据延时来完成按键事件,键值存入队列供应用程序读取。
3 Qt/Embedded键盘输入策略
3.1 Qt/Embedded架构简介
Qt/Embedded Linux应用程序需要一个正在运行着的服务器应用或者是本身就是一个服务器应用程序。任何一个Qt/Embedded Linux应用程序都可以扮演服务器的角色。当多于一个应用程序运行的时候,应用程序作为客户端与服务器程序相连接。
服务器进程和客户端进程有不同的分工:服务器进程管理着鼠标指针的处理、字符的输入和屏幕的输出。另外服务器还控制着屏幕光标的输出和屏幕保护程序。客户端进程完成所有应用程序的具体操作。一个QWSServer类的实例代表一个服务器应用,一个QWSClient类的实例代表着一个客户端应用。每一方面都有一些类完成各种操作。
所有系统产生的事件包括键盘事件和鼠标事件都被传递到服务器应用中,然后服务器将这些事件分发到客户端应用中。
3.2 客户端/服务器的通信
如图3所示,正在运行着的程序通过增加和删除窗口不断地改变屏幕的显示。服务器在对应的QWSWindow对象中维护着每一个顶层窗口的信息。每当服务器接收到一个事件时,它都会查询它的顶层窗口列表找到包含该事件位置的窗口。每一个窗口都有一个创建它们的客户端应用的ID,将这个ID返回给服务器。最后服务器将这个事件封装成一个QWSEvent类的实例,传递给相应的客户端。


另外还可以通过QWSServer::KeyboardFilter类实现按键事件的全局的底层过滤器。这种方法可以实现电源管理中的一键挂起,而不用在所有的应用程序中都对这个按键事件进行过滤。
如图4所示,服务器通过UNIX域套接字与客户端进行通信。客户端从服务器接收事件,这些事件通过重新实现QApplication的qwsEvent-Filter()函数可以被直接检索访问。

客户端相互之间(和服务器)通过QCopChannel类通信。QCOP用于在多个通道间传送信息,是一个多对多的通信协议。每个通道用名字作为识别 ID,任何一个想要和它通信的通道都能监听它。QCOP协议既允许在相同的地址空间内的客户端之间进行通信,也允许在不同的进程的客户端之间进行通信。
3.3 字符输入层
如图5所示,当一个服务器应用程序开始运行时使用Qt的插件系统加载键盘驱动,驱动是一个QWSKeyboardHandler类的实例。


键盘驱动从设备接收键盘事件,并把事件封装成一个QWSEvent类的实例,然后把这个类传送给服务器。定制键盘可以通过子类QWSKeybo- ardHandler类创建一个键盘驱动插件来实现。默认的QKbdDriveRFactory类将自动检测到这个插件然后把驱动加载到正在运行的服务器应用中。

4 键盘驱动插件的实现
本文通过Qt的插件系统实现了矩阵键盘的接口驱动。插件是一种遵循一定规范的应用程序接口编写出来的程序。在现代计算机语言中,应用环境复杂多变,常常要面临着适应这样那样的未知需求的挑战,为了使程序设计语言具有良好的可扩展性,使之能够适应复杂的应用环境,同时也出于降低设计复杂性的考虑,采用插件机制是一个很不错的方法。通过采用插件系统,把扩展功能从框架中剥离出来,可以降低框架的复杂度,让框架更容易实现。扩展功能与框架之间以一种松耦合的方式集成,允许在保持接口不变的情况下,实现彼此的独立变化。
Qt提供了两种插件:一种是高层的插件,用来扩展Qt自身,如自定义数据库驱动、图像格式、文本编解码器、自定义风格等;一种是底层的插件,用来扩展Qt应用程序。
一个键盘插件的实现,通常至少需要两个类:一个是插件封装器类,它实现了插件的通用API函数;另外一个是一个或多个处理器类,每个处理器类都实现了一种用于特殊类型插件的API。通过封装器类才能访问这些处理器类。下面是具体的实现过程:
首先要实现一个自己的MyKeyDriverPlugin类,这个类继承了QKbdDriverPlugin类,需要重新实现QKbdDriverPlugin::keys()函数和QKbdDriverPlugin::create()函数。

keys()函数返回一个键盘插件的键值,这个键值不能和其他的键值相冲突。create()函数返回一个给定键值的QWSKeyboardHandler派生类的实例。

在.cpp文件的最后,必须添加一个下面这样的宏:Q_EXPORT_PLUGIN2(keyboard,MyKeyDriverPlugin)
第一个参数项是目标库名字去除任意扩展符、前缀或者版本号之后的基本名。第二个参数则是插件的类名。
第二个要实现的类是处理类MyKeyboardHandler,这个类需要继承QWSKeyboardHandler类。当键盘驱动捕获到键盘数据时,系统会通过套接字监听键盘信息,并在MykeyboardHandler::readKbdData()中对捕捉到的扫描数据进行处理并封装,然后向服务器端发送键盘事件。
①打开键盘设备并初始化,一般调用open()函数。

监控键盘设备,调用QScoketNotIFier监控键盘设备kbdFd。


③发生键盘事件时读取键盘事件,读取键盘事件后将键值、按下等信息翻译成Qt内部键盘事件的格式,并通过调用processKeyEvent将事件分发出去。

5 键盘插件在应用程序中的使用
将键盘插件编译后生成一个libkeyboard.so的动态库,这个动态库的名字是由Q_EXPORT_PLUGIN2宏的第一个参数决定的。派生插件默认存储在标准插件目录下的子目录中,如果它们没有存储在正确的目录下Qt不会找到这些插件,所以要在使用的文件系统中创建Qt的标准插件目录。
要想应用程序在启动的时候能够正确加载键盘插件还要设置嵌入式Linux系统中的环境变量:
QWS_KEYBOARD=MyKeyHandler:/dev/kbd
MyKeyHandler对应着key()函数中的键值,kbd是在/dev文件夹下的键盘设备文件。Qt应用程序开始运行后要根据 QWS_KEYBOARD这个环境变量创建一个MyKeyboardHandler类。窗口部件响应服务器分发的键盘事件还要重新实现如下函数。
作者:刘绍宗 张庆荣 张金亮 朱庆科

相关经验

评论

高级模式
您需要登录后才可以回帖 登录 | 注册

发经验
课程
    关闭

    站长推荐 上一条 /8 下一条

    快速回复 返回顶部 返回列表