歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Linux和qtopia下的矩陣鍵盤驅動程序

Linux和qtopia下的矩陣鍵盤驅動程序

日期:2017/3/1 10:06:08   编辑:Linux編程

基於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