Chinaunix首页 | 论坛 | 博客
  • 博客访问: 264075
  • 博文数量: 25
  • 博客积分: 329
  • 博客等级: 一等列兵
  • 技术积分: 1380
  • 用 户 组: 普通用户
  • 注册时间: 2012-08-24 09:43
文章分类

全部博文(25)

文章存档

2014年(4)

2013年(12)

2012年(9)

分类: LINUX

2012-08-24 10:05:45

MicrosoftInternetExplorer402DocumentNotSpecified7.8Normal0

按键驱动可以有两种方式实现:一种是查询法,一种是中断法。查询法的确定非常耗资源,在一个高效的系统中,不应使用查询法来获取按键值,优点是程序代码简单。故一般只在bootloader中使用这种方式。中断法优点是不耗资源,驱动时程序实现起来较复杂。

中断法采用的设计思想是:应用程序来读按键的键值,如果没有按键按下的时候,进程休眠,当有按键按下的时候在中断程序中唤醒进程。

按键驱动的实现步骤:

1.定义一个等待队列

2.在模块加载函数中使用request_irq函数注册中断,request_irq函数中有五个参数,分别是中断号,中断处理函数,中断的触发方式,名字,和传入中断函数的参数(该参数在共享中断中用于区分是哪一个触发的中断)。

3.在read函数中使用wait_event_interruptible函数有条件休眠进程,该休眠方式是可中断休眠。wait_event_interruptible(button_waitq, ev_press); 当ev_press为真时,立即返回,当ev_press为假时,进入可中断休眠。并将该进程挂载button_waitq定义的等待队列上。

4.在中断处理函数中使用wake_up_interruptible函数唤醒进程。

wake_up_interruptible(&button_waitq);

处在于中断处理程序是在中断上下文中运行的,它的行为受到某些限制:

1.不能向用户空间发送或接受数据

2.不能使用可能引起阻塞的函数

3.不能使用可能引起调度的函数

一些命令:

cat /proc/interrupts  查看当前注册的中断

exec 5

关闭设备文件exec 5<&-  关闭之前打开5指向的设备文件。

对于应用程序来说:可以使用去读按键键值,没有按键按下就休眠。也可以让应用程序一直休眠,当有按键按下的时候,在驱动的中断处理函数中给应用程序发信号,唤醒应用程序,让它来读取数据。这是就用到了file_operations中的.fasync函数。

在驱动中.fasync函数实现很简单,如下:

static struct fasync_struct * fapp;

static int buttons_fasync(int fd, struct file *filp, int on)

{

return fasync_helper(fd, filp, on, &fapp);

}

在中断处理函数中,当有按键按下,给应用程序发信号。

使用函数kill_fasync(&fapp, SIGIO, POLL_IN);

在应用程序中:

A.使用函数signal(SIGIO, my_signal_fun)注册信号处理函数my_signal_fun

B.调用fcntl(fd, F_SETOWN, getpid());  // 告诉内核,发给谁,即将进程的pid告诉内核

C.Oflags = fcntl(fd, F_GETFL);   

D.fcntl(fd, F_SETFL, Oflags | FASYNC);  // 改变fasync标记,最终会调用到驱动的faync > fasync_helper:初始化/释放fasync_struct

E.在信号处理函数中,读取按键值

信号量

信号量(semaphore)是用于保护临界区的一种常用方法,只有得到信号量的进程才能执行临界区代码。

当获取不到信号量时,进程进入休眠等待状态。

定义信号量

struct semaphore sem;

初始化信号量

void sema_init (struct semaphore *sem, int val);

void init_MUTEX(struct semaphore *sem);//初始化为0

static DECLARE_MUTEX(button_lock);     //定义互斥锁

获得信号量

void down(struct semaphore * sem);

int down_interruptible(struct semaphore * sem); 

int down_trylock(struct semaphore * sem);

释放信号量

void up(struct semaphore * sem);

阻塞操作    

是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。

被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。

非阻塞操作  

进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。

fd = open("...", O_RDWR | O_NONBLOCK); 

按键是一个机械结构,在按下的时候难免会发生机械抖动,为了解决这一个问题,我们引入了内核定时器,这样来实现按键的去抖,对于内核定时器的使用,总价为以下几点:

1. 定义一个struct timer_list结构;

2. 使用init_timer函数初始化定时器

3. 设置超时函数,即timer_list结构中.function成员

4. 使用add_timer函数添加定时器

5. 使用mod_timer重新设置定时器超时时间

在入口函数中添加了定时器,在出口函数中应该删除定时器,使用del_timer函数。

为了提供统一的接口,在内核中实现了一套input子系统,实现这个操作的步骤:

1. 分配一个struct input_dev结构,使用input_allocate_device函数实现

2. 设置能产生那类事件,使用set_bit函数实现

3. 能参数这类事件中的哪些事件,使用set_bit函数实现

4. 注册input_dev结构

5. 在发生中断的时候使用input_event函数上报事件(对于按键来说,有按下弹起两种事件),上报完后使用input_sync再上报同步事件

Input系统代码:

static struct input_dev  *buttons_dev;

在入口函数中:

/* 分配一个input_dev结构 */

buttons_dev = input_allocate_device();

/* 设置 input_dev结构*/

set_bit(EV_KEY, buttons_dev->evbit); // 能产生按键类事件

set_bit(KEY_L, buttons_dev->keybit); // 能产生按键事件中,键值为L的事件

在中断函数中:

/* 上报产生了键值为pin_pd->keyval的按键松开时的事件 */ 

input_event(buttons_dev, EV_KEY, pin_pd->keyval, 0); 

/* 上报产生了键值为pin_pd->keyval的按键按下时的事件 */ 

input_event(buttons_dev, EV_KEY, pin_pd->keyval, 1); 

/* 上报完事件后应上报一个同步信号告诉内核,已上报完 */

input_sync(buttons_dev);


以上内容全是本人凭个人理解写出的:如有错误请多包涵,若能指出,本人将万分感激。

阅读(1770) | 评论(0) | 转发(0) |
0

上一篇:字符设备驱动

下一篇:总线设备驱动

给主人留下些什么吧!~~