• 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 }

     运行过程如下:

  • 相关阅读:
    用指针方法排序数组
    struct和typedef struct
    结构体类型定义的一般式
    HDOJ1020 Encoding
    malloc函数详解
    新手入门 acm 输入输出练习
    【算法入门】广度/宽度优先搜索(BFS)
    C++栈和队列
    hdu畅通工程
    codevs 2639 约会计划
  • 原文地址:https://www.cnblogs.com/wmx-learn/p/5365385.html
Copyright © 2020-2023  润新知