歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> 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\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_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\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_code[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)); 
  } 
  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\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_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\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_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\n", 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("\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; // Enter 
          case 4:      k=Qt::Key_Escape; break; // Enter 
          default: k=(-1); break; 
        } 
 
 if ( k >= 0 ) 
 { 
  qwsServer->processKeyEvent( 0, k, 0, 1, false ); 
 } 
 
#endif 

Copyright © Linux教程網 All Rights Reserved