历史上的今天
返回首页

历史上的今天

今天是:2025年02月05日(星期三)

正在发生

2018年02月05日 | 基于嵌入式Linux内核的键盘驱动控制模块设计

2018-02-05 来源:eefocus

    为了适应嵌入式设备外设的多样性,本文以特殊矩阵键盘为例,设计了一套完整的驱动控制模块。硬件电路设计采用外扩3片SN74HC 164芯片的方式,节省了GPIO引脚的使用,大大提高了利用效率。同时,在此基础上引出了Linux内核中input子系统的特性和工作机制,呈现了较为完整的输入事件由内核空间传递到用户空间进程的过程。实验结果表明,设计的驱动模块具有良好的实时性和准确性。

    随着微处理器技术的不断发展和数字化产品的普及,嵌入式系统的研究开发逐渐成为热点,Linux也以其开源、稳定、可裁剪的优势成为嵌入式操作系统的主流。在众多的嵌入式系统中,键盘成为一种应用最为广泛的输入设备。然而,嵌入式设备的功能差异性又决定了为其提供一种通用性键盘是不可行的,往往需要根据系统的实际功能设计所需的特殊键盘,并实现相应的驱动程序。
    S3C6410是三星公司高性能的32位RISC微处理器,内部集成了多种强大的硬件加速器,适合进行视频和图像处理,成为了目前嵌入式处理器领域的主流产品。本文以在S3C6410微处理器基础上实现一个24键矩阵键盘为例,呈现了在嵌入式系统中开发设备驱动程序的整体流程,并对Linux系统下输入事件的底层传递机制进行了研究和分析。

1 接口电路的设计
    在嵌入式设备上扩展键盘的常用方式是通过对CPU的GPIO端口进行扫描实现的,显然这种方式在键盘按键数目较多的情况下,会占用过多的GPIO资源,增加了GPIO端口资源较为紧张的嵌入式处理器的负担。
    本系统的硬件设计通过增加3片SN74HC164芯片来达到节约GPIO资源的目的。SN74HC164是一种8位的串行输入、并行输出移位寄存器,它的内部由8个D触发器串联而成。每当时钟信号由低电平变为高电平时,两个输入端将当前输入信号传送到并行输出端,并实现移位操作。系统硬件原理图如图1所示。

a.JPG

    
    3个SN74HC164芯片串联后,将它们的CLK引脚接到S3C6410开发板的GPE4端口上。第一个SN74HC164芯片的A、B输入引脚共同接到开发板的GPE3端口上,并且将这两个GPIO端口配置成输出模式。GPE2端口与键盘按键的上拉端连接,系统运行时在中断模式和输入模式之间切换,以达到触发中断和对键盘扫描的目的。这样我们就借助于3个SN74HC164移位寄存器,只占用3个GPIO端口,给扫描键盘的24个按键提供输入信号,既节约了成本,又避免了GPIO资源的浪费。

2 扫描工作原理
    扩展硬件电路的同时给键盘驱动程序的实现带来了一定的麻烦,驱动程序首先要将SN74HC164驱动起来,然后才能对电路进行控制。该电路的输出引脚被接到S3C6410的GPE2端口上,并且这个端口被配置成中断源,无键按下时直接读为高电位。键盘扫描时通过SN74HC164芯片先将键盘的24个键置低电平,任何一个键被按下,GPE2端口就会有从高电平到低电平的跳变,从而触发一次中断。
    在中断处理过程中,将GPE2端口置为输入状态。然后根据SN74HC164芯片的输入/输出特性,给串联的3个SN74HC164芯片发送24个高电平信号,使得键盘的各键位均为高电平。在随后的24个时钟脉冲下,给SN74HC164芯片送入1个0和23个1,使得0在每个键位的输入端都只出现一次,同时在GPE2端口进行扫描。当被按下键处于0输入状态时,其所在行就会读到一个低电平,也就可以确定出键盘上哪个键被按下了。

