linux键盘有延时,基于s3c2440的linux键盘驱动  延时消抖,重复按键,多键齐按

基于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);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值