李其珂,付红桥
(重庆大学光电工程学院,重庆 400030)
随着电子信息技术的高速发展,越来越多的工业控制设备和移动终端设备得到广泛的应用,这些设备通称为嵌入式系统。IEEE(国际电气和电子工程师协会)对嵌入式系统的定义为“用于控制、监视或者辅助操作机器和设备的装置”。在这些嵌入式系统中,键盘作为人机交互的重要手段,是应用最为广泛的输入设备之一。当按键较少时,采用独立式键盘,当按键较多时,采用矩阵式键盘。嵌入式Linux具有开源、免费、容易裁剪和良好的硬件支持等特性[1],在嵌入式计算领域得到了最为广泛的应用。
当前,对于嵌入式系统中的矩阵键盘功能实现,多数应用是基于中断方式来实现的[2-3]。按键事件在一个嵌入式系统中并不需要实时性很高,而采用中断的方式来判断按键是否按下的方案会把一个嵌入式系统中的按键事件的优先级提高,系统中其他更需要迫切处理的事件就延迟处理了,这就降低了整个系统的实时性能。本研究在基于嵌入式微处理器 AT91RM9200的基础上,采用轮询的机制实现了一个4×4矩阵键盘,并设计了矩阵键盘的驱动程序。
AT91RM9200是ATMEL应用最为广泛的一款基于RISC架构的工业级ARM9处理器,主频高达200 MHz,功耗低,片内外设非常丰富,支持嵌入式微处理器的各种常用接口,并且提供了4×32个可编程的GPIO端口[4]。本系统采用PB端口扩展了一个4×4矩阵键盘。其中键盘硬件连接示意图如图1所示。按键列阵列必须提供上拉信号,即通过4.7 K上拉电阻连接到+3.3 V电源电压VDD上,行和列阵列都加了20 Ω电阻,防止瞬间电流过大对嵌入式微处理器I/O口造成冲击。
按照键盘的构造方式,常用的键盘接口分为独立式键盘接口和矩阵式键盘接口。其中,独立式键盘的每个按键都占用嵌入式微处理器的一个I/O口,各按键相互独立。使用独立式键盘的优点是电路简单、可靠,软件编程简单。缺点是在资源受到限制的嵌入式系统中,独立式键盘对I/O口占用较多。因此,在嵌入式系统中很少使用独立式键盘这种方案。
在嵌入式应用系统中,大量使用矩阵式键盘,它能用比较少的I/O端口驱动比较多的按键。图1是嵌入式系统中最常采用的4×4矩阵键盘。4×4矩阵键盘电路由4根行线和4根列线组成,按键位于行、列的交叉点上。一个4×4的行列结构可以构成一个16个按键的键盘。4×4矩阵键盘的行和列分别和AT91RM9200处理器 的4个GPIO(General Parallel Input/Output)端口相连。显然,在按键数量较多的场合,矩阵键盘和独立键盘相比可以大大节省嵌入式系统的I/O口资源。
图1 键盘硬件连接示意图
本研究采用Linux 2.6内核作为设计的软件平台。随着信息技术的不断进步,系统的拓扑结构越来越复杂,操作系统对智能电源管理、热插拔以及即插即用的支持要求也越来越高,Linux 2.4内核已经难以满足这些需求。为了满足这些新的需求,Linux 2.6内核开发了全新的总线、类、设备和驱动环环相扣的设备模型[5]。
Linux 2.6内核的设备模型如图2所示。它是基于总线、设备和驱动的。设备挂在总线上,驱动和设备进行分离。
Linux系统将设备分成3种基本类型:字符设备、块设备和网络设备[6]。键盘属于字符设备。许多设备都挂在总线上,但是也有设备没有挂在总线上,本系统的矩阵键盘设备就没有挂在总线上。Linux 2.6内核提供了一种platform总线机制,它是一种虚拟的总线。没有挂在总线上的设备都可以挂在platform总线上。这种机制完全符合Linux 2.6内核的总线、设备和驱动的设备模型,并将设备和驱动进行了分离。
在Linux内核源代码中,各种驱动程序的代码量占据了整个Linux内核代码的85%,这足以说明Linux设备驱动在内核代码中的重要作用。设备驱动不仅为应用程序提供了访问设备的机制,还为应用程序屏蔽了硬件的具体细节。在应用程序看来,硬件设备只是一个设备文件,用户可以像操作普通的文件一样应用程序操作硬件。
图2 Linux设备模型
在完成4×4矩阵键盘接口电路的设计和掌握Linux 2.6的设备模型的基础上,还需要编写相应的矩阵键盘驱动程序。本研究采用的基于AT91RM9200处理器的平台已经成功移植并运行了嵌入式Linux操作系统[7],因此键盘的驱动模块开发就变得简单了许多。嵌入式Linux中的驱动程序运行在内核态,属于操作系统的范畴。驱动程序可以静态编译到内核,也可以以模块方式动态加载到内核。为了方便调试,本研究选择将按键驱动以动态模式加载到内核中。矩阵键盘驱动程序的编写主要可分为驱动程序初始化、文件操作接口实现以及键盘扫描3部分。
键盘驱动程序属于字符设备驱动程序的范畴。键盘驱动模块主要由模块加载函数、模块卸载函数和模块许可证声明3部分组成。模块加载函数除了完成键盘设备主、次设备号的申请和键盘设备的注册外,还应包括硬件的初始化。下面是本文根据AT91RM9200处理器的特性[4]编写了硬件初始化的伪代码。
键盘在嵌入式系统中起到输入作用,所以在矩阵键盘驱动程序的文件操作结构体中,最重要的文件操作函数是文件读函数。在键盘设备文件读函数中加入自旋锁,确保矩阵键盘文件在同一时刻只被一个进程打开。因为多次按键可能无法被及时处理,结合实际项目的应用需求,可以利用缓冲区缓存被按下键的键码值,直到用户应用程序处理这一按键事件。本研究中的缓冲区是用一个环形队列来实现的。缓冲区的大小可以根据具体的应用来调整,本研究设定缓冲区的大小为10个字节。当按键被按下后,键码值就缓存到缓冲区的下一个空闲位置处,并更新缓冲区位置指针。当缓冲区满时,丢弃此次按键的键码值。用户应用程序通过系统调用read()函数来读取缓冲区的键码值,驱动程序根据实际读取键码值的多少调整缓冲区的位置指针。以下是实现键盘读函数keyboard_read()的伪代码:个数;
键盘扫描程序是用来判断被按下键的位置并取得相应的键码值,然后存放到键码缓冲区中。键盘扫描程序的实现有中断方式和轮询方式。本研究中利用嵌入式Linux操作系统的内核定时器实现了轮询矩阵键盘的扫描方案,主要基于以下原因:一是AT91RM9200的一组GPIO端口只有一条中断信号线,如果采用中断方式,对于图1的硬件设计方案必须使用4条中断信号线才能满足设计要求;二是因为键盘是嵌入式系统中的一种低速输入设备,采用轮询扫描键盘的方式就可以满足矩阵键盘的输入要求。
嵌入式Linux操作系统提供了良好的内核定时器机制,只要通过对内核定时器接口函数和数据结构进行简单的操作,就可以实现周期性地轮询矩阵键盘的状态,并对按键事件进程进行处理。这一周期性时间间隔本研究指定为10 ms。本文给出的4×4矩阵键盘轮询扫描程序算法流程如图3所示。
对于消除按键抖动,本研究采用软件消抖的方法。键盘作为输入设备,也是共享设备。本文采用了“自旋锁(spinlock)”机制来对共享的键盘实现互斥访问。因为自旋锁保护的代码不能睡眠,否则就有可能发生死锁,造成系统死机,因此消除按键抖动的思路是在第1次检测到有键按下时,调用mdelay(KBD_JITTER)函数实现2 ms的短延时后,再次确认该键是否被按下,从而达到消除按键抖动的目的。
图3 矩阵键盘扫描算法流程
利用按键按下抬起标志(按键抬起的键码值是按下的键码值加上0xA0),把一次完整的按键事件分成按下和抬起2部分。当有键按下未抬起的同时又有其他键按下,这时线程就会一直循环扫描键盘事件,所以能把其他的按键事件捕捉到并加入到环形等待队列中。
键盘驱动测试主要测试键盘驱动能否正确地判断被按下键的位置并取得相应的键码值。在硬件方面,首先将PB0~PB7端口与矩阵键盘的行列线如图1所示连接。然后用串口线把PC机的COM口和以AT91RM9200为核心的嵌入式目标系统的调试串口相连。在软件方面,保证嵌入式目标系统已经能稳定运行Linux操作系统。然后利用PC机上的串口控制台来控制嵌入式目标系统[8]。用insmod命令将编译好的矩阵驱动模块动态加载到内核,然后执行矩阵键盘测试应用程序。
当有按键被按下时,就会有被按下键的键码值立即回显在串口控制台;当按键抬起时,就会有相应按键的键码值加上0xA0立即回显在串口控制台。测试表明,此矩阵键盘驱动程序能实时、正确地判断被按下键的位置。
本文介绍了基于ATMEL AT91RM9200和嵌入式Linux的4×4矩阵键盘驱动程序的实现方案。该驱动程序以动态模块的方式加入内核后,通过应用程序测试证明矩阵键盘驱动工作准确、稳定。这一方案也对在其他嵌入式设备上扩展矩阵键盘具有参考价值和意义。
[1]Alex Lentnon.Embedded Linux[J].Embedded Systems,2001(5):125-128.
[2]孟桂芳.基于嵌入式Linux的矩阵键盘设备驱动的设计[J].苏州大学学报:工科版,2011(4):71 -74.
[3]杨斌斌,张雪英,王玉宏.基于嵌入式Linux的矩阵键盘驱动程序研究与开发[J].现代电子技术,2009(2):39-41.
[4]ATMEL.AT91RM9200 User Manual[S].
[5]宋宝华.Linux设备驱动开发详解[M].北京:人民邮电出版社,2008.
[6]Jonathan Corbet,Alessandro Rubini,Greg Kroah-Hartman.Linux设备驱动程序[M].魏永明,耿岳,钟书毅,译.北京:中国电力出版社,2005.
[7]陈阳,徐晓光,陈跃东.基于嵌入式系统的电能采集终端设计与实现[J].重庆理工大学学报:自然科学版,2011(3):97-101.
[8]王森林,庄圣贤.基于嵌入式Linux的MP3播放器设计[J].重庆工学院学报:自然科学版,2007(3):65-68.