3 驱动模块结构
    在Linux2.6的版本中新加入了input子系统,给驱动编写者提供了一个完整的输入事件——从底层设备传递到用户进程的模型。本文基于input子系统架构,设计了一个较为完善的特殊键盘驱动模块。键盘驱动模块结构如图2所示。

b.JPG

    
    在input子系统的设备内核模型中,最重要的数据结构体是struct input_dev,作为驱动的主体,每个structinput_dev代表一个输入设备。该结构体中既包含了设备所能响应的输入事件类型、响应按键种类、键盘码表,以及坐标范嗣等字段,同时还包含了设备打开、关闭以及回调函数等字段,能够完整地记录和标识整个设备的功能与行为。在向内核注册input_dev之前,需要进行input_dev结构的初始化,同时向内核申请键盘中断。

    首先设置输入设备的功能,input_set_capability(&sim_key,EV_KEY,KEY_A)函数完成键盘A键的输入使能,类似可完成B~X共24个按键的输入使能。然后设置键盘的码表。该键盘包含20个按键,码表可表示为:statIC unsigned char sim_keycode[24]={KEY_A,KEY_B,KEY_C,KEY_D,KEY_E,KEY_F,KEY_G,KEY_H,KEY_I,KEY_J,KEY_K,KEY_L,KEY_M,KEY_N,KEY_O,KEY_P,KEY_Q,KEY_R,KEY_S,KEY_T,KEY_U,KEY_V,KEY_W,KEY_X)。当相应键按下时,码表中的键值将被作为键盘码上报到用户空间的进程。初始化工作完成之后,调用函数input_register_device(&sim_kb)向内核注册输入设备。
    由于键盘设备的输入是异步的,可能会在任何时间得到按键事件,所以需向内核申请中断以保证对键盘输入的实时响应。中断函数完成键盘的扫描操作,并上报输入事件到用户进程,是整个驱动模块的功能主体。然而使用中断会遇到一个问题,在键盘的扫描过程中,按键的每次按下和抬起都会有10~20 ms的毛刺抖动存在,会将用户的一次按键操作误当作几次按键来处理。所以为了获取稳定的按键信息,必须要想办法去掉这种抖动。去毛刺的一种常见的方法是在注册输入设备时定义一个定时器timer,当触发中断时先关闭I/O中断,然后启动定时器,等跳过毛刺抖动以后再去调用扫描程序得到键值,并重新打开中断。按键事件被发送到input子系统核心后通知给用户进程,从而实现查键过程。

4 基于input子系统的事件传递机制
    实现底层驱动程序与用户进程通信的最主要的函数是input_event(struct input_dev * dev,unsigned int type,unsigned int code,int value),也是input输入子系统的核心,其实现机制如下。
    Linux系统在启动过程中会向系统核心注册input_handler,一般将其称为handler处理器,表示对输入事件的具体处理,input_handler为输入设备的功能实现了一个接口。在执行input_register_device注册输入设备的时候,会自动将input_dev结构与系统中已注册的input_
handler进行遍历匹配。与对应的input_handler成功匹配后,Linux内核自动创建evdev结构体来表示输入事件设备,该结构中包含了input _handle等字段,作为连接input_dev与input_handler的媒介。其中Linux内核中与键盘设备匹配的input_handler代码为:
    static struct input_handler evdev_handler={
          .event=evdev_event,
          .connect=evdev_connect,
          .disconnect=evdev_disconnect,
          .fops=&evdev_fops,
          .minor=EVDEV_MINOR_BASE,
          .name=“evdev”,
          .id_table=evdev_ids,
    };
    evdev_event函数为事件处理函数,输入设备所上报的事件通过evdev_handler中的evdev_event函数包装成input_event标准输入格式,并存放在evdev下的evdev_list缓冲区中,该结构代码如下:
    struct input_event{
          struct timeval time; //事件发生的时间
          __u16 type; //事件类型
          __u16 code; //子事件
          __s32 value; //事件发生的相关值
    };
    用户进程读取键盘事件时即会按照此种特定格式进行。值得注意的是,当读取事件为鼠标输入时,需要先后读取X轴坐标和Y轴坐标两种数据,以完成完整的读取操作。
    在Linux系统中,所有的外设都是通过虚拟文件系统向应用程序提供接口,所以每个具有独立功能的外设在Linux系统中都对应着相应的设备文件。同时,在内核中代表设备文件的结构体包含了实现该设备功能的特定操作函数。
    完成驱动模块的安装之后,Linux系统会在/devr目录下自动创建输入事件设备文件,本文中该设备名为event0。用户进程打开对应的输入事件设备文件event0,即可执行相应的文件操作,如rcad、ioctl等。文件操作函数最终要进入内核,并调用存储在事件设备结构体中的
