实际工程中,不能直接通过延时来消抖 ! 这里我们采用定时器来消抖,这也是内核处理消抖的一种方式。
目录
一、基本原理
1、延时消抖的弊端
2、定时器消抖原理
二、按键消抖实现
1、按键中断
2、定时器中断
三、附加:按键 / 定时器中断初始化
1、按键初始化
2、定时器初始化
一、基本原理
1、延时消抖的弊端
按键按下的时候,存在抖动,延时消抖其实就是不管中间抖了多少下,全都忽略。这样的话,不确定因素太多。
- 延时设置的过短可能无法达到消抖的效果;
- 延时设置的过长可能会存在“第一次按键按下没反应,第二次才有反应”的情况。这就是延时过长导致第一次按下被无视。
2、定时器消抖原理
定时器消抖则是考虑了每一次的抖动。当 t1 时刻产生一次抖动时,我们启动一个定时器,定时器的周期为 10 ms(10ms已经基本覆盖了全部的抖动)
第二次检测到抖动的时候,此时按理说还没有触发中断,我们再次启动定时器,第一次和第二次用的同一个定时器,所以这次定时器启动时会重新计数。
如此往复,直到最后一次抖动出现之前,定时器在不断被刷新计数,始终没有触发过中断,即没有调用过中断服务函数。
当最后一次抖动出现时,说明抖动过程已经结束,此时会调用中断服务函数,中断服务函数里要做的就是按键按下以后的处理逻辑。
这里涉及到两个中断,分别是按键中断、定时器中断
- 按键中断:中断触发时,启动定时器
- 定时器中断:中断触发时,执行按键按下以后的处理逻辑
二、按键消抖实现
定时器实现按键消抖的关键在于设置两个中断服务函数。
1、按键中断
每次按键中断触发的时候(假设是下降沿触发),我们都要重新启动 EPIT1 定时器,此时定时器就会刷新计数,重新开始计时。
void key_irqhandler(void* userParams)
{
restart_epit(33000000/100); // 启动定时器(每隔10ms触发一次中断)
GPIO1_ISR |= (1 << 18);
}
restart_epit 函数在最后的《定时器中断初始化》部分。在使用 restart_epit 之前,需要在 main.c 中先调用 epit_init 函数初始化定时器。
2、定时器中断
按键抖动的时候,不会触发定时器中断,只有当按键出现最后一次抖动时(按键抖动过程结束),才会触发定时器中断,此时再实现我们原本希望按键按下时该做的事。
假设我们希望按键按下时,切换LED灯的状态。
void epit_irqhandler(void* userParams)
{
stop_epit(); // 防止处理中断时,按键再次被按下,影响现有的中断处理
if (((GPIO1_DR >> 18) & 0x01) == 0)
{
switch_led(epit_stat);
epit_stat = !epit_stat;
}
EPIT1_SR |= 1; // 清除中断标志位
}
三、附加:按键 / 定时器中断初始化
使用中断之前,必然需要先进行中断初始化,这里附上按键和定时器的初始化操作。
1、按键初始化
/* 按键初始化 */
void key_init()
{
/* 1、设置IO复用为GPIO1_IO03 */
IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_B = 0x5;
/* 2、初始化复用引脚,设置电气属性 */
IOMUXC_SW_PAD_CTL_PAD_UART1_CTS_B = 0xF080;
/* 3、初始化GPIO1 */
GPIO1_GDIR &= ~(1 << 18);
/* 4、按键中断初始化 */
key_int_init();
}
/* 按键中断初始化 */
void key_int_init()
{
/* 使能GIC 中 IRQ 的某个外设对应的中断 */
GIC_EnableIRQ(99); // 99 表示 GPIO1_IO16 ~ 31 的中断
// GPIO1 中断使能
GPIO1_IMR |= (1 << 18);
// 配置 GPIO1_IO18 中断触发方式(下降沿触发)
GPIO1_EDGE_SEL &= ~(1 << 18);
GPIO1_ICR2 |= (3 << 4);
// 注册中断服务函数
register_irqhandler(99, key_irqhandler);
}
2、定时器初始化
定时器初始化时,暂时先不打开定时器,因为要触发了按键中断以后再启动定时器。
/*
* frac: 代表分频数
* interval: 代表计数器的初始值
*/
void epit_init(unsigned int frac, unsigned int value)
{
if (frac < 1 )
frac = 1;
else if(frac > 4096)
frac = 4096;
EPIT1_CR = 0; // CR 寄存器清零(停止定时器)
/* EPIT1 使能、分频、时钟源初始化 */
EPIT1_CR |=(0xE | ((frac - 1) << 4) | (0x1 << 24));
/* 加载寄存器 */
EPIT1_LR = value;
/* 比较寄存器 */
EPIT1_CMPR = 0;
/* EPIT1 中断使能 */
GIC_EnableIRQ(88);
/* 注册中断服务函数 */
register_irqhandler(88, epit_irqhandler);
/* 计数寄存器 */
// EPIT1_CNR = value;
// EPIT1_CR |= 1; // 启动定时器
}
/*
* 停止定时器
*/
void stop_epit()
{
EPIT1_CR &= ~(1 << 0);
}
/*
* 启动定时器
*/
void restart_epit(unsigned int val)
{
stop_epit();
EPIT1_LR = val;
EPIT1_CR |= 1; // 启动定时器
}