今天实践了一个比较复杂的驱动,完成了按键出发外部中断EINT2,测试函数统计按键按下的次数打印出来,同时包含了防抖动功能。
编程主要思想是,按键产生外部中断2,中断服务函数里面启动定时器,定时器预设防抖动的检测时间,每个定时器中断产生,定时器服务函数检测外部中断,连续3次都检测有按键中断产生,确认,计数加一,否则认为是按键抖动,丢弃。测试函数读取驱动里面统计的按键中断数目,打印到终端上。
驱动用到了定时器,自旋锁,中断函数,等待队列,同步等驱动的机制,几乎涵盖了linux驱动开发的常用功能的使用,非常利于学习。下面是我的学习与实践过程,附上源码,编译好的的文件,以及shell脚本。由于每个人的环境不一样,肯定是要修改一下才能用的。
https://static.assets-stash.eet-china.com/album/old-resources/2009/8/25/60e7a38e-dfcd-4b82-acbf-1734e5b6a9c4.rar
对于驱动的开发架构这里不再赘述,大家可以看我前面的博文学习,我主要写下新东西的使用。
1.定时器
熟悉单片机的应该很熟悉定时+定时中断的工作模式,其实我们在linux驱动开发的时候也可以使用这个功能。
内核定时器在头文件#include <linux/delay.h>下,也可能是#include <linux/timer.h>有兴趣的可以去查看它们的定义。
使用方法:
static struct timer_list ring_timer; //声明一个定时器
init_timer(&ring_timer); //初始化定时器
ring_timer.function = timer_handler; //设置定时中断服务函数
ring_timer.expires = jiffies + TIMER_DELAY; //赋值定时器的定时数值
add_timer(&ring_timer); //启动定时器
static inline void timer_handler(unsigned long data) //编写定时中断服务函数
{ .....
del_timer(&ring_timer);
}
2.中断函数的申请与使用
中断函数在linux驱动里面使用很多,他的使用方法如下:
#define IRINT_MAJOR 96 //申请中断号
set_external_irq(IRQ_EINT2,EXT_FALLING_EDGE,GPIO_PULLUP_DIS);
//设置外 部中断2属性
request_irq(IRQ_EINT2, irint_interrupt, SA_INTERRUPT,
DEVICE_NAME, NULL); //注册中断服务函数
static void irint_interrupt(int irq, void *dev_id, struct pt_regs *regs) //完成中断服务函数
{ ... }
//另外有两个开关中断的宏
enable_irq(IRQ_EINT2);
disable_irq(IRQ_EINT2);
3.自旋锁的使用
自旋锁主要用来并发控制,和信号量完成的功能相近。自旋锁适合保持时间非常短的情况,可以在任何上下文中使用。如果共享资源需要在中断上下文访问,必须使用自旋锁,不能用信号量。
spinlock_t lock; //声明自旋锁
spin_lock_init(lock) //初始化自旋锁
spin_lock_irq(&lock); //锁起来
...共享资源....
spin_unlock_irq(&lock); //解锁
4.等待队列
等待队列主要使用在阻塞与非阻塞中,阻塞就是设备操作是不能获得资源,等待挂起,知道满足条件时候再进行操作。
使用方法:
static wait_queue_head_t wq ; //声明等待队列
init_waitqueue_head(&wq); //初始化等待队列
if (eint2_value ==0) //如果没有中断资源
{
interruptible_sleep_on(&wq); //一直睡眠等待 有中断 可以被中断
}
wake_up_interruptible(&wq); //阻塞,但可被中断 与上面功能相近
具体是如何使用的大家可以详细分析代码。
用户1603071 2011-5-22 11:15
用户239938 2010-11-17 10:56