evdev_handler.evdev_fops操作函数集完成对应的文件操作。
    e.JPG
    例如用户进程在执行rcad操作时,会调用内核中evdev_fops->evdev_rcad函数,先判断当前输入事件设备缓冲区中是否有待读取的input _event事件。若缓冲区中无按键事件,进程则放入等待队列进行睡眠,直到有按键事件产生并保存到缓冲区后,将睡眠进程唤醒,调用copy_ to_user复制函数完成输入事件从内核空间到用户空间的拷贝,从而实现读取操作。

结语
    通过以上分析可以得出,键盘设备所产生的输入事件以input子系统为传递介质,并通过虚拟文件系统接口得以通知用户进程。本文从键盘的驱动开发出发,呈现了较为完整的输入事件由内核空间传递到用户空间进程的过程,对于驱动开发者了解底层驱动的机制和更加有效地设计驱动模块有着较为重要的意义。经过测试,该键盘具有良好的响应特性,并实现了所预期的功能。

推荐阅读

史海拾趣

ELNA(依娜)公司的发展小趣事

进入电子领域后,ELNA迅速在电子元件领域取得了突破。公司凭借其在材料科学和制造工艺方面的专长,成功开发出了一系列高性能的电子元件产品。这些产品广泛应用于通信、计算机、消费电子等领域,为ELNA赢得了广泛的客户群。

为了保持技术领先,ELNA不断投入研发资源,加强技术创新。公司积极引进国际先进的生产设备和检测手段,提升产品品质和可靠性。同时,ELNA还加强了与国内外科研机构和高校的合作,共同推动电子元件技术的发展。

Anixter Inc公司的发展小趣事

在数字化转型和智能化升级的趋势下,ELNA开始积极探索新技术和新模式。公司加强了与互联网和人工智能技术的融合,推动产品和服务的智能化升级。同时,ELNA还加强了对大数据和云计算技术的应用,提升生产效率和管理水平。

以上是对ELNA公司在电子行业中的部分发展历程的概述。您可以根据这些故事的开头进一步扩展和完善内容以满足字数要求。

集创北方(CHIPONE)公司的发展小趣事

在显示芯片领域,集创北方是国产化替代的积极推动者。公司凭借自身在显示芯片领域的深入研究和创新实力,成功打破了国外厂商的技术垄断,为国内厂商提供了更多选择。同时,集创北方还积极参与行业标准制定,推动了国内显示芯片产业的健康发展。

弘凯光电(BRIGHTEK)公司的发展小趣事

弘凯光电一直致力于技术创新和产品升级。公司拥有一支高效、创新的技术团队,通过对产品技术的深入研究和持续创新,不断推出具有竞争力的新产品。例如,公司的iCLed系列产品,凭借其独特的设计和卓越的性能,在智能座舱设计领域获得了广泛应用。这种对技术创新的不断追求,使得弘凯光电在电子行业中始终保持领先地位。

Decawave公司的发展小趣事

Decawave公司成立于2007年,总部位于爱尔兰都柏林。成立之初,公司就专注于超宽带(UWB)技术的研发。在初创阶段,Decawave面临着技术难度大、资金紧张等挑战。然而,凭借对UWB技术的深入理解和研究,公司成功开发出了具有高精度定位能力的UWB芯片,这一技术突破为Decawave在电子行业中赢得了声誉。

C&K Components公司的发展小趣事

