基于s3c2440和linux,实现了3*4的矩阵键盘驱动。
功能:延时消抖,重复按键,多键齐按(??)
更详细的说明文档:“基于S3C24440和嵌入式Linux的矩阵键盘设计”,电子技术,2008,45(5):21-23
#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)
#define REPEAT_START_DELAY (HZ)
#define REPEAT_DELAY (HZ/2)
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);
static void init_gpjcon()
{
//GPJ9,GPJ10,GPJ11,GPJ12------>output
GPJCON &= 0xFC03FFFF;
GPJCON |= 0x01540000;
}
//
static inline void output_giop(int value ) //往所有行输出
{
value &= 0x0000000F;
value <<= 9;
GPJDAT &= 0xE1FF;
GPJDAT |= value;
udelay(2);
}
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;
}
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;
}
static inline int key_changed()
{
return (pre_keyboard_code == curr_keyboard_code)?
0 : 1;
}
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));
}
}
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));
}
//使能中断
static inline void enableIrq()
{
//清除SRCPND寄存器中eint1 eint2 eint8相应位
SRCPND = 0x0000002A;
//使能中断
enable_irq(IRQ_EINT1);
enable_irq(IRQ_EINT3);
enable_irq(IRQ_EINT8);
}
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);
//设置自动重复开始延时定时器
}
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));
}
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;
}
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;
}
}
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;
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,
};
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);
}
static int requestIrq()
{
int ret;
//==================================================
// 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);
return 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;
}
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);