键盘ledlcd的3个驱动程序,以WORD的格式打开,有设计思想,步骤,框图,为你学习带来方便
源代码在线查看: 7.5.2.txt
7.5.2 键盘驱动程序的实现
1.键盘驱动程序的主要函数
应用程序通过
fd1 = open(“/dev/kbd7312”,0)
指向设备端口,然后再for(;;)死循环中调用ioctl(fd1,0,0),进程进入驱动程序执行kbd_kernel_ioctl()。
//对键盘设备文件进行读/写之外的其他操作
static int kbd_kernel_ioctl(struct inode * inode,struct file *file,unsigned int cmd,unsigned long arg)
{
switch(cmd)
{
//接收一个按键
case KBD_GETKEY:
return kbd_kernel_getkey();
default:
printk(“Unknown Keyboard command ID.\n”);
}
return 0;
}
//获取一个键值
static int kbd_kernel_getkey()
{
int i,j,k;
enable_irq(16); //开启键盘中断
KeyValue=0; //初始化键值
//循环将各列扫描线为高电平,检测是否有键按下
for(i=0;i { //循环置列线为高电平
//系统寄存器1的低4位为1000B,列线0高电平
//系统寄存器1的低4位为1001B,列线1高电平
//系统寄存器1的低4位为1010B,列线2高电平
//系统寄存器1的低4位为1011B,列线3高电平
SYSCON1 &= ~0xf;
SYSCON1 |=I % 4 + 8;
//延时循环,避免键盘抖动
for(j =0;j for(k=0;k //如果有键按下,则返回键值
if(KeyValue){
return KeyValue;
}
}
//无键按下,返回0
return 0;
}
键盘驱动定义的全局变量KeyValue为键值,标志是否有键按下。初始值为0,表示没有键按下。这里cmd为KBD_GETKEY执行取键值程序段。i为键盘的列,先将它赋值为0,然后加以判断。当它为0时,将寄存器0x80000100中低4位置为100B,此时键盘第0列为高,其他为高阻态。如果没有中断,延时一段时间后,将i赋值为1,此时,键盘第1列为高,其他为高阻态。依此类推,直到键盘第4列为高。此间如果没有键按下,则不作任何操作返回应用程序。由于应用程序为一个死循环,所以进程将通过循环调用ioctl(fd1,0,0)进入驱动程序,执行kbd_kernel_ioctl()。继续进行如上所示操作,直到按下一个键,进程接收到中断信息,转入执行中断服务程序kbd_Interrupt()。
//键盘中断服务程序
static void kbd_Interrupt(int irq,void *dev_id,struct pt_regs *regs)
{
unsigned char LHB_PortA; /* 端口A数据寄存器的低4位 */
unsigned char row,col; /* 按键的行列值 */
unsigned char keypressed; /* 是否有键按下的标志 */
//屏蔽键盘中断
disable_irq(16);
//获取端口A数据寄存器的低4位
LHB_PortA = PADR & 0xf;
//判断列值:
//系统寄存器1低4位为1000B,列值为0
//系统寄存器1低4位为1001B,列值为1
//系统寄存器1低4位为1010B,列值为2
//系统寄存器1低4位为1011B,列值为3
col = (SYSCON1 & 0xF) – 0x8;
//判断行值
//端口A数据寄存器的低4位为0001B,行值为0
//端口A数据寄存器的低4位为0010B,行值为1
//端口A数据寄存器的低4位为0100B,行值为2
//端口A数据寄存器的低4位为1000B,行值为3
keypressed = 0;
for(row=0;row {
if(LHB_PortA>>row) & 0x1)
{
keypressed = 1;
break;
}
}
//计算键值
if(keypressed){
KeyValue = (row * 4 + 1)+col;
}
else
enable_irq(16);
}
此时keypressed被赋值为1,表示有键按下。键盘中断服务程序将端口A数据寄存器中的值赋给LHB_PortA。LHB_PortA值的低4位代表键盘的4行。判断LHB_PortA值,如果为01H,则第0位为1,即按下键在键盘第0行;如果LHB_PortA值为02H,则第1位为1,则按下键在键盘第1行;如果LHB_PortA值为04H,则第2位为1,则按下键在键盘第2行;如果LHB_PortA值为08H,则第3位为1,则按下键在键盘第3行。由row和col的值来计算键值。
中断程序执行完后,进程返回中断点,继续执行KbdIoctl(),返回键值到应用程序,供应用程序使用。如果中间有另一次中断,则将中断值放入键盘中断使用的寄存器,等待下一次读取。
值得注意的是,如果当键盘的第0列为高时,按下的键并不在这一列,则并没有中断产生,但是由于人手按下按键要持续很短的时间,而列轮流置高的速度又很快,所以在按键还没有弹起的瞬间,就已经将按下键所在列置高,产生中断,进程转入中断程序。为了去除抖动问题,由两个嵌套的空的for循环实现一小段时间延时。
2.键盘驱动程序的初始化
当键盘设备打开后,kbd_kernel_init(void)向内核注册模块所能提供的设备,传递的参数为指向描述这个新设备的数据结构和姚注册的设备名称,同时对该设备的各寄存器赋初始化值。
//初始化键盘设备
int kbd_kernel_init(void)
{
int result; /* 注册设备结果 */
//注册键盘设备
printk("\nRegistering Keyboard Device\t- - ->\t");
result=register_chrdev(KEYBOARD_MAJOR,"keryboard_ep7312",&Kbd_fops);
if(result {
printk(KERN_INFO"[FAILED:Cannot register keyboard_ep7312!]\n");
return result;
}
else
printk("Initializing Keyboard Device\t- - ->\t");
//设置键盘设备
setup_kbd();
//注册键盘中断
if(request_irq(16,kbd_Interrupt,0,"kbd_interrupt",NULL))
{
printk(KERN_INFO"[FAILED:Cannot register keyboard interrupt!]\n");
return -EBUSY;
}
else
printk("[OK]\n");
//显示成功加载信息
printk("\nEP7312 Keyboard Driver installed:\n");
return 0;
}
所用到的定义和函数如下:
#define SYSCON1 *(unsigned long * )0xff000100 /* 系统寄存器1 */
#define SYSCON2 *(unsigned long * )0xff001100 /* 系统寄存器2 */
#define PADR *(unsigned char * )0xff000000 /* 端口A数据寄存器 */
#define PADDR *(unsigned char * )0xff000040 /* 端口A方向寄存器 */
#define PBDR *(unsigned char * )0xff000001 /* 端口B数据寄存器 */
#define PBDDR *(unsigned char * )0xff000041 /* 端口B方向寄存器 */
//ioctl 命令ID
#define KBD_GETKEY 1
//定义键盘的主设备号
#define KEYBOARD_MAJOR 59
static char KeyValue; /* 键值 */
//设置键盘设备
static void setup_kbd(void)
{
//设置系统寄存器
SYSCON1 &= ~0xf;
SYSCON1 |= 0x8;
SYSCON2 |= 0x000000008;
PADDR |= 0xfffffffff;
//初始化键值
KeyValve = 0;
}
3.键盘驱动程序提供给文件系统的接口
当一个进程试图对键盘设备进行操作时就利用了下面的结构。这个结构就是提供给操作系统的接口,其指针保存在设备列表中,在kbd_kernel_init(void)中被传递给操作系统。
//键盘设备文件操作结构
struct file_operations Kbd_fops =
{
open: kbd_kernel_open, /* 打开设备文件 */
ioctl: kbd_kernel_ioctl, /* 设备文件的其他操作 */
};