进入新时代,C&K继续坚持创新驱动的发展战略,不断加大在研发和创新方面的投入。公司紧跟行业趋势,积极探索新技术、新工艺和新应用,推出了一系列具有创新性和领先性的开关产品。同时,C&K也注重与客户的沟通和合作,深入了解客户需求,提供个性化的解决方案和优质的服务。这些举措使C&K在激烈的市场竞争中始终保持领先地位,并赢得了更多客户的认可和支持。

这些故事展示了C&K Components公司在电子行业中的发展历程和取得的成就。从初创时期的坚持与探索,到60年代的转型与突破,再到90年代的行业领先地位确立,以及千禧年代的并购与融合和新时代的创新与发展,C&K始终保持着对技术的热爱和对市场的敏锐洞察,不断推动公司向前发展。

问答坊 | AI 解惑

高清视频处理,用FPGA还是DSP

在视频处理中,FPG和DSP之争早就有了,考虑到量产价格的原因 ,我们老大偏向DSP,可据说FPGA做高清视频处理比 DSP效果好,各位有什么看法啊,也没机会做个比较!…

查看全部问答>

[思考]C++不适合做大项目?

http://blog.codingnow.com/2009/12/cpp2009.html Lippman 大牛的第一场,关于大型可伸缩性的软件开发的, Chen Shuo 同学翻译的很不错 找到电源,所以可以写写了。 果然是牛人啊,上来就讲形而上的东西。我听的有趣,就做了点笔记,但是记的不 ...…

查看全部问答>

噪音变燃料

每一滴水都饱含环保的燃料-氢,但是,怎样能把它从水里拿出来是一个挑战。利用噪音污染-例如,在主要道路上,一种新材料有了可利用的希望。威斯康星-麦迪逊大学的一个研究小组,做出了氧化锌晶体,当把它沉浸在水中时,它就会吸收振动并创建出强大 ...…

查看全部问答>

推荐一本嵌入式系统设计的好书

前几天借到一本邱毅凌写的《现代嵌入式系统开发专案实务》,描述了一个beginner到项目经理的故事,易懂有趣,讲了很多一般软件工程书不好讲或者不敢讲的事,有志做项目领导者的朋友不妨读读。…

查看全部问答>

calibrate_delay 的头文件是什么

嵌入式开发,Linux系统,是可以在驱动里面调用calibrate_delay 这个函数的吧 不知道需要包含什么头文件…

查看全部问答>

WINCE下10Mbps的SPI数据流采用中断的接收工作方式(等到了中断然后一个个地读接收SFR)能不能处理的过来?难道必须用DMA工作方式吗?

WINCE下10Mbps的SPI数据流采用中断的接收工作方式(等到了中断然后一个个地读接收SFR)能不能处理的过来?难道必须用DMA工作方式吗?…

查看全部问答>

sql同步问题

我在VS2005和SQL2005平台上,开发mc3000应用程序,该程序在WM5上运行正常,但在mc3000上同步数据有问题,如下: string SQL        = \"\"; SqlCeConnection cn; SqlCeCommand cmd; SqlCeRemoteDataAccess RDA = null; ID ...…

查看全部问答>

多串口采集卡的设计思路

现在市场上有很多多串口卡卖,但都必须借助PC为平台来使用,我想有一块多串口卡 ;其功能是在外部有一个25Hz的中断信号输入到多串口卡,此时几个串口同时录取数据,录取完成以后将几路的数据按一定的顺序写入到一个RAM中,以给其他设备来使用。…

查看全部问答>

关于select()函数的问题

原程序:#include <fcntl.h>#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <time.h>int main(){        int fds[2]; & ...…

查看全部问答>

请问开关电源模块与电源适配器的比较

请问各位高手,220V转5V (3A)的开关电源模块与220V转5V的电源适配器(老式复读机用的那种)相比二者的性能(例如纹波、噪声等)、可靠性哪个更好?是给一个单片机数据处理模块供电,工作电流不会超过100mA。用于一个对设备可靠性要求极高的场合。…

查看全部问答>