linux和qtopia下的矩阵键盘驱动程序

2019-07-12 21:58发布

基于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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include #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
}