十.定时器EPIT——2按键消抖

在讲中断那一章我们留了个BUG:中断服务函数应该是快进快出的,中间是严禁使用定时函数的,那样会严重浪费CPU的性能,在delay中什么事都做不了。而机械按键确实要考虑到消抖的效果,所以使用定时器来实现功能。 

定时器消抖原理

按键消抖的原理网上一搜一大把,这里就不说了,总之就是按下按钮当时不反应,过一会再去确认一下按键的值。这个“过一会”我们前面都是通过延时去刷新的,那么在延时这段时间里,CPU就阻塞了,什么也干不了。如果改用定时器,就可以设置好定时,时间到了触发中断,那么在定时器计时这段时间里是不需要CPU资源的,只需要等时间到了触发中断事件即可。

 借用教程里面的图例,上面的电平就是类似按键抖动的电平变化效果,我们可以定义按键下降沿的时候启动EPIT定时器,比如t1,启动了10ms的定时,但是还没到10ms时t2处又将定时器重置了,而t3时间将定时器重置,t3过10ms内没有新的下降沿重置定时器,就触发定时中断,在这个中断事件响应我们要实现的功能就可以了。

使用EPIT进行按键消抖的流程

根据前面说的消抖原理,整个流程应该是这样的:

  1. 初始化按键中断、EPIT定时器(EPIT只初始化,不启动)
  2. 按键按下,触发按键中断
  3. 按键中断服务启动EPIT定时器,清除中断标志位
  4. 定时器未到时,按键抖动,重新触发中断引发定时器重新计时
  5. 定时器到时,EPIT定时中断触发,清除中断标志位
  6. EPIT中断服务函数被调用。要注意的是,我们需要响应按键按下功能的函数应该是对应EPIT中断服务函数,按键的中断只是引发EPIT计时器启动。

代码构成

bsp文件夹下新建目录

 

这个代码没什么细节需要讲,主要就是通过一个中断启动另一个中断。并且在定时中断中只执行一个定时循环。

#include "bsp_keyfilter.h"
#include "bsp_int.h"
#include "bsp_beep.h"

/*
* @description            :   带消抖的按键初始化
* @param                  :   None
* @return                 :   None
*/
void keyfilter_init(void)
{
    gpio_pin_config_t key_config;

    IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);          //GPIO复用初始化
    IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xF080);  //GPIO电气初始化

    key_config.direction = kGPIO_DigitalInput;                  //GPIO设置为输入方向
    key_config.interruptMode = kGPIO_IntFallingEdge;            //GPIO中断信号为下降沿触发

    gpio_init(GPIO1,18,&key_config);                            //GPIO初始化

    GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);                   //GIC使能GPIO1中断
    system_register_irqHandler(GPIO1_Combined_16_31_IRQn,
                               gpio1_16_31_irqhandler,
                               NULL);                           //注册中断函数
    
    gpio_intenable(GPIO1,18);                                   //GPIO中断使能

    // filtertimer_init(66000000/100);                             //按键消抖定时器初始化
    filtertimer_init();
}   


/*
* @description            :   按键消抖定时器初始化
* @param-value            :   EPIT_LR寄存器设定值
* @return                 :   None
*/
void filtertimer_init(void)
{
    EPIT1->CR = 0;                                              //清除EPIT1_CR
    EPIT1->CR |= (1<<1) |(1<<2) |(1<<3) |(1<<24);               //设置EPIT1_CR
    EPIT1->LR = 0;                                              //设置EPIT1_LR 
    EPIT1->CMPR = 0;                                            //设置EPIT1_CMPR

    GIC_EnableIRQ(EPIT1_IRQn);                                  //GIC使能EPIT1

    system_register_irqHandler(EPIT1_IRQn,
                               filter_irqhandler,
                               NULL);                           //EPIT1中断服务函数注册
}


/*
* @description            :   GPIO1_IO16~31对应中断服务
* @param                  :   None
* @return                 :   None
*/
void gpio1_16_31_irqhandler(int gicciar,void *param)
{
    /*开启定时器*/
    filtertimer_restart(66000000/100);                          //重启EPIT定时器,定时10ms
    gpio_clearIntFlags(GPIO1,18);                               //清除gpio中断标志位

}

/*
* @description            :   EPIT1定时停止
* @param                  :   None
* @return                 :   None
*/
void filtertimer_stop(void)
{
    EPIT1->CR &= ~(1<<0);
}

/*
* @description            :   EPIT1定时功能重启
* @param-value            :   LR的寄存器的值
* @return                 :   None
*/
void filtertimer_restart(unsigned int value)
{
    EPIT1->CR &= ~(1<<0);
    EPIT1->LR = value;
    EPIT1->CR |= 1<<0;
}

/*
* @description            :   EPIT1中断服务函数
* @param                  :   None
* @return                 :   None
*/
void filter_irqhandler(int gicciar,void *param)
{
    static unsigned char state = OFF;
    
    if(EPIT1->SR &= (1<<0)) //EPIT定时10ms时间
    {   
        filtertimer_stop();
        if(gpio_pinread(GPIO1,18) == 0)
        {
            state = !state;
            beep_switch(state);
        }
    }
    EPIT1->SR |= (1<<0);        //清除EPIT1_SR标志位
}

当然还有头文件

#ifndef __BSP_KEYFILTER_H
#define __BSP_KEYFILTER_H

#include "imx6ul.h"
#include "bsp_gpio.h"


void filtertimer_stop(void);
void filtertimer_restart(unsigned int value);

void keyfilter_init(void);
void filtertimer_init(void);

void gpio1_16_31_irqhandler(int gicciar,void *param);
void filter_irqhandler(int gicciar,void *param);
#endif#ifndef __BSP_KEYFILTER_H
#define __BSP_KEYFILTER_H

#include "imx6ul.h"
#include "bsp_gpio.h"


void filtertimer_stop(void);
void filtertimer_restart(unsigned int value);

void keyfilter_init(void);
void filtertimer_init(void);

void gpio1_16_31_irqhandler(int gicciar,void *param);
void filter_irqhandler(int gicciar,void *param);
#endif
bsp_keyfilter.h

除了最后控制蜂鸣器的函数整个流程的备注还是很清楚的。在main.c里直接引用就可以了。

keyfilter_init();    //使用通过中断消抖的按键
原文地址:https://www.cnblogs.com/yinsedeyinse/p/15796078.html