15.linux按键驱动程序(二)

        linux按键驱动程序

  包含内容定时器延时去抖动,阻塞型设备驱动设计

一、定时器延时去抖

  按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,开关不会马上稳定地接通或断开。因而在闭合及断开的瞬间总是伴有一连串的抖动的。按键去抖动的方法主要有两种,一种是硬件电路去抖动;另一种就是软件延时去抖动。而延时又一般分为了两种,一种是for循环等待,另一种是定时器延时。在操作系统中,由于效率方面的原因,一使用定时器。

1.1内核定时器

定时器的使用分为了四个步骤:

  1.定义定时器的变量,就是timer_list结构。

  2.要对结构进行初始化。Init_timer是系统自动运行的初始化函数,能初始化很大部分timer_list里面的成员。但是,超时函数是需要我们自己设置,就是function。

  3.使用add_timer函数注册定时器。

  4.mod_timer重启定时器。注意,定时器不是循环的,需要重复调用mod_timer函数。

Linux内核使用struct timer_list来描述一个定时器:

1 struct timer_list{
2     struct list_head entry;
3     unsigned long expires;
4     void (*function)(unsigned long);
5     unsigned long data;
6     struct tvec_base *base;
7 };

  其中expires是定时器延时的时间,function函数是定时器超时后要做的事情。

1.2使用内核定时器

二、阻塞型驱动程序设计

  当一个设备无法立刻满足用户的读写请求时应当如何处理? 例如:调用read时,设备没有数据提供, 但以后可能会有;或者一个进程试图向设备写入数据,但是设备暂时没有准备好接收数据。当上述情况发生的时候,驱动程序应当(缺省地)阻塞进程,使它进入等待(睡眠)状态,直到请求可以得到满足。

2.1内核等待队列

  在实现阻塞驱动的过程中,也需要有一个“候车室”来安排被阻塞的进程“休息”,当唤醒它们的条件成熟时,则可以从“候车室”中将这些进程唤醒。而这个“候车室”就是等待队列。

2.2队列描述

 1、定义等待队列

  wait_queue_head_t my_queue
2、初始化等待队列
  init_waitqueue_head(&my_queue)
3、定义+初始化等待队列
  DECLARE_WAIT_QUEUE_HEAD(my_queue)

4、进入等待队列,睡眠

4.1 wait_event(queue,condition)
  当condition(布尔表达式)为真时,立即返回;否则让进程
  进入TASK_UNINTERRUPTIBLE模式的睡眠,并挂在queue参数所指定的等待队列上。

4.2wait_event_interruptible(queue,condition)
  当condition(布尔表达式)为真时,立即返回;否则让

  进程进入TASK_INTERRUPTIBLE的睡眠,并挂在queue参数所指定的等待队列上。

 4.3int wait_event_killable(queue, condition)

  当condition(一个布尔表达式)为真时,立即返回;否则让进程进入TASK_KILLABLE的睡眠,并挂在queue参数所指定的等待队列上。

5、从等待队列中唤醒进程
5.1 wake_up(wait_queue_t *q)
  从等待队列q中唤醒状态为TASK_UNINTERRUPTIBLE,TASK_INTERRUPTIBLE,TASK_KILLABLE 的所有进程。

