之前写过一篇linux按键驱动,采用的是中断的方式,很多时候,我们没有更多的中断,只能采用扫描的方式了。具体思路就不做详细解释了,比较简单,先上code吧。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
//GPD0--GPD9==> KEY0--KEY9
//GPD10:BOOKMARK
//GPD11:T_RIGHT
//GPD12:T_LEFT
//GPD13:T_ENTER
//GPD14:OVERTURN
//GPD15:BLOWUP
//GPB0:MENU
//GPG1:CANCEL
//GPG2:UP
//GPG3:DOWN
//GPG4:LEFT
//GPB5:RIGHT
//GPB6:OK
//GPB9:PLAY/PAUSE
*/
static struct key_info
{
unsigned int pin;
int key_code;
char *name;
}key_info_tab[] =
{
{S3C2410_GPD0, 0x01, "Key 01" },
{S3C2410_GPD1, 0x02, "Key 02" },
{S3C2410_GPD2, 0x03, "Key 03" },
{S3C2410_GPD3, 0x04, "Key 04" },
{S3C2410_GPD4, 0x05, "Key 05" },
{S3C2410_GPD5, 0x06, "Key 06" },
{S3C2410_GPD6, 0x07, "Key 07" },
{S3C2410_GPD7, 0x08, "Key 08" },
{S3C2410_GPD8, 0x09, "Key 09" },
{S3C2410_GPD9, 0x0A, "Key 0A" },
{S3C2410_GPD10, 0x0B, "Key 0B" },
{S3C2410_GPD11, 0x0C, "Key 0C" },
{S3C2410_GPD12, 0x0D, "Key 0D" },
{S3C2410_GPD13, 0x0E, "Key 0E" },
{S3C2410_GPD14, 0x0F, "Key 0F" },
{S3C2410_GPD15, 0x10, "Key 10" },
{S3C2410_GPB0, 0x11, "Key 11" },
{S3C2410_GPG1, 0x12, "Key 12" },
{S3C2410_GPG2, 0x13, "Key 13" },
{S3C2410_GPG3, 0x14, "Key 14" },
{S3C2410_GPG4, 0x15, "Key 15" },
{S3C2410_GPB5, 0x16, "Key 16" },
{S3C2410_GPB6, 0x17, "Key 17" },
{S3C2410_GPB9, 0x18, "Key 18" },
};
#define TRUE 1
#define FALSE 0
#define DEVICE_NAME "pic_key"
#define MAX_KEY_BUF 16 //KEY BUFFER SIZE
#define KEY_NUM 24 //key num
#define BUF_HEAD (pic_key_dev.buf[pic_key_dev.head])
#define BUF_TAIL (pic_key_dev.buf[pic_key_dev.tail])
#define INCBUF(x,mod) ((++(x))&((mod)-1))
#define SCANKEY_TIME 20//50ms
#define PIC_KEY_MAJOR 250
static dev_t pic_key_major = PIC_KEY_MAJOR;
typedef unsigned int KEY_RET;
struct pic_key_dev_t
{
struct cdev cdev;
unsigned int buf[MAX_KEY_BUF];
unsigned int head,tail;
unsigned int key_value;
wait_queue_head_t wq;
};
struct pic_key_dev_t pic_key_dev;
struct timer_list key_timer;
#define ISKEY_DOWN(key) (s3c2410_gpio_getpin(key_info_tab[key].pin) == 0)
static void pic_key_timer(unsigned long data);
static int pic_key_open(struct inode *inode, struct file *filp);
static int pic_key_release(struct inode *inode, struct file *filp);
static ssize_t pic_key_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos);
static ssize_t pic_key_read(struct file *file,char __user *buffer, size_t count, loff_t *ppos);
static unsigned int get_key_value(void)
{
int key;
unsigned int ret=0;
for (key=0;key
{
if (ISKEY_DOWN(key))
{
ret |= (1<
}
}
return ret;
}
#define FLGKEY_KEY_MASK 0x00ffffff//24key
#define FLGKEY_PRESSED 0x00000000
#define FLGKEY_RELEASED 0x01000000
#define FLGKEY_CONTINUE 0x02000000
#define FLGKEY_HOLD 0x02000000
#define FLGKEY_LONGPRESSED 0x04000000
#define FLGKEY_LONGHOLD 0x04000000
#define FLGKEY_ACT_MASK 0x0f000000 // Key action mask
#define KEY_HOLD_COUNT 10
#define KEY_HOLD_CONTINUE 1//2
#define KEY_LONG_HOLD_COUNT 10//9
static unsigned int uiLastKeyStatus = 0;
static unsigned int uiLastMDStatus = 0;
static unsigned int uiStableKeyStatus = 0;
static unsigned int uiStableMDStatus = 0;
static unsigned int uiLastStableKeyStatus = 0;
static unsigned int uiKeyMaskPress = FLGKEY_KEY_MASK;
static unsigned int uiKeyMaskRelease = FLGKEY_KEY_MASK;
static unsigned int uiKeyMaskContinue = FLGKEY_KEY_MASK;
static unsigned int uiKeyMaskLong = 0;
static unsigned int uiKeyHoldCount = 0;
static unsigned int uiKeyLongHoldCount = 0;
static bool bKeyHoldFlag = FALSE;
static unsigned int g_uiKeyScanFlags;
static void handle_pic_key_event(unsigned int keycode)
{
BUF_HEAD = keycode;
pic_key_dev.head = INCBUF(pic_key_dev.head,MAX_KEY_BUF);
printk(KERN_ERR "key_value:%x\n",keycode);
wake_up_interruptible(&(pic_key_dev.wq));
}
static void pic_key_timer(unsigned long data)
{
unsigned int uiCurKeyStatus, uiTempKeyStatus;
unsigned int uiKeyChanged;
unsigned int uiKeyPressed, uiKeyReleased, uiKeyContinue;
unsigned int bKeyCode;
uiCurKeyStatus =
uiTempKeyStatus = get_key_value();
uiCurKeyStatus &= (uiLastKeyStatus & FLGKEY_KEY_MASK);
uiKeyChanged = uiCurKeyStatus ^ uiStableKeyStatus;
uiKeyPressed = uiKeyChanged & uiCurKeyStatus;
uiKeyReleased = uiKeyChanged & (~uiCurKeyStatus);
uiKeyContinue = uiCurKeyStatus & uiStableKeyStatus & uiLastStableKeyStatus;
uiLastStableKeyStatus = uiStableKeyStatus & ~(uiKeyContinue);
uiStableKeyStatus = uiCurKeyStatus;
uiLastKeyStatus = uiTempKeyStatus;
if (uiKeyPressed)
{
uiTempKeyStatus = uiKeyPressed & uiKeyMaskPress;
if (uiTempKeyStatus)
{
g_uiKeyScanFlags |= (FLGKEY_PRESSED | uiTempKeyStatus);
}
}
if (uiKeyReleased)
{
uiTempKeyStatus = uiKeyReleased & uiKeyMaskRelease;
if (uiTempKeyStatus != 0)
{
g_uiKeyScanFlags |= (FLGKEY_RELEASED | uiKeyReleased);
bKeyHoldFlag = FALSE;
uiKeyHoldCount = 0;
uiKeyLongHoldCount = 0;
}
}
if (uiKeyContinue)
{
uiTempKeyStatus = uiKeyContinue & uiKeyMaskContinue;
if (uiTempKeyStatus)
{
uiKeyHoldCount++;
if (bKeyHoldFlag == FALSE && uiKeyHoldCount > KEY_HOLD_COUNT ||
bKeyHoldFlag == TRUE && uiKeyHoldCount > KEY_HOLD_CONTINUE)
{
uiKeyHoldCount = 0;
// Play keypad tone
if (bKeyHoldFlag == FALSE)
{
bKeyHoldFlag = TRUE;
g_uiKeyScanFlags |= (FLGKEY_HOLD | uiKeyContinue);
}
// Continue key pressed event
if (bKeyHoldFlag)
{
uiKeyLongHoldCount++;
if (uiKeyLongHoldCount > KEY_LONG_HOLD_COUNT)
{
uiKeyLongHoldCount = 0;
g_uiKeyScanFlags &= ~FLGKEY_HOLD;
g_uiKeyScanFlags |= FLGKEY_LONGHOLD;
}
}
}
}
}
if (g_uiKeyScanFlags)
{
handle_pic_key_event(g_uiKeyScanFlags);
g_uiKeyScanFlags = 0;
}
init_timer(&key_timer);
key_timer.expires = jiffies + SCANKEY_TIME*HZ/1000; ;
key_timer.function = (void*)pic_key_timer;
add_timer(&key_timer);
}
static const struct file_operations pic_key_fops =
{
.owner = THIS_MODULE,
.read = pic_key_read,
.write = pic_key_write,
.open = pic_key_open,
.release = pic_key_release,
};
static void pic_key_setup_cdev(void)
{
int i;
int err,devno = MKDEV(pic_key_major,0);
cdev_init(&pic_key_dev.cdev,&pic_key_fops);
pic_key_dev.cdev.owner = THIS_MODULE;
pic_key_dev.cdev.ops = &pic_key_fops;
//pic_key_dev.key_status = 0;
err = cdev_add(&pic_key_dev.cdev, devno, 1);
if (err)
{
printk(KERN_NOTICE "Error %d adding pic_key",err);
}
init_waitqueue_head(&(pic_key_dev.wq));
init_timer(&key_timer);
key_timer.expires = jiffies + HZ;
key_timer.function = (void*)pic_key_timer;
add_timer(&key_timer);
}
static int pic_key_open(struct inode *inode, struct file *filp)
{
filp->private_data = &pic_key_dev;
printk(KERN_NOTICE "pic_key opened\n");
return 0;
}
static int pic_key_release(struct inode *inode, struct file *filp)
{
printk(KERN_NOTICE "pic_key released\n");
return 0;
}
static ssize_t pic_key_write(struct file *filp, const char __user *buffer, size_t count, loff_t *ppos)
{
return 0;
}
static ssize_t pic_key_read(struct file *filp,char __user *buffer, size_t count, loff_t *ppos)
{
KEY_RET key_ret;
struct pic_key_dev_t *dev;
dev = (struct pic_key_dev_t*)filp->private_data;
retry:
if (pic_key_dev.head != pic_key_dev.tail)
{
key_ret = BUF_TAIL;
dev->tail = INCBUF(dev->tail,MAX_KEY_BUF);
copy_to_user(buffer,(char*)&key_ret,sizeof(KEY_RET));
return sizeof(KEY_RET);
}else
{
if (filp->f_flags & O_NONBLOCK)
{
return -EAGAIN;
}
interruptible_sleep_on(&(dev->wq));
if (signal_pending(current))
{
return -ERESTARTSYS;
}
goto retry;
}
return sizeof(KEY_RET);
}
static int __init pic_key_init(void)
{
int result;
int i;
dev_t devno = MKDEV(pic_key_major,0);
if (pic_key_major)
{
result = register_chrdev_region(devno, 1, DEVICE_NAME);
}else
{
result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
pic_key_major = MAJOR(devno);
}
if (result < 0)
{
return result;
}
pic_key_setup_cdev();
for (i=0;i
{
s3c2410_gpio_cfgpin(key_info_tab[i].pin,0);//输入
s3c2410_gpio_pullup(key_info_tab[i].pin,2);//上拉
}
return 0;
}
static void __exit pic_key_exit(void)
{
int i;
cdev_del(&pic_key_dev.cdev);
unregister_chrdev_region(MKDEV(pic_key_major,0),1);
del_timer(&key_timer);
}
MODULE_AUTHOR("Creator");
MODULE_LICENSE("Dual BSD/GPL");
module_init(pic_key_init);
module_exit(pic_key_exit);