按键抖动
按键所用的开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,开关不会马上稳定地接通或断开。因而在闭合及断开的瞬间总伴随有一连串的抖动。
按键去抖动的方法主要有两种,一种是硬件电路去抖;另一种就是软件延时去抖。而延时去抖一般又分为两种,一种是for循环等待,另一种是定时器延时。在操作系统中,由于效率方面的原因,一般不允许使用for循环来等待,只能使用定时器。
内核定时器
Linux内核使用struct timer_list来描述一个定时器:
struct timer_list{
struct list_head entry;
unsigned long expires; //定时时间
void (*function)(unsigned long); //处理函数
unsigned long data;
struct tvec_base *base;
};
定时器使用流程
1.定义定时器
2.初始化定时器
2.1.init_timer初始化
2.2.设置超时函数
3.add_timer注册定时器
4.启动定时器器
4.1.设置超时时间
4.2.mod_timer启动定时器
jiffies表示系统当前的时间,单位是嘀嗒数,一秒钟有1000个嘀嗒。HZ表示一秒。
keydev.c
/******************************************************************** *头文件 *********************************************************************/ #include <linux/init.h> #include <linux/module.h> #include <linux/miscdevice.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/fs.h> /******************************************************************** *宏定义 *********************************************************************/ #define GPGCON 0x56000060 //按键控制寄存器地址 #define GPGDAT 0x56000064 //按键数据寄存器地址 /******************************************************************** *全局变量 *********************************************************************/ unsigned int *keycon; //按键控制指针 unsigned int *keydat; //按键数据指针 struct work_struct *work; //按键工作 struct timer_list timer; //按键定时 /******************************************************************** *定时处理 *********************************************************************/ //设备定时 void key_timer(unsigned long data){ //读取按键状态 unsigned short keyTmp; int level; keyTmp = readw(keydat); //获取GPGDAT值 level = keyTmp & 0x0001; //获取按键电平 //判断按键状态 if(level == 0){ printk("key down!\n"); } } /******************************************************************** *中断处理 *********************************************************************/ //设备中断下部 void key_work(struct work_struct *work){ //启动定时设备 mod_timer(&timer, jiffies + HZ/100); //jiffies表示系统当前嘀嗒数,1HZ = 1s = 1000jiffies } //设备中断上部 irqreturn_t key_irq(int irq, void *dev_id){ //处理硬件相关 //提交硬件无关 schedule_work(work); return 0; } /******************************************************************** *设备方法 *********************************************************************/ //设备打开 int key_open(struct inode *node, struct file *filp){ return 0; } //设备关闭 int key_close(struct inode *node, struct file *filp){ return 0; } //设备方法 struct file_operations key_fops = { .open = key_open, .release = key_close }; /******************************************************************** *模块安装 *********************************************************************/ //混杂设备 struct miscdevice misdev = { .minor = 200, //次设备号 .name = "keydev", //设备名称 .fops = &key_fops //设备方法 }; //注册硬件 void handware_init(void){ //初始化按键控制寄存器 unsigned int keyTmp; keycon = ioremap(GPGCON, 4); //虚拟地址映射 keyTmp = readl(keycon); //获取GPGCON值 keyTmp &= ~(0x3<<0); //GPG0[1:0]:00 keyTmp |= (0x2<<0); //GPG0[1:0]:EINT[8] writel(keyTmp, keycon); //设置GPGCON值 //初始化按键状态寄存器 keydat = ioremap(GPGDAT, 2); //虚拟地址映射 } //安装模块 static int key_init(void){ //注册混杂设备 misc_register(&misdev); //注册硬件设备 handware_init(); //注册中断处理 request_irq(IRQ_EINT8, key_irq, IRQF_TRIGGER_FALLING, "keyirq", 0); //下降沿触发,IRQ_EINT8定义在irqs.h文件中 //注册工作队列 work = kmalloc(sizeof(struct work_struct), GFP_KERNEL); INIT_WORK(work, key_work); //注册定时设备 init_timer(&timer); //初始化定时器 timer.function = key_timer; //添加定时函数 add_timer(&timer); //添加定时设备 return 0; } //卸载模块 static void key_exit(void){ //注销混杂设备 misc_deregister(&misdev); //注销中断处理 free_irq(IRQ_EINT8, 0); } /******************************************************************** *模块声明 *********************************************************************/ MODULE_LICENSE("GPL"); MODULE_AUTHOR("D"); MODULE_DESCRIPTION(""); MODULE_VERSION("v1.0"); module_init(key_init); module_exit(key_exit);
时间: 2024-10-13 12:58:44