嵌入式Linux设备驱动开发之:按键驱动程序实例
11.6按键驱动程序实例
11.6.1按键工作原理
LED和蜂鸣器是最简单的GPIO的应用,都不需要任何外部输入或控制。按键同样使用GPIO接口,但按键本身需要外部的输入,即在驱动程序中要处理外部中断。按键硬件驱动原理图如图11-7所示。在图11-7的4×4矩阵按键(K1~K16)电路中,使用4个输入/输出端口(EINT0、EINT2、EINT11和EINT19)和4个输出端口(KSCAN0~KSCAN3)。
图11.7按键驱动电路原理图
按键驱动电路使用的端口和对应的寄存器如表11-18所示。
表11.18 按键电路的主要端口
管脚 | 端口 | 输入/输出 | 管脚 | 端口 | 输入/输出 | |
KEYSCAN0 | GPE11 | 输出 | EINT0 | EINIT0/GPF0 | 输入/输出 | |
KEYSCAN1 | GPG6 | 输出 | EINT2 | EINT2/GPF2 | 输入/输出 | |
KEYSCAN2 | GPE13 | 输出 | EINT11 | EINT11/GPG3 | 输入/输出 | |
KEYSCAN3 | GPG2 | 输出 | EINT19 | EINT19/GPG11 | 输入/输出 |
因为通常中断端口是比较珍贵且有限的资源,所以在本电路设计中,16个按键复用了4个中断线。那怎么样才能及时而准确地对矩阵按键进行扫描呢?
某个中断的产生表示,与它所对应的矩阵行的4个按键中,至少有一个按键被按住了。因此可以通过查看产生了哪个中断,来确定在矩阵的哪一行中发生了按键操作(按住或释放)。例如,如果产生了外部2号线中断(EINT2变为低电平),则表示K7、K8、K9和K15中至少有一个按键被按住了。这时候4个EINT端口应该通过GPIO配置寄存器被设置为外部中断端口,而且4个KSCAN端口的输出必须为低电平。
在确定按键操作所在行的位置之后,我们还得查看按键操作所在列的位置。此时要使用KSCAN端口组,同时将4个EINT端口配置为通用输入端口(而不是中断端口)。在4个KSCAN端口中,轮流将其中某一个端口的输出置为低电平,其他3个端口的输出置为高电平。这样逐列进行扫描,直到按键所在列的KSCAN端口输出为低电平,此时按键操作所在行的EINT管脚的输入端口的值会变成低电平。例如,在确认产生了外部2号中断之后,进行逐列扫描。若发现在KSCAN1为低电平时(其他端口输出均为高电平),GPF2(EINT2管脚的输入端口)变为低电平,则可以断定按键K8被按住了。
以上的讨论都是在按键的理想状态下进行的,但实际的按键动作会在短时间(几毫秒至几十毫秒)内产生信号抖动。例如,当按键被按下时,其动作就像弹簧的若干次往复运动,将产生几个脉冲信号。一次按键操作将会产生若干次按键中断,从而会产生抖动现象。因此驱动程序中必须要解决去除抖动所产生的毛刺信号的问题。
11.6.2按键驱动程序
首先按键设备相关的数据结构的定义如下所示:
/*butt_drv.h*/
……
typedefstruct_st_key_info_matrix/*按键数据结构*/
{
unsignedcharkey_id;/*按键ID*/
unsignedintirq_no;/*对应的中断号*/
unsignedintirq_gpio_port;/*对应的中断线的输入端口地址*/
unsignedintkscan_gpio_port;/*对应的KSCAN端口地址*/
}st_key_info_matrix;
typedefstruct_st_key_buffer/*按键缓冲数据结构*/
{
unsignedlongjiffy[MAX_KEY_COUNT];/*按键时间,5s以前的铵键作废*/
unsignedcharbuf[MAX_KEY_COUNT];/*按键缓冲区*/
unsignedinthead,tail;/*按键缓冲区头和尾*/
}st_key_buffer;
……
下面是矩阵按键数组的定义,数组元素的信息(一个按键信息)按照0行0列,0行1列,…,3行2列,3行3列的顺序逐行排列。
staticst_key_info_matrixkey_info_matrix[MAX_COLUMN][MAX_ROW]=
{
{{10,IRQ_EINT0,S3C2410_GPF0,S3C2410_GPE11},/*0行0列*/
{11,IRQ_EINT0,S3C2410_GPF0,S3C2410_GPG6},
{12,IRQ_EINT0,S3C2410_GPF0,S3C2410_GPE13},
{16,IRQ_EINT0,S3C2410_GPF0,S3C2410_GPG2}},
{{7,IRQ_EINT2,S3C2410_GPF2,S3C2410_GPE11},/*1行0列*/
{8,IRQ_EINT2,S3C2410_GPF2,S3C2410_GPG6},
{9,IRQ_EINT2,S3C2410_GPF2,S3C2410_GPE13},
{15,IRQ_EINT2,S3C2410_GPF2,S3C2410_GPG2}},
{{4,IRQ_EINT11,S3C2410_GPG3,S3C2410_GPE11},/*2行0列*/
{5,IRQ_EINT11,S3C2410_GPG3,S3C2410_GPG6},
{6,IRQ_EINT11,S3C2410_GPG3,S3C2410_GPE13},
{14,IRQ_EINT11,S3C2410_GPG3,S3C2410_GPG2}},
{{1,IRQ_EINT19,S3C2410_GPG11,S3C2410_GPE11},/*3行0列*/
{2,IRQ_EINT19,S3C2410_GPG11,S3C2410_GPG6},
{3,IRQ_EINT19,S3C2410_GPG11,S3C2410_GPE13},
{13,IRQ_EINT19,S3C2410_GPG11,S3C2410_GPG2}},
};
下面是与按键相关的端口的初始化函数。这些函数已经在简单的GPIO字符设备驱动程序里被使用过。此外,set_irq_type()函数用于设定中断线的类型,在本实例中通过该函数将4个中断线的类型配置为下降沿触发式。
staticvoidinit_gpio(void)
{
s3
嵌入式Linux 设备驱动 按键驱动程序 操作系统 相关文章:
- 嵌入式Linux技术在工业控制网络中的应用(10-30)
- 基于嵌入式Linux的组态软件实时数据库的设计(02-01)
- 基于ARM+DSP的嵌入式Linux数控系统设计(11-18)
- 基于嵌入式Linux的细胞特征提取算法设计(11-19)
- 基于S3C2410的嵌入式Linux系统构建(03-02)
- 嵌入式Linux网络编程之:网络基础编程(08-13)