5.2 wake_up_interruptible(wait_queue_t *q)
  从等待队列q中唤醒状态为TASK_INTERRUPTIBLE 的进程

 按键阻塞代码如下(也是关于按键所有的程序):

  1 #include <linux/module.h>
  2 #include <linux/init.h>
  3 #include <linux/miscdevice.h>
  4 #include <linux/interrupt.h>
  5 #include <linux/io.h>
  6 #include <linux/fs.h>
  7 #include <linux/slab.h>
  8 #include <linux/uaccess.h>
  9 
 10 #include <linux/sched.h>
 11  
 12 #define GPNCON 0x7f008830
 13 #define GPNDAT 0x7f008834
 14 
 15 MODULE_LICENSE("GPL");
 16 
 17 struct work_struct *work1;
 18 unsigned int *gpio_dat;
 19 struct timer_list key_timer;
 20 
 21 unsigned int key_num = 0;
 22 wait_queue_head_t key_q;
 23 
 24 void work1_func(struct work_struct *work)
 25 {
 26     //启动定时器 100毫秒超时=HZ/10,HZ=1秒。jiffies是系统当前时间
 27     mod_timer(&key_timer,jiffies + (HZ/10));  //启动定时器
 28 }
 29  
 30 void key_timer_func(unsigned long data)  //定时器超时函数
 31 {
 32     unsigned int key_val;
 33     key_val = readw(gpio_dat) &0x01;
 34     if(key_val==0)
 35         key_num = 1;
 36     key_val = readw(gpio_dat) &0x02;
 37     if(key_val==0)
 38         key_num = 2;
 39     wake_up(&key_q);
 40 }
 41  
 42 irqreturn_t key_int(int irp,void *dev_id) //中断处理函数
 43 {
 44     //3.提交下半部
 45     schedule_work(work1);
 46     return IRQ_HANDLED;
 47 }
 48 
 49 void key_hw_init(void)   //硬件初始化
 50 {
 51     unsigned int *gpio_config;
 52     unsigned short data;    
 53     gpio_config = ioremap(GPNCON,4);  //动态映射虚拟地址
 54     data = readw(gpio_config);
 55     data &= ~0xfff;
 56     data |= 0xaaa;
 57     writew(data,gpio_config);
 58     gpio_dat = ioremap(GPNDAT,4);  //将数据寄存器地址转化为虚拟地址
 59 }
 60 
 61 int key_open(struct inode *node,struct file *filp)
 62 {    
 63     return 0;
 64 }
 65 
 66 ssize_t key_read (struct file *filp, char __user *buf, size_t size, loff_t *pos)
 67 {
 68     wait_event(key_q,key_num);   //进入睡眠
 69     
 70     copy_to_user(buf,&key_num,4);
 71     
 72     key_num = 0;
 73     return 4;
 74 }
 75 
 76 struct file_operations key_fops = 
 77 {
 78     .open = key_open,
 79     .read = key_read,
 80 };
 81 
 82 struct miscdevice key_miscdev = 
 83 {
 84     .minor = 200,     //次设备号
 85     .name = "key",
 86     .fops = &key_fops,
 87 };
 88 //驱动程序初始化
 89 static int button_init(void)
 90 {
 91     misc_register(&key_miscdev);   //1注册混杂设备    
 92     //2按键硬件初始化
 93     key_hw_init();
 94     request_irq(IRQ_EINT(0),key_int,IRQF_TRIGGER_FALLING,"key",0);  //注册中断处理程序
 95     request_irq(IRQ_EINT(1),key_int,IRQF_TRIGGER_FALLING,"key",0);  //注册中断处理程序
 96     
 97     //3创建工作1
 98     work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
 99     INIT_WORK(work1,work1_func);
100     
101     //4初始化内核定时器
102     init_timer(&key_timer);
103     key_timer.function = key_timer_func;
104     //注册定时器
105     add_timer(&key_timer);
106     //初始化等待队列
107     init_waitqueue_head(&key_q);    
108     return 0;
109 }
110 
111 static void button_exit(void)
112 {
113     misc_deregister(&key_miscdev);   //注销混杂设备
114     //注销中断处理程序
115     free_irq(IRQ_EINT(0),0);
116     free_irq(IRQ_EINT(1),0);
117 }
118 
119 module_init(button_init);
120 module_exit(button_exit);

 按键应用程序:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 int main()
 5 {
 6     int fd;
 7     int key_num;
 8     
 9     //1.打开设备
10     fd = open("/dev/6410key",0);
11     if(fd<0)
12         printf("open device fail!
");
13     
14     //2.读取设备
15     read(fd,&key_num,4);
16         printf("key is %d
",key_num);
17         
18     //3.关闭设备
19     close(fd);
20 }

 运行过程如下:

原文地址:https://www.cnblogs.com/wmx-learn/p/5365385.html