历史上的今天
今天是:2025年10月13日(星期一)
2022年10月13日 | mini2440 按键驱动添加定时器消抖动
2022-10-13 来源:csdn
测试程序和Makefile同前面的实验一样的,这里只记录一下驱动的源代码就行了,改动不大,就是把唤醒进程和发送异步信号的操作移动到定时器的超时函数里面去了,这样做的目的是为了消除按键的机械抖动。
驱动源代码:
#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define GRH_MODULE_NAME "key_interrupt" static int major; static struct class *key_interrupt_class; static struct class_device *key_interrupt_device; static int key_value; //wait_event_interruptible函数需要的两个变量 static DECLARE_WAIT_QUEUE_HEAD(grh_wait_interrupt); //休眠的进程队列头 static volatile int sleep_for_interrupt; //这个变量为0的时候read函数会休眠,中断里面将其置1,read函数末尾将其设置为0 //异步信号队列定义 static struct fasync_struct *grh_async_queue; //定义一个定时器,用于按键防抖动 static struct timer_list grh_timer_shake; static int first_timer_timeout; //pin_desc是对每一个按键中断的描述,不仅仅可以是整数,也可以是更复杂到的字段,这里用简单的按键值就行了 int pin_desc[6] = { 1, 2, 3, 4, 5, 6 }; //中断处理函数 static irqreturn_t grh_handle_key_eint(int irq, void *dev_id){ int *p; p = dev_id; //printk(KERN_EMERG"key pressed! key=%dn", *p); key_value = *p; //重新设置定时器的超时时间,其中HZ是一秒钟对应的系统计数值,下面的定时器是100ms的 mod_timer(&grh_timer_shake, jiffies+HZ/10); //唤醒休眠进程和发送异步信号的工作交给定时器超时处理函数来实现 return IRQ_HANDLED; } static void grh_timer_shake_handler(unsigned long n){ if(first_timer_timeout){ first_timer_timeout = 0; return; } //唤醒休眠的进程 sleep_for_interrupt = 1; wake_up_interruptible(&grh_wait_interrupt); //向用户空间进程发送异步信号 kill_fasync(&grh_async_queue, SIGIO, POLL_IN); } static void init_key(void){ //注册irq中断处理函数,将按键值和中断号绑定,所有清中断操作以及初始化中断相关寄存器的操作全部交给 //内核自动完成了,不再需要像裸机程序一样显式地对寄存器进行读写了,中断发生后会自动跳到grh_handle_key_eint request_irq(IRQ_EINT8, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key1", pin_desc); request_irq(IRQ_EINT11, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key2", pin_desc+1); request_irq(IRQ_EINT13, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key3", pin_desc+2); request_irq(IRQ_EINT14, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key4", pin_desc+3); request_irq(IRQ_EINT15, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key5", pin_desc+4); request_irq(IRQ_EINT19, grh_handle_key_eint, IRQ_TYPE_EDGE_FALLING, "key6", pin_desc+5); } static int key_interrupt_open(struct inode *inode, struct file *file){ printk(KERN_EMERG"DRIVER: OPENn"); sleep_for_interrupt = 0; init_key(); return 0; } static ssize_t key_interrupt_write(struct inode *inode, const char __user *buf, size_t count, loff_t *ppos){ printk(KERN_EMERG"DRIVER: WRITEn"); return 0; } static ssize_t key_interrupt_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){ printk(KERN_EMERG"DRIVER: READn"); //根据sleep_for_interrupt的数值决定是否将驱动进程加进休眠队列grh_wait_interrupt中,立即休眠进程 wait_event_interruptible(grh_wait_interrupt, sleep_for_interrupt); copy_to_user(buf, &key_value, 4); //下一次进入read的时候继续休眠等待中断发生 sleep_for_interrupt = 0; return 0; } int key_interrupt_release(struct inode *inode, struct file *file){ //注销中断 free_irq(IRQ_EINT8, pin_desc); free_irq(IRQ_EINT11, pin_desc+1); free_irq(IRQ_EINT13, pin_desc+2); free_irq(IRQ_EINT14, pin_desc+3); free_irq(IRQ_EINT15, pin_desc+4); free_irq(IRQ_EINT19, pin_desc+5); printk(KERN_EMERG"DRIVER: RELEASEn"); return 0; } //sys_poll会反复在死循环里面调用key_interrupt_poll static unsigned int key_interrupt_poll(struct file *file, struct poll_table_struct *wait){ unsigned int mask; printk(KERN_EMERG"DRIVER POLLn"); poll_wait(file, &grh_wait_interrupt, wait); //把当前进程挂到休眠队列里面,但是不立即休眠 mask = 0; if(sleep_for_interrupt){ //中断发生了 mask |= POLLIN | POLLRDNORM; //把有可读数据的标志位设置为1,用户层可以得到这个mask } return mask; /* 如果返回的mask是0那么进程直接进入定时休眠,如果在定时休眠过程中中断发生了,sys_poll里面的 定时休眠结束,sys_poll又会循环调用key_interrupt_poll,但这个时候mask一定返回非零数值了,这时 sys_poll中的休眠结束,进程继续运行。如果定时休眠过程中中断一直没有发生,那么定时休眠超时之后, sys_poll再调用一次key_interrupt_poll,然会判断是否超时,如果超时直接结束进程的休眠。这是 Linux内核的poll机制的大概原理,这样在用户层调用一次poll函数,进程最多休眠时间就是传进来的wait参数, 中断一发生,定时休眠立刻结束,否则进程就休眠到一次定时休眠结束为止。 */ } //用户空间程序调用fcntl(fd, F_SETFL, flag | FASYNC)的时候,下面的异步通知设置函数会被调用 static int key_interrupt_fasync(int fd, struct file *file, int on){ //fasync_helper函数会将用户空间进程的pid传入grh_async_queue里面 //这样中断处理函数里发出的信号才能被用户空间的应用程序收到 printk(KERN_EMERG"DRIVER : FASYNCn"); //初始化grh_async_queue的工作交给fasync_helper来做,驱动程序不去实现了 return fasync_helper(fd, file, on, &grh_async_queue); } static struct file_operations key_interrupt_fops = { .owner = THIS_MODULE, .open = key_interrupt_open, .write = key_interrupt_write, .read = key_interrupt_read, .release = key_interrupt_release, .poll = key_interrupt_poll, .fasync = key_interrupt_fasync, }; int key_interrupt_module_init(void){ printk(KERN_EMERG"INIT MODULE!n"); //初始化防抖动的timer,默认的超时时间是0 init_timer(&grh_timer_shake); grh_timer_shake.function = grh_timer_shake_handler; add_timer(&grh_timer_shake); first_timer_timeout = 1; //register the driver with the device major = register_chrdev(0, GRH_MODULE_NAME, &key_interrupt_fops); //create my own device class key_interrupt_class = class_create(THIS_MODULE, "key_interrupt_class"); //create my device of my own class key_interrupt_device = device_create(key_interrupt_class, NULL, MKDEV(major,0), NULL, "key_interrupt_device"); return 0; } void key_interrupt_module_exit(void){ unregister_chrdev(major, GRH_MODULE_NAME); device_unregister(key_interrupt_device); class_destroy(key_interrupt_class); printk(KERN_EMERG"EXIT MODULE!n"); } module_init(key_interrupt_module_init); module_exit(key_interrupt_module_exit); MODULE_AUTHOR("GRH"); MODULE_VERSION("1.0"); MODULE_DESCRIPTION("KEY POLL DRIVER"); MODULE_LICENSE("GPL");
史海拾趣
|
AGPS就是移动通讯系统的定位功能,该功能首先在CDMA系统中实现。因CDMA系统需要严格的同步时钟, 该时钟源来自GPS,加上基站的位置可知,于是高精度的定位就可以实现了,不过此技术无法实现大范围机动高精度定位,只 ...… 查看全部问答> |
|
有归属、没宗派,有尊重、没辈分,有针对、没争竞,有奉献、没名利 相互尊重、友好交流、扩大共识,倡导不同文化间增进了解、相互借鉴 电子工程师,结构工程师,软件工程师,硬件工程师,FPGA研发。另有数码产品,影音视听产品,手机,微型硬盘播放机,R ...… 查看全部问答> |
|
阿牛哥冒着七月的艳阳酷暑,晚上听完国际广播电台的新闻,西瓜荔枝,要看世界杯球赛了。晚上的这场要看,凌晨的那场要听。电视电脑收音机都需要,就是不能影响白天的工作。先说说世界杯比赛,不能在家里看,太影响家人.去哥们马六家看;啤酒不喝 ...… 查看全部问答> |
|
◆ 全面替代S3C2440,且性价比更优 ◆ 模块背面无任何器件,方便直接贴焊或通过插针与底板相连 ◆ 商业级(0°~70°)、工业级(-40°~85°) ◆ 微尺寸(40x40x4mm)、低价格(千片价… 查看全部问答> |
|
PC104设备在纯DOS下跑程序的问题,虚拟软驱引导的纯DOS和实际安装的DOS有区别不? 如题。 现在程序跑在虚拟软驱引导的纯DOS里面,2000里面用bc31写的程序在2000下跑很流畅,但是到DOS下却非常卡。 想在DOS里面再编译,链接模式选small能通过,但是程序一跑就死,选large能通过,一跑连画面都没有,选其他linker都不成功。何解? ...… 查看全部问答> |
|
wince 是否支持\"微软雅黑\"字体? 看到linux下显示的字体比较漂亮,font文件下看到msyh.ttf,也就是雅黑字体了,EVC下显示的字效果比较一般,要是wince支持msyh.ttf的话,应该怎样添加呢? … 查看全部问答> |
|
紧急求救!!用c开发手机或PDA需要看什么方面的书籍资料!!谢谢各位拉 本人用vc写过应用程序,用c写过mp3的程式,但是c++不太好,在vc里面基本用的都是c,现在想转做手机或pda方面,请教需要看哪些书啊?我看些资料,一般都是在用WinCE或在Linux下开发,哪个发展更好些?不知道这方面薪水待遇怎么样?敢请各位不啬赐教 ...… 查看全部问答> |
|
MSP430FG461X系列的20位地址指针在C语言中如何实现? 我把一个20位的地址0x10000,赋值给一个指针变量时,总是提示超出了0~0xFFFF的范围。 在汇编中这是可以做到的,因为MSP430X提供了MOVA等带A的指令,且工作寄存器都是20位的,可以用于20位的地址传输。就是不知道C语言中怎么实现。 不知是不是有 ...… 查看全部问答> |




