| |
Ø四个输出引脚:
Ø KEYSCAN0---( GPE11 )----OUTPUT
Ø KEYSCAN1---( GPG6 )----OUTPUT
Ø KEYSCAN2---( GPE13 )----OUTPUT
Ø KEYSCAN3---( GPG2 )----OUTPUT
键盘的驱动实现
Ø引入结构体key_info对按键进行描述
Østatic struct key_info {
Ø int irq_no; //外部中断号
Ø unsigned int gpio_port; //输入端口,EINT
Ø unsigned int gpio_port_kscan; //输出端口,OUTPUT
Ø int key_no; //按键序号,或者名字
Ø} key_info_tab[16] = {
Ø ……
Ø}
键盘初始化程序
Østatic int __init matrix4_buttons_init(void)
Ø{
Ø 注册字符设备 register_chrdev(……);
Ø 初始化按键对应的输出端口 buttons_io_port_init();
Ø 采用中断机制,注册中断号 request_irqs();
Ø}
键盘初始化程序
Ø/* 初始化kscan口为输出0 */
Østatic void buttons_io_port_init(void)
Ø{
Ø int i;
Ø for(i=0; i < sizeof kscan / sizeof kscan[1]; i++) {
Ø set_gpio_ctrl(kscan[i] | GPIO_PULLUP_DIS | GPIO_MODE_OUT);
Ø write_gpio_bit(kscan[i], 0);
Ø }
Ø}
请求注册中断
Østatic int request_irqs(void)
Ø{
Ø for (i = 0; i < 使用中断个数; i++) {
Ø 设置与外部中断号相对应的GPIO端口以及模式 请求中断号,并注册中断响应函数。
Ø }
Ø}
键盘驱动的卸载函数
Østatic void __exit matrix4_buttons_exit(void)
Ø{
Ø 释放中断 free_irqs(…);
Ø 注销字符设备unregister_chrdev(…);
Ø}
按键中断处理 buttons_irq
Østatic void buttons_irq(int irq, void *dev_id, struct pt_regs *reg)
Ø{
Ø 设置GPIO为输入状态
Ø 键盘扫描
Ø 唤醒按键等待队列的进程
Ø 重新设置GPIO为输出
Ø 重新设置中断
Ø}
通过延时去键盘抖动
Ø理想的情况下,当按键被按下时,I/O口电平被拉低,即逻辑0,当按键松开时,为逻辑1。
Ø但实际机械的触点动作与微处理器快速的执行速度不匹配,导致开关被按下或松开时产生抖动,如同弹簧一样,不能立刻产生稳定的0或1。
Ø常用的去抖动方法有硬件和软件两种,硬件可采用单稳态触发起、高通滤波器等硬件实现,但该方法一般要对每一路输入添加特殊的硬件,比较复杂。软件去抖动则简单多了,可通过增加延时和判断来解决。
matrix4_buttons_fops
Ø由于按键不具有I/O功能,它只会在按下的时候产生中断,所以这里不需要实现open,read,write,ioctl等功能。
Ø为了测试驱动,可以添加这些函数。
源代码:
/********************************************
* *GEC2410-BOX
* *date : created at 2007-09-19 by baijb
********************************************/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/devfs_fs_kernel.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#define DEVICE_NAME "key_driver"
#define LEDRAW_MINOR 1
#define KBD_TIMER_DELAY (HZ/50)
static int keyMajor = 0;
//--------------------------------------------
static struct key_info
{
unsigned int irq_num;
unsigned int gpio_port;
unsigned int key_num;
} keys[2] = {
{ IRQ_EINT10, S3C2410_GPG2, 1 },
{ IRQ_EINT11, S3C2410_GPG3, 2 },
};
static int key_value = 0;
static int ready = 0;
static struct timer_list kbd_timer;
static DECLARE_WAIT_QUEUE_HEAD(key_wait);
/****************************************
* *Pin function: EXINT
****************************************/
static void init_key_exint(void)
{
__raw_writel(__raw_readl(S3C2410_GPGCON) & (~(0x0f<<4)) | (0x0a<<4), S3C2410_GPGCON); //GPG2,3--EXINT10,11
__raw_writel(__raw_readl(S3C2410_EXTINT1) & (~(0x77<<8)) | (0x22<<8), S3C2410_EXTINT1); //EXINT10,11--falling edge triger
}
/****************************************
* * Pin funciton: input
****************************************/
static void init_key_input(void)
{
__raw_writel(__raw_readl(S3C2410_GPGCON) & (~(0x0f<<4)), S3C2410_GPGCON); //GPG2,3--EXINT10,11
}
//-------------------------------------------
static void kbd_timer_handler(unsigned long date)
{
struct key_info *key=NULL;
key_value = 0;
if((__raw_readl(S3C2410_GPGDAT) & (1<<2)) == 0 ) { //GPG2--pressed
key = &keys[0];
key_value = key->key_num;
}
else if((__raw_readl(S3C2410_GPGDAT) & (1<<3)) == 0 ) { //GPG3--pressed
key = &keys[1];
key_value = key->key_num;
}
ready = 1;
del_timer(&kbd_timer);
wake_up_interruptible(&key_wait);
init_key_exint();
}
//--------------------------------------------
static irqreturn_t key_irq_handle(int irq, void *devid, struct pt_regs *regs){
init_key_input(); //GPG2,3--input.delay 20 ms,then query which key is pressed.
kbd_timer.expires = jiffies + KBD_TIMER_DELAY; //delay 20 ms
add_timer(&kbd_timer);
return IRQ_HANDLED;
}
//---------------------------------------------
static int s3c2410_key_open(struct inode *inode, struct file *filp)
{
int ret;
struct key_info *key=NULL;
printk(KERN_INFO DEVICE_NAME ": opened.\n");
init_key_exint();
ready = 0;
key = &keys[0];
ret = request_irq(key->irq_num, key_irq_handle, SA_INTERRUPT, DEVICE_NAME, NULL);
if(ret) {
printk(KERN_INFO DEVICE_NAME " Failed to request irq.\n");
return ret;
}
key = &keys[1];
ret = request_irq(key->irq_num, key_irq_handle, SA_INTERRUPT, DEVICE_NAME, NULL);
if(ret) {
printk(KERN_INFO DEVICE_NAME " Failed to request irq.\n");
return ret;
}
init_timer(&kbd_timer);
kbd_timer.function = kbd_timer_handler;
return 0;
}
//-------------------------------------------------
static ssize_t s3c2410_key_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
static int value;
wait_event_interruptible(key_wait, ready == 1);
// interruptible_sleep_on(&key_wait);
if(count != sizeof(key_value)) {
printk("there is no key pressed!\n");
return -EINVAL;
}
value = key_value;
copy_to_user(buffer, &value, sizeof(value));
ready = 0;
return sizeof(value);
}
//----------------------------------------------------------
static int s3c2410_key_release(struct inode *inode, struct file *filp)
{
struct key_info *key=NULL;
printk(KERN_INFO DEVICE_NAME ": released.\n");
key = &keys[0];
free_irq(key->irq_num, NULL);
key = &keys[1];
free_irq(key->irq_num, NULL);
return 0;
}
//--------------------------------------------------------
static struct file_operations s3c2410_fops = {
owner: THIS_MODULE,
open: s3c2410_key_open,
read: s3c2410_key_read,
release: s3c2410_key_release,
};
static int __init key_driver_init(void)
{
int ret;
printk("s3c2410 key_driver init\n");
ret = register_chrdev(0, DEVICE_NAME, &s3c2410_fops);
if (ret < 0) {
printk(DEVICE_NAME " can't get major number\n");
return ret;
}
keyMajor = ret;
#ifdef CONFIG_DEVFS_FS
devfs_mk_dir("key");
devfs_mk_cdev(MKDEV(keyMajor, LEDRAW_MINOR), S_IFCHR|S_IRUGO|S_IWUSR, "key/%d", 0);
#endif
return ret;
}
static void __exit key_driver_exit(void)
{
printk("key_driver exit!\n");
#ifdef CONFIG_DEVFS_FS
devfs_remove("key/%d", 0);
devfs_remove("key");
#endif
unregister_chrdev(keyMajor, DEVICE_NAME);
}
module_init(key_driver_init);
module_exit(key_driver_exit);