实现了多键齐按和重复按键的嵌入式系统键盘驱动设计
- 格式:doc
- 大小:21.50 KB
- 文档页数:5
实现了多键齐按和重复按键的嵌入式系统键盘驱动设计1 键盘驱动程序的设计随着电子信息技术飞速发展,嵌入式系统构成的各种设备得到了广泛的应用,嵌入式 Linux是一种开放源码、软实时、多任务的操作系统,是开发嵌入式产品的优秀操作系统平台,其中键盘是人机界面中人类监控计算机重要数据输入设备。
实现键盘有两种方法:一种是采用现有的一些芯片实现键盘扫描;二是用软件实现键盘扫描。
目前许多芯片可用来实现键盘扫描,但是键盘扫描的软件实现方法有助于缩减系统的重复开发成本,而只需很少的 CPU 开销。
嵌入式控制器的功能很强,可以充分利用这一资源。
本课题提出的键盘方案是以嵌入式 Linux和PXA255为软硬件平台,通过测试,表明其具有良好的稳定性和实时性。
2 矩阵式键盘的结构与工作原理本课题采用矩阵键盘,如图 1所示。
四根行线四根列线组成 4 *4矩阵键盘,分别用 CPU 的 4个GPIO口。
当有键按下,某个列 GPI O 口电平被下拉从而产生下降沿,触发中断。
其中按键行阵列必须提供上拉信号,列阵列加二极管,防止瞬间电流过大对 GPI O口造成冲击。
图 1 矩阵键盘原理图。
3 Linux键盘驱动简介在 Linux中,键盘驱动被划分成两层来实现。
上层是一个通用键盘抽象层,下层则是硬件处理层,主要对硬件进行直接的操作。
键盘驱动程序上层公共部分在driver /keyboard 。
c里。
文件中最重要的是内核用 EXPORT _SYM BOL 这个宏导出的 handle_scancode函数。
在这个文件中还定义了其它的几个回调函数,它们由键盘驱动程序中上层公共部分调用,并且由底层硬件处理函数实现。
键盘驱动程序的底层硬件处理部分则根据不同硬件有不同实现。
4 键盘驱动程序的实现 4.1 宏定义 module init和 module exit 通过宏定义 module init和module exit可以看出,驱动程序的入口从 kd_ctrl_init()开始。
嵌入式系统的多功能智能遥控器设计与实现嵌入式系统的多功能智能遥控器是一种集成多种功能的智能设备,它能够实现对不同电子设备的遥控操作,例如电视机、空调、音响等。
本文将介绍嵌入式系统的多功能智能遥控器的设计与实现过程。
首先,嵌入式系统的多功能智能遥控器的设计需要确定所支持的设备种类以及对应的操作方式。
通过用户调查和市场调研,可以确定要支持的电子设备种类,例如电视机、DVD播放器、机顶盒等。
然后,根据各个设备的遥控操作原理和协议,确定对应的操作方式。
这些操作方式可以包括红外遥控信号发射和接收、蓝牙通信、WIFI通信等,根据不同设备的特点选择合适的通信方式。
其次,在设计多功能智能遥控器的硬件方面,需要选择适当的处理器和外围组件。
处理器是嵌入式系统的核心,可以选择ARM、MIPS等常用的嵌入式处理器,通过其提供的GPIO、串口、SPI等接口与各个设备进行通信。
外围组件可以包括红外发射和接收模块、蓝牙模块、WIFI模块等,根据操作方式的选择,适当添加相应的外围组件。
在软件方面,多功能智能遥控器的操作界面需要友好易用。
可以通过图形用户界面(GUI)设计实现用户对设备的快速操作。
为了方便用户选择设备和操作,可以采用下拉列表、图标等直观的方式呈现。
此外,还可以设置收藏夹、预设场景等功能,让用户可以快速调用自己常用的设备和操作组合。
同时,为了确保智能遥控器的灵活性和可扩展性,还可以提供一个配置界面,允许用户自定义设备和操作方式。
在实现过程中,需要为每个设备编写相应的操作代码。
这些代码可以通过学习设备的红外信号或者通过逆向工程获取。
对于支持蓝牙或者WIFI通信的设备,还需要编写与设备通信的协议栈和驱动程序。
所有的操作代码需要通过嵌入式系统的处理器进行控制和执行。
最后,设计完成后,需要对多功能智能遥控器进行测试和优化。
测试可以包括对各个设备的操作测试、信号传输和接收的测试等。
通过测试,可以发现潜在的问题和改进的空间。
在优化方面,可以对操作界面进行优化,增加一些快捷键等功能,提升用户体验。
嵌入式按键的实现方法因具体的硬件和软件环境而异,但通常涉及以下步骤:1. 硬件连接:将按键连接到嵌入式系统的电路板上。
这可以通过焊接或使用排针和电缆来完成。
通常,按键的一端是连接电源的公共端,另一端连接到电路板上的数字输入引脚。
2. 初始化硬件:在嵌入式系统中配置所需的硬件接口,以便能够读取和处理按键输入。
这可能涉及到设置GPIO(通用输入/输出)引脚的模式(例如,输入或输出),并启用适当的中断或DMA(直接内存访问)通道。
3. 按键识别:嵌入式系统通过检测特定引脚的电平变化来识别按键按下或释放。
这通常涉及使用中断或轮询来检查特定引脚的电压状态。
如果按键按下,该引脚的电压将降低或变为低电平。
4. 按键处理:一旦检测到按键按下,嵌入式系统需要识别按键的位置(按下的是哪一个键)并执行相应的操作。
这可能涉及激活菜单、发送信号到其他设备、记录数据或执行其他与用户交互的任务。
在处理按键后,嵌入式系统通常会重新设置引脚状态以指示按键已被释放。
5. 特殊按键处理:对于特殊按键(如锁定或解锁按钮),嵌入式系统可能需要配置额外的硬件或软件功能来识别和处理这些按键。
例如,可以使用额外的LED灯或传感器来检测特殊按键的状态,或者在软件中实现特定的识别算法。
6. 校准和测试:为了确保按键的正常工作,需要进行适当的校准和测试。
这可能涉及在开发环境中模拟按键按下和释放的测试,以确保系统能够正确识别和处理这些操作。
除了上述基本步骤外,嵌入式按键的实现还可能涉及其他因素,如按键的机械和电气特性、电路板的布局和布线、电源管理、软件优化等。
因此,具体的实现方法可能因嵌入式系统和应用场景而异。
请注意,上述步骤仅提供了一个大致的指导,并不适用于所有嵌入式系统和应用场景。
在实际应用中,可能需要根据具体情况进行调整和优化。
敬请登录网站在线投稿 2013年第2期 21基于嵌入式Linux系统的键盘驱动设计徐德龙,余瑾(北京邮电大学,北京100876)摘要:为了适应嵌入式设备外设的多样性,本文以特殊矩阵键盘为例,设计了一套完整的驱动控制模块。
硬件电路设计采用外扩3片SN74HC164芯片的方式,节省了GPIO引脚的使用,大大提高了利用效率。
同时,在此基础上引出了Linux内核中input子系统的特性和工作机制,呈现了较为完整的输入事件由内核空间传递到用户空间进程的过程。
实验结果表明,设计的驱动模块具有良好的实时性和准确性。
关键词:SN74HC164;矩阵键盘;驱动控制模块;Linux内核;input子系统中图分类号:TP39 文献标识码:AKeyboard Driver Design Based on Embedded LinuxXu Delong,Yu Jin(Beijing University of Posts and Telecommunications,Beijin 100876,China)Abstract:In order to adapt the diversity of embedded device peripheral,this paper takes special matrix keyboard as an example,and acomplete set of drive control module is designed.Hardware circuit design takes 3expanding SN74HC164chips to save the GPIO pins,and it greatly improves the utilization efficiency.Meanwhile,on the basis it leads to the characteristics and working mechanism of inputsubsystem in Linux kernel,presenting a complete process of input events from kernel space to user space.The experimental resultsshow that,the driving module has good performance in real-time and accuracy.Key words:SN74HC164;matrix keyboard;driver control module;Linux kernel;input subsystem引 言随着微处理器技术的不断发展和数字化产品的普及,嵌入式系统的研究开发逐渐成为热点,Linux也以其开源、稳定、可裁剪的优势成为嵌入式操作系统的主流。
一、实验目的1. 理解键盘驱动程序的基本原理和设计流程。
2. 掌握键盘扫描矩阵的原理和实现方法。
3. 学习使用C语言进行键盘扫描驱动程序的开发。
4. 提高嵌入式系统开发能力和实际动手能力。
二、实验环境1. 开发平台:北邮嵌入式实验室提供的STM32开发板。
2. 编译工具:Keil uVision 5。
3. 实验软件:嵌入式Linux操作系统。
三、实验原理键盘扫描矩阵是一种常用的键盘输入方式,它通过行和列的交叉来检测按键的状态。
当按键被按下时,行和列的交叉点会形成一个特定的逻辑地址,该地址对应于键盘上的一个按键。
在嵌入式系统中,键盘驱动程序负责扫描键盘矩阵,识别按键状态,并将按键信息传递给上层应用程序。
本实验中,我们将使用C语言开发键盘驱动程序,实现以下功能:1. 初始化键盘硬件资源。
2. 扫描键盘矩阵,识别按键状态。
3. 将按键信息转换为ASCII码或其他编码格式。
4. 通过中断或轮询方式将按键信息传递给上层应用程序。
四、实验步骤1. 硬件连接将STM32开发板与键盘模块连接,确保键盘模块的行和列引脚正确连接到开发板的GPIO引脚。
2. 编写键盘驱动程序(1)初始化键盘硬件资源在驱动程序中,首先需要初始化键盘硬件资源,包括设置GPIO引脚的模式、上拉/下拉电阻等。
```cvoid keyboard_init(void) {// 设置GPIO引脚模式为输出GPIO_InitTypeDef GPIO_InitStruct = {0};__HAL_RCC_GPIOA_CLK_ENABLE();GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);// 设置GPIO引脚模式为输入__HAL_RCC_GPIOB_CLK_ENABLE();GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_PULLUP;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);}```(2)扫描键盘矩阵在驱动程序中,编写一个函数用于扫描键盘矩阵,识别按键状态。
嵌入式Linux系统的键盘驱动实现1引言Linux由于其具有内核强大且稳定,易于扩展和裁减,丰富的硬件支持等诸多优点,在嵌入式系统中得到了广泛的应用。
很多嵌入式Linux系统,特别是一些具有与用户强交互的嵌入式系统,往往需要配备一个特殊键盘,此时开发者需要根据实际情况,为自己的特殊键盘编写驱动程序。
2Linux键盘驱动简介Linux中的大多数驱动程序都采用了层次型的体系结构,键盘驱动程序也不例外。
在Linux中,键盘驱动被划分成两层来实现。
其中,上层是一个通用的键盘抽象层,完成键盘驱动中不依赖于底层具体硬件的一些功能,并且负责为底层提供服务;下层则是硬件处理层,与具体硬件密切相关,主要负责对硬件进行直接操作。
键盘驱动程序的上层公共部分都在driver/keyboard.c中。
该文件中最重要的就是内核用EXPORT_SYMBOL这个宏导出的handle_scancode函数。
handle_scancode 完成的功能是:首先将扫描码转换成键码,接着根据shift,alt等扩展键的按下情况将键码转换成目标码,一般情况下是ASCII码,最后将该ASCII码放到终端设备的缓冲区中,并且调度一个tasklet负责将其在显示器上回显出来。
可以看出,这个函数完成的是键盘驱动程序中最核心的一些工作,而这些核心的逻辑功能是不依赖于底层硬件的,所以可以将其独立出来,并且导出给底层的硬件处理函数调用。
在这个文件中还定义了其它几个回调函数,它们由键盘驱动程序中的上层公共部分调用,并由底层硬件处理函数实现。
比如kbd_init_hw,kbd_translate, kbd_unexpected_up等等。
其中kbd_translate由handle_scancode调用,负责将扫描码转换成键码;键盘驱动程序的底层硬件处理部分则根据不同的硬件有不同的实现。
例如PC平台上标准键盘的底层硬件处理函数都集中在driver/Pc_keyb.c中。
Qt_Embedded环境下嵌⼊式键盘驱动的实现—254— Qt/Embedded 环境下嵌⼊式键盘驱动的实现张萍,徐晶(华中科技⼤学电⼦信息与⼯程系,武汉 430074)摘要:描述了基于嵌⼊式Linux 和Qt/Embedded 的⼿持终端的键盘驱动的设计与实现。
依据嵌⼊式⼿持设备键盘的⼯作特点及其驱动实现难点,采⽤了⼀种将键盘驱动分解成底层驱动模块和上层⽂本输⼊模块的双层设计⽅案,且在实际设备中予以实现。
关键词:嵌⼊式终端;Linux ;Qt/Embedded ;键盘驱动;⽂本输⼊Implementation of Embedded Keyboard Driverin Qt/Embedded EnvironmentZHANG Ping, XU Jing(Electronic and Information Department, Huazhong University of Science and Technology, Wuhan 430074)【Abstract 】This article discusses the design and implementation of keyboard device driver based on embedded Linux and Qt/Embedded. Based on the working process and keyboard driver characteristics, the article divides the keyboard driver into two modules: bottom device driver module and upper text input module.【Key words 】Embedded device; Linux; Qt/embedded; Keyboard driver; Text input计算机⼯程Computer Engineering 第33卷第11期Vol.33 No.11 2007年6⽉June 2007·开发研究与设计技术·⽂章编号:1000—3428(2007)11—0254—02⽂献标识码:A中图分类号:TP334嵌⼊式系统(Embedded System)⽆疑是当今最热门的概念之⼀。
C语言嵌入式系统编程修炼之五键盘操作键盘操作在嵌入式系统中是非常常见和重要的一项功能。
通过键盘操作,我们可以与嵌入式系统进行交互,实现一些基本的功能,如控制LED 灯的亮灭、调整音量等。
本文将介绍如何在C语言中实现键盘操作。
在嵌入式系统中,通常会使用外部键盘模块来实现键盘操作。
外部键盘模块会通过一些IO口与嵌入式系统连接,当按下键盘上的按键时,会通过IO口发送一个信号给嵌入式系统,嵌入式系统通过读取IO口的状态来获取按键信息。
首先,我们需要配置IO口的工作模式。
在大多数的嵌入式系统中,IO口可以设置为输入模式或输出模式。
对于键盘操作来说,我们需要将IO口设置为输入模式。
可以通过设置相应的寄存器或调用相应的库函数来实现。
接下来,我们需要在程序中不断地读取IO口的状态,以获取按键信息。
可以使用轮询的方式,即不断地读取IO口的状态,当IO口的状态发生变化时,说明有按键被按下。
也可以使用中断的方式,即当IO口的状态发生变化时,触发一个中断,中断服务程序中读取IO口的状态来获取按键信息。
当获取到按键信息后,我们可以根据不同的按键来执行不同的操作。
可以使用if语句或switch语句来进行判断,根据不同的按键执行相应的代码。
例如,当按下一些按键时,可以控制LED灯的亮灭,当按下另一个按键时,可以调整音量等。
在进行键盘操作时,还需要考虑一些其他的因素。
例如,按键抖动问题。
由于按键的机械性质,当按键被按下时,会出现抖动现象,即按键会在按下和松开的过程中多次切换状态。
为了避免这种问题,我们可以在程序中添加一定的延时操作,当读取到IO口的状态发生变化后,再等待一段时间,再次读取IO口的状态,观察IO口的状态是否稳定。
另外,还需要考虑多个按键同时按下的情况。
在处理多个按键同时按下的情况时,可以使用一个变量来保存当前按下的按键信息,然后在程序中进行相应的判断和处理。
总结来说,键盘操作在嵌入式系统中是非常重要的一项功能。
通过键盘操作,我们可以与嵌入式系统进行交互,实现一些基本的功能。
linux和qtopia下的矩阵键盘驱动程序基于s3c2440和linux,实现了3*4的矩阵键盘驱动。
功能:延时消抖,重复按键,多键齐按(??)更详细的说明文档:“基于S3C24440和嵌入式Linux的矩阵键盘设计”,电子技术,2008,45(5):21-23/*********************************************************** s3c2440-keyboard.c** keyboard driver for S3C2440 based PDA*** History:2007/04/30*************************************************************/#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/miscdevice.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/poll.h>#include <linux/spinlock.h>#include <asm/irq.h>#include <asm/arch/irq.h>#include <asm/arch/irqs.h>#include <asm/arch/clocks.h>#include <asm/hardware.h>#include <asm/arch/S3C2440.h>#define DEVICE_NAME "s3c2440-kb" //键盘设备名static int kbMajor = 0; //默认的主设备号#define MAX_KB_BUF 10 //循环队列的尺寸typedef struct {unsigned int keyStatus;int irq;// int timeCount;u_short buf[MAX_KB_BUF]; /* 循环队列*/unsigned int head, tail; /* 循环队列的读写指针*/spinlock_t lock; /*锁*/} KB_DEV;static KB_DEV kbdev;#define KEY_UP 0 //按键弹起#define KEY_DOWN 1 //按键按下#define NO_KEY_DOWN 2 //没有键按下#define EINT1_DOWN 0#define EINT3_DOWN 1#define EINT8_DOWN 2#define NO_EINT_DOWN 3/*循环队列操作*/#define BUF_HEAD (kbdev.buf[kbdev.head])#define BUF_TAIL (kbdev.buf[kbdev.tail])#define INCBUF(x) if((++x)==MAX_KB_BUF) x=0/*定时器设置*/#define KB_TIMER_DELAY (HZ/50) /*HZ表示每秒产生的时钟滴答数,定时器超时为20ms*/#define REPEAT_START_DELAY (HZ) /* 自动重复开始延时:1秒*/ #define REPEAT_DELAY (HZ/2) /*自动重复延时:0.5秒*/static struct timer_list kb_timer;static struct timer_list repeat_timer;spinlock_t repeat_lock;static int timeCount =1;static int TIME_OUT =5;/*键盘矩阵*/static u_short keyboard_code_map[4][3]={{1 , 2 , 3 },{4 , 5 , 6 },{7 , 8 , 9 },{10, 11, 12}}; //每个按键对应的键盘码static u_short pre_keyboard_code = 0; //上次扫描得到的按键值static u_short curr_keyboard_code = 0;//当前扫描得到的按键值static u_short snap_keyboard_code[4][3]={{0 , 0 , 0 },{0 , 0 , 0 },{0 , 0 , 0 },{0, 0, 0}}; //临时按键值#define DETECTION_THROLD 3static int requestIrq();static int s3c2440_kb_release(struct inode *inode, struct file *filp);/*----------------------------------------------------* func: 初始化GPJCON寄存器,将GPJ9,GPJ10,GPJ11,GPJ12* 配置成output管腿*------------------------------------------------------*/static void init_gpjcon(){//GPJ9,GPJ10,GPJ11,GPJ12------>outputGPJCON &= 0xFC03FFFF;GPJCON |= 0x01540000;}/*----------------------------------------------------* func: 向GPJ9,GPJ10,GPJ11,GPJ12输出value* param:* value: 输出值*------------------------------------------------------*///static inline void output_giop(int value ) //往所有行输出{value &= 0x0000000F;value <<= 9;GPJDAT &= 0xE1FF;GPJDAT |= value;udelay(2);}/*----------------------------------------------------* func: 判断eint当前是否是低电平* param:* irq: 当前引发中断的eint的irq号* return:* EINT1_DOWN: eint1上是低电平* EINT3_DOWN: eint3上是低电平* EINT8_DOWN: eint8上是低电平* NO_EINT_DOWN:eint上不是低电平------------------------------------------------------*/int get_eint_value(int irq){u_int IOValue;IOValue = GPFDAT ;if( (irq == 1) && (( IOValue & 0x00000002)==0) ) {return EINT1_DOWN;}if((irq ==3 ) && (( IOValue & 0x00000008)==0) ) {return EINT3_DOWN;}IOValue = GPGDAT ;if((irq ==36) && (( IOValue & 0x0000001)==0) ) {return EINT8_DOWN;}return NO_EINT_DOWN;}/*----------------------------------------------------* func: 扫描键盘,判断哪一个键被按下* param:* x: 得到按键的行号* y: 得到按键的列号* return:* KEY_DOWN: 键盘上有键被按下* NO_KEY_DOWN: 键盘上没有键被按下------------------------------------------------------*/static inline int scan_keyboard(int* x,int* y) {int matrix_row,matrix_col,matrix_col_status; output_giop(0xF); //往所有行输出1//判断按键在哪一行matrix_row=matrix_col=-1;output_giop(0xE);//在第1行上输出1,其余行输出0 matrix_col_status = get_eint_value(kbdev.irq);if(matrix_col_status != NO_EINT_DOWN){matrix_row = 0;matrix_col = matrix_col_status;goto scanend;}output_giop(0xD);//在第2行上输出1,其余行输出0matrix_col_status = get_eint_value(kbdev.irq);if(matrix_col_status != NO_EINT_DOWN){matrix_row=1;matrix_col = matrix_col_status;goto scanend;}output_giop(0xB);//在第3行上输出1,其余行输出0 matrix_col_status =get_eint_value(kbdev.irq);if(matrix_col_status != NO_EINT_DOWN){matrix_row=2;matrix_col = matrix_col_status;goto scanend;}output_giop(0x7);//在第4行上输出1,其余行输出0 matrix_col_status =get_eint_value(kbdev.irq);if(matrix_col_status != NO_EINT_DOWN){matrix_row=3;matrix_col = matrix_col_status;goto scanend;}scanend:output_giop(0);if(matrix_row >=0 ){snap_keyboard_code[matrix_row][matrix_col_status]=snap_keyboard_code[matrix_row][matrix_col_status] + 1;if(snap_keyboard_code[matrix_row][matrix_col]>=DETECTION_THROLD) {*x=matrix_row;*y=matrix_col;curr_keyboard_code = keyboard_code_map[matrix_row][matrix_col]; return KEY_DOWN;}}return NO_KEY_DOWN;}/*----------------------------------------------------* func: 判断本次按键是否与上次按键相同* param:** return:* 0: 相同* 1: 不同------------------------------------------------------*/static inline int key_changed(){return (pre_keyboard_code == curr_keyboard_code)? 0 : 1; }/*----------------------------------------------------* func: 将按键对应的键盘码保存到循环队列中* param:* keyValue: 按键的对应的键盘码* return:*------------------------------------------------------*/static inline void save_key_to_queue(u_short keyValue) {if (kbdev.keyStatus == KEY_DOWN){BUF_HEAD = keyValue;INCBUF(kbdev.head);//wake_up_interruptible(&(kbdev.wq));}}/*----------------------------------------------------* func: 重复按键定时器处理程序,如果一直按住某键,则将该键的键盘码定时存到循环队列中* param:* data: 无参数* return:*------------------------------------------------------*/static inline void repeat_timer_handler(unsigned long data){spin_lock_irq(&(repeat_lock));if(kbdev.keyStatus ==KEY_DOWN){repeat_timer.expires = jiffies + REPEAT_DELAY;//设置自动重复延时add_timer(&repeat_timer);//将定时器加入队列if(pre_keyboard_code != 0){//printk("repeat save keyvalue\n %d",pre_keyboard_code);save_key_to_queue(pre_keyboard_code);//将按键值存入循环队列}}else//如果按键弹起{//del_timer(&repeat_timer);// printk("del repeat timer\n");}spin_unlock_irq(&(repeat_lock));}/*----------------------------------------------------* func: 使能中断* param:* return:*------------------------------------------------------*///使能中断static inline void enableIrq(){//清除SRCPND寄存器中eint1 eint2 eint8相应位SRCPND = 0x0000002A;//使能中断enable_irq(IRQ_EINT1);enable_irq(IRQ_EINT3);enable_irq(IRQ_EINT8);}/*----------------------------------------------------* func: 键盘定时扫描程序,如果得到稳定键码,将键码存* 入循环队列;如果没有,则延时20ms后继续扫描* param:* data: 无参数* return:*------------------------------------------------------*/static inline void kb_timer_handler(unsigned long data){int x,y;spin_lock_irq(&(kbdev.lock));x = y = 0;if(scan_keyboard(&x,&y) == KEY_DOWN){// printk("snap_keyboard_code=%d, %d, %d, %d\n",snap_keyboard_code[0][1],snap_keyboard_code[1][1],snap_keyboard_c ode[2][1],snap_keyboard_code[3][1]);kbdev.keyStatus =KEY_DOWN;if(key_changed()){pre_keyboard_code = curr_keyboard_code;save_key_to_queue(pre_keyboard_code);//printk("KEY_DOWN:%d x=%d y =%d\n",timeCount,x,y);//设置自动重复开始延时定时器/*repeat_timer.expires = jiffies + REPEAT_START_DELAY;add_timer(&repeat_timer);*/}timeCount=1;memset(snap_keyboard_code,0,12*sizeof(u_short));//curr_keyboard_code =0;kb_timer.expires = jiffies + KB_TIMER_DELAY;add_timer(&kb_timer);}else{//printk("snap_keyboard_code=%d, %d, %d, %d\n",snap_keyboard_code[3][0],snap_keyboard_code[3][1],snap_keyboard_c ode[3][2],snap_keyboard_code[3][3]);kb_timer.expires = jiffies + KB_TIMER_DELAY;add_timer(&kb_timer);//printk("timeCount:%d\n",timeCount);if (timeCount==TIME_OUT) //扫描5次后仍然没有得到稳定键值{//复位计数器timeCount=1;kbdev.keyStatus =KEY_UP;//使能中断enableIrq();//关闭定时器del_timer(&kb_timer);del_timer(&repeat_timer);//printk("enable irq \n\n\n");curr_keyboard_code = 0;pre_keyboard_code= 0 ;memset(snap_keyboard_code,0,12*sizeof(u_short));elsetimeCount++;}spin_unlock_irq(&(kbdev.lock));}/*----------------------------------------------------* func: 从循环队列中读取按键的键码* param:* return: 按键的键码*------------------------------------------------------*/static inline int kbRead(){u_short keyvalue;spin_lock_irq(&(kbdev.lock));keyvalue = BUF_TAIL;INCBUF(kbdev.tail );spin_unlock_irq(&(tsdev.lock));return keyvalue;}/*----------------------------------------------------* func: 对应文件读的函数,如果循环队列中有键码, 则将键码拷贝到用户空间的buffer中* param:** return:* 返回从循环队列中读取的键码的字节数**------------------------------------------------------*/static ssize_tS3C2440_kb_read(struct file *filp, char *buffer, size_t count, loff_t * ppos) {u_short keyvalue;if(kbdev.head == kbdev.tail){return 0;}else{keyvalue = kbRead();count = sizeof(keyvalue);/*将数据拷贝到用户空间*/copy_to_user(buffer,&(keyvalue),count);return count;}}/*----------------------------------------------------* func: 与打开文件对应的open函数,初始化全局变量和定* 时器以及请求中断* param:*** return:*------------------------------------------------------*/static int S3C2440_kb_open(struct inode *inode, struct file *filp) {kbdev.keyStatus = KEY_UP;kbdev.head=kbdev.tail = 0;kbdev.lock = SPIN_LOCK_UNLOCKED;repeat_lock = SPIN_LOCK_UNLOCKED;output_giop(0);//初始化定时器init_timer(&kb_timer);kb_timer.function = kb_timer_handler;//初始化重复按键定时器init_timer(&repeat_timer);repeat_timer.function = repeat_timer_handler;/*if(requestIrq() !=0)return -1;*/enableIrq();MOD_INC_USE_COUNT;return 0;}static struct file_operations kb_fops = {owner: THIS_MODULE,open: S3C2440_kb_open,read: S3C2440_kb_read,release: s3c2440_kb_release,};/*----------------------------------------------------* func: 中断处理程序,关闭中断开启键盘扫描定时器* param:*** return:*------------------------------------------------------*/static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) {spin_lock_irq(&kbdev.lock);//禁止所有中断disable_irq(IRQ_EINT1);disable_irq(IRQ_EINT3);disable_irq(IRQ_EINT8);kbdev.irq = irq;//printk("irq=%d\n",kbdev.irq);//启动定时器kb_timer.expires = jiffies + KB_TIMER_DELAY;add_timer(&kb_timer);repeat_timer.expires = jiffies + REPEAT_START_DELAY;add_timer(&repeat_timer);spin_unlock_irq(&kbdev.lock);}/*----------------------------------------------------* func: 初始化eint中断相关寄存器,安装中断处理程序* param:*** return:*------------------------------------------------------*/static int requestIrq(){int ret;/* Enable interrupt *///==================================================// irq: Linux中断号,与硬件中断号不同// handle: 中断处理程序// flag: SA_INTERRUPT指示这是个快速中断处理程序// dev_id: 用于共享的中断信号线,通常设置成NULL//===================================================ret =set_external_irq(IRQ_EINT1,EXT_FALLING_EDGE,GPIO_PULLUP_DIS); if(ret)goto eint_failed ;ret = request_irq(IRQ_EINT1, keyboard_interrupt, SA_INTERRUPT, DEVICE_NAME, NULL);if(ret)goto eint1_failed;ret =set_external_irq(IRQ_EINT8,EXT_FALLING_EDGE,GPIO_PULLUP_DIS); //EXT_LOWLEVELif(ret)goto eint_failed;ret = request_irq(IRQ_EINT8, keyboard_interrupt, SA_INTERRUPT, DEVICE_NAME, NULL);if(ret)goto eint8_failed;ret =set_external_irq(IRQ_EINT3,EXT_FALLING_EDGE,GPIO_PULLUP_DIS); if(ret)goto eint_failed;ret = request_irq(IRQ_EINT3, keyboard_interrupt, SA_INTERRUPT, DEVICE_NAME, NULL);if(ret)goto eint3_failed;return 0;eint3_failed:free_irq(IRQ_EINT3, keyboard_interrupt);eint8_failed:free_irq(IRQ_EINT8, keyboard_interrupt);eint1_failed:free_irq(IRQ_EINT1, keyboard_interrupt);eint_failed:printk(DEVICE_NAME ": IRQ Requeset Error\n");return ret;}static int s3c2440_kb_release(struct inode *inode, struct file *filp){/*注销设备*/// unregister_chrdev(kbMajor, DEVICE_NAME);/*释放中断*//*free_irq(IRQ_EINT1,NULL);free_irq(IRQ_EINT8,NULL);free_irq(IRQ_EINT3,NULL);MOD_DEC_USE_COUNT;*/return 0;}/*----------------------------------------------------* func: 初始化键盘驱动,注册字符设备* param:*** return:>=0 : 初始化键盘驱动成功<0: 失败*------------------------------------------------------*/static int __init s3c2440_kb_init(void){int ret;/*初始化管腿配置*/init_gpjcon();output_giop(0);/*注册设备*/ret = register_chrdev(99, DEVICE_NAME, &kb_fops); if(ret < 0) {printk(DEVICE_NAME " can't get major number\n"); return ret;}kbMajor = ret;printk("%s: major number=99\n",DEVICE_NAME);requestIrq();//暂时禁止所有中断,等到open时再打开disable_irq(IRQ_EINT1);disable_irq(IRQ_EINT3);disable_irq(IRQ_EINT8);return 0;}/*----------------------------------------------------* func: 注销字符设备,释放中断* param:*** return:*------------------------------------------------------*/static void __exit s3c2440_kb_exit(void){/*注销设备*/unregister_chrdev(kbMajor, DEVICE_NAME); printk("exit\n");/*释放中断*/free_irq(IRQ_EINT1,NULL);free_irq(IRQ_EINT8,NULL);free_irq(IRQ_EINT3,NULL);}module_init(s3c2440_kb_init);module_exit(s3c2440_kb_exit);//EXPORT_SYMBOL(s3c2440_kb_init);//EXPORT_SYMBOL(s3c2440_kb_exit);如果将此驱动和qtopia程序结合起来,需要修改qt源代码的qkeyboard_qws.cpp文件,添加对矩阵键盘的支持class QWSHPCButtonsHandler : public QWSKeyboardHandler {Q_OBJECTpublic:QWSHPCButtonsHandler();virtual ~QWSHPCButtonsHandler();bool isOpen() { return buttonFD > 0; }private slots:void readKeyboardData();private:QString terminalName;int buttonFD;struct termios newT, oldT;QSocketNotifier *notifier;};QWSHPCButtonsHandler::QWSHPCButtonsHandler() : QWSKeyboardHandler(){#ifdef QT_QWS_HPCterminalName = "/dev/keyboard";buttonFD = -1;notifier = 0;if ((buttonFD = open(terminalName, O_RDONLY | O_NDELAY, 0)) < 0) { qWarning("Cannot open %s\n", tin1());return;}notifier = new QSocketNotifier( buttonFD, QSocketNotifier::Read, this ); connect( notifier, SIGNAL(activated(int)),this,SLOT(readKeyboardData()) );#endif}QWSHPCButtonsHandler::~QWSHPCButtonsHandler(){#ifdef QT_QWS_HPCif ( buttonFD > 0 ) {::close( buttonFD );buttonFD = -1;}#endif}void QWSHPCButtonsHandler::readKeyboardData(){#ifdef QT_QWS_HPC//-----------port form ButtonDetect-begin-----------int tempint,i;unsigned short buffer[1];tempint=0;int current_press=-1;do{tempint=read(buttonFD,buffer,1);if(tempint > 0 ){// printf("\nCurrent Press Buttom %d \n",buffer[0]);current_press = (int)buffer[1];goto set_hpckey;}}while(tempint >0);//-----------port form ButtonDetect-end-------------set_hpckey:int k=(-1);switch(current_press) {case 3: k=Qt::Key_Up; break; //case 11: k=Qt::Key_Down; break; //case 8: k=Qt::Key_Left; break; //case 6: k=Qt::Key_Right; break; //case 7: k=Qt::Key_Enter; break; // Entercase 4: k=Qt::Key_Escape; break; // Enter default: k=(-1); break;}if ( k >= 0 ){qwsServer->processKeyEvent( 0, k, 0, 1, false ); }#endif}。
嵌入式系统下按键操作的软件设计方法嵌入式系统键盘软件设计存在3方面问题:软件去抖动、等待按键抬起和连击处理。
1嵌入式系统键盘软件设计的3个问题1.1软件去抖动问题一次完整按键过程的时序波形如图1所示。
当按键未被按下时,单片机端口输入为通过上拉电阻获得的高电平;按下时,端口接至地,端口输入为低电平。
当机械触点断开、闭合时会有抖动,这种抖动对人来说是感觉不到的,但对计算机来说,则是完全可以感应到的。
计算机处理的速度是us级,而机械抖动的时间至少是ms级,对计算机而言,这已是漫长的时间了。
为使单片机能正确地读出端口的状态,对每一次按键只作一次响应,这就必须考虑如何去除抖动的问题。
嵌入式系统一般采用软件延时去除抖动。
软件延时去除抖动其实很简单,就是在单片机获得端口有按键动作时,不是立即认定按键开关已被按下,而是延时10 ms 或更长一段时间后再次检测端口,如果仍为动作电平,则说明按键开关的确按下了,这实际上是避开了按键按下时的抖动时间;而在检测到按键释放后(端口为高)再延时5~10 ms,消除后沿的抖动,然后再对键值处理。
当然,实际应用中对按键的要求也是千差万别,要根据不同的需要来编制处理程序,但以上是软件延时去除抖动的基本原则。
1.2等待按键抬起问题单片机在查询读取按键时,不断地扫描键盘,扫描到有键按下后,进行键值处理。
它并不等待键盘释放再退出键盘程序,而是直接退出键盘程序,返回主程序继续工作。
计算机系统执行速度快,很快又一次执行到键盘程序,并再次检测到键还处于按下的状态,单片机还会去执行键值处理程序。
这样周而复始,按一次按键系统会执行相应处理程序很多次。
而程序员的意图一般是只执行一次,这就是等待按键抬起问题。
通常的解决办法是,当按键抬起后再次按下才再次执行相应的处理程序,等待时间一般在几百ms以上。
通常在软件编程中,当执行完相应处理程序后,要加一个非常大的延时函数,再向下执行。
对于软件去抖动问题和等待按键抬起问题,若采用软件延时,会大大削弱系统的实时性;若采用中断方式延时,会占用定时器,耗费了系统资源,且软件的多任务编程会增大软件设计的复杂度。
基于嵌入式Linux系统键盘驱动的原理及实现
郑志国;赵万春
【期刊名称】《计算机系统应用》
【年(卷),期】2007(000)011
【摘要】本文基于Linux内核中键盘驱动程序的整体框架结构,介绍Linux键盘驱动的原理,并以MPC5200开发平台为例具体介绍嵌入式Linux系统中键盘驱动的实现.
【总页数】3页(P120-122)
【作者】郑志国;赵万春
【作者单位】中国电子科技集团公司第十研究所,成都,610036;总装重庆军代局驻成都地区军代室,成都,610036
【正文语种】中文
【中图分类】TP3
【相关文献】
1.基于龙芯2H的Linux矩阵键盘驱动的研究与实现 [J], 李泽银;龚俊;吴昌昊
2.基于嵌入式Linux系统的键盘驱动设计 [J], 徐德龙;余瑾
3.基于嵌入式Linux系统的设备驱动实现研究 [J], 李革梅;刘福岩
4.基于嵌入式Linux系统的高速设备驱动程序实现 [J], 徐煜;杨华
5.基于嵌入式Linux系统的LCD驱动实现 [J], 宫莉莉;赵勇
因版权原因,仅展示原文概要,查看原文内容请购买。
实现了多键齐按和重复按键的嵌入式系统键盘驱动设计
1 键盘驱动程序的设计随着电子信息技术飞速发展,嵌入式系统构成的各种设备得到了广泛的应用,嵌入式Linux是一种开放源码、软实时、多任务的操作系统,是开发嵌入式产品的优秀操作系统平台,其中键盘是人机界面中人类监控计算机重要数据输入设备。
实现键盘有两种方法:一种是采用现有的一些芯片实现键盘扫描;二是用软件实现键盘扫描。
目前许多芯片可用来实现键盘扫描,但是键盘扫描的软件实现方法有助于缩减系统的重复开发成本,而只需很少的CPU 开销。
嵌入式控制器的功能很强,可以充分利用这一资源。
本课题提出的键盘方案是以嵌入式Linux和PXA255为软硬件平台,通过测试,表明其具有良好的稳定性和实时性。
2 矩阵式键盘的结构与工作原理本课题采用矩阵键盘,如图1所示。
四根行线四根列线组成4 *4矩阵键盘,分别用CPU 的4个GPIO口。
当有键按下,某个列GPI O 口电平被下拉从而产生下降沿,触发中断。
其中按键行阵列必须提供上拉信号,列阵列加二极管,防止瞬间电流过大对GPI O口造成冲击。
图1 矩阵键盘原理图。
3 Linux键盘驱动简介在Linux中,键盘驱动被划分成两层来实现。
上层是一个通用键盘抽象层,下层则是硬件处理层,主要对硬件进行直接的操作。
键盘驱动程序上层公共部分在driver /keyboard 。
c里。
文件中最重要的是内核用EXPORT _SYM BOL这个宏导出的handle_scancode函数。
在这个文件中还定义了其它的几个回调函数,它们由键盘驱动程序中上层公共部分调用,并且由底层硬件处理函数实现。
键盘驱动程序的底层硬件处理部分则根据不同硬件有不同实现。
4 键盘驱动程序的实现4.1 宏定义module init和module exit通过宏定义module init和module exit可以看出,驱动程序的入口从kd_ctrl_init()开始。
当内核模块加载的时候,默认调用module_ i nit(kd_c trl_init),在kd_ctr l_ i nit()中将完成一些初始化工作,主要如下:。