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

发布时间:2024-06-06  

基于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 keyvaluen  %d",pre_keyboard_code); 
  save_key_to_queue(pre_keyboard_code);//将按键值存入循环队列 
  } 
 } 
 else//如果按键弹起 
 { 
  //del_timer(&repeat_timer); 
 // printk("del repeat timern"); 
 } 
 
 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, %dn", 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 =%dn",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, %dn", 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:%dn",timeCount); 
   
  if (timeCount==TIME_OUT) //扫描5次后仍然没有得到稳定键值 
  { 
  //复位计数器 
  timeCount=1; 
  kbdev.keyStatus =KEY_UP; 
  //使能中断 
  enableIrq(); 
  //关闭定时器 
  del_timer(&kb_timer); 
 
  del_timer(&repeat_timer); 
  //printk("enable irq nnn"); 
  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=%dn",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 Errorn"); 
 
 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 numbern"); 
  return ret; 
 } 
 kbMajor = ret; 
 
 printk("%s: major number=99n",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("exitn"); 
 
    /*释放中断*/ 
 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 %sn", 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 

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>