首页 / 操作系统 / Linux / 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 3 static 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------>output GPJCON &= 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,其余行输出0 matrix_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
%d",pre_keyboard_code); save_key_to_queue(pre_keyboard_code);//将按键值存入循环队列 } } else//如果按键弹起 { //del_timer(&repeat_timer); // printk("del repeat timer
"); } 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
", snap_keyboard_code[0][1],snap_keyboard_code[1][1],snap_keyboard_code[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
",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
", snap_keyboard_code[3][0],snap_keyboard_code[3][1],snap_keyboard_code[3][2],snap_keyboard_code[3][3]); kb_timer.expires = jiffies + KB_TIMER_DELAY; add_timer(&kb_timer); //printk("timeCount:%d
",timeCount); if (timeCount==TIME_OUT) //扫描5次后仍然没有得到稳定键值 { //复位计数器 timeCount=1; kbdev.keyStatus =KEY_UP; //使能中断 enableIrq(); //关闭定时器 del_timer(&kb_timer); del_timer(&repeat_timer); //printk("enable irq
"); curr_keyboard_code = 0; pre_keyboard_code= 0 ; memset(snap_keyboard_code,0,12*sizeof(u_short)); } else timeCount++; } 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_t S3C2440_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
",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_LOWLEVEL if(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
"); 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
"); return ret; } kbMajor = ret; printk("%s: major number=99
",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
"); /*释放中断*/ 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_OBJECT public: 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_HPC terminalName = "/dev/keyboard"; buttonFD = -1; notifier = 0; if ((buttonFD = open(terminalName, O_RDONLY | O_NDELAY, 0)) < 0) { qWarning("Cannot open %s
", terminalName.latin1()); return; } notifier = new QSocketNotifier( buttonFD, QSocketNotifier::Read, this ); connect( notifier, SIGNAL(activated(int)),this, SLOT(readKeyboardData()) ); #endif } QWSHPCButtonsHandler::~QWSHPCButtonsHandler() { #ifdef QT_QWS_HPC if ( 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("
Current Press Buttom %d
",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; // Enter case 4: k=Qt::Key_Escape; break; // Enter default: k=(-1); break; } if ( k >= 0 ) { qwsServer->processKeyEvent( 0, k, 0, 1, false ); } #endif }
收藏该网址