字符设备驱动程序之定时器防抖动


当按键按得比较快的时候,这里出现了两次中断值,也即产生了抖动。

这里产生了“抖动”,按键是机械开关,按下松开时里面的金属弹片可能抖动了好几次。这种抖动产生了多次“脉冲”导致多次中断。

方法:

使用定时器来防抖动。

定时器有两个概念:

① 超时时间:

② 时间到了之后的“处理函数”。

可以在中断处理中,如定时 10ms 后处理确定按键值上报。

之后再产生中断:

在中断中加定时器,当遇到 A 中断时加一个 10ms 的定时器,过了 10ms 后就去执行“处理函数”(确定按键值上报)。因为机械的抖动会非常快,没等到 10ms 后的处理,这时因为抖动又来了一个中断 B,这时中断 B 把之前的那个定时器修改了。所以 A 中断的定时器就取消了。最后又来了一个中断 C,同样会修改掉 B 中断的定时器。上图中是假设抖动时产生了 3 个中断,所以对于同一个“定时器”,最终中断 C 的定时器没有被修改,所以 10ms 后由中断 C 的处理函数上报了按键值。最后这个 10ms 是从抖动 C 处开始。这样 3 个抖动的中断只会导致最后处理一个“上报按键值”(定时器过后的处理函数只会执行一次)。以上便是用定时器消除抖动的原理。因为是修改同一个定时器,所以前面的定时又取消,相当于把“闹钟”时间往后调整时,最终只要响一次闹铃。

部分代码解析:

​static struct timer_list buttons_timer;//定义一个定时器

mod_timer(&buttons_timer, jiffies+HZ/100);//修改定时器的值

init_timer(&buttons_timer);//初始化定时器

​buttons_timer.function = buttons_timer_function;//给定时器添加处理函数

​add_timer(&buttons_timer); //将定时器注册进内核,此时定时器启动了

操作步骤:

1.   

static struct timer_list buttons_timer;//定义一个定时器

static int sixth_drv_init(void)
{
    init_timer(&buttons_timer);//初始化定时器
    buttons_timer.function = buttons_timer_function;//给定时器添加处理函数
    //buttons_timer.expires  = 0;
    add_timer(&buttons_timer);
//将定时器注册进内核,此时定时器启动了 

    major = register_chrdev(0, "sixth_drv", &sencod_drv_fops);

    sixthdrv_class = class_create(THIS_MODULE, "sixth_drv");

    /* 为了让mdev根据这些信息来创建设备节点 */
    sixthdrv_class_dev = class_device_create(sixthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */

    gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
    gpfdat = gpfcon + 1;

    gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
    gpgdat = gpgcon + 1;

    return 0;
}

 

2. 按键按下触发中断,

static struct pin_desc *irq_pd;

static irqreturn_t buttons_irq(int irq, void *dev_id)
{
    /* 10ms后启动定时器 */
    irq_pd = (struct pin_desc *)dev_id;      //给定时器处理函数使用
    mod_timer(&buttons_timer, jiffies+HZ/100);   //修改定时器的值,10ms后定时器产生中断

    return IRQ_RETVAL(IRQ_HANDLED);
}

3.  

static void buttons_timer_function(unsigned long data)
{
    struct pin_desc * pindesc = irq_pd;
    unsigned int pinval;

    if (!pindesc)
        return;
    
    pinval = s3c2410_gpio_getpin(pindesc->pin);

    if (pinval)
    {
        /* 松开 */
        key_val = 0x80 | pindesc->key_val;
    }
    else
    {
        /* 按下 */
        key_val = pindesc->key_val;
    }

    ev_press = 1;                  /* 表示中断发生了 */
    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */
    
    kill_fasync (&button_async, SIGIO, POLL_IN);
}

程序流程

当注册了驱动程序后,其实定时器已经启动了。这时我们读取的话,因为没有按键按下返回值为负,所以应用程序里休眠5秒钟,什么也没有做。这时定时器也启动了,那它在干嘛呢?因为此时没有修改定时器的值,它还是初始值0,所以它一直会进入定时器处理函数,但是由于按键没有按下,无法进行后续的处理,也会返回,不会做什么事情!如果按下按键的话,会进入中断处理函数。在中断处理函数中,会将ev_press置为1,并且修改定时器的值。定时器定时一段时间后,会进入定时器处理函数,完成本来应该中断处理程序完成的工作。​

猜你喜欢

转载自blog.csdn.net/qq_39541098/article/details/84522105
今日推荐