12. 定时器按键消抖
- 定时器按键消抖简介
- 定时器消抖配置步骤
- 程序编写
- bsp_keyfilter.h
- bsp_keyfilter.c
- main
定时器按键消抖简介
使用延时函数消抖会浪费 CPU 性能,因为延时函数就是空跑。如果按键是使用中断的方式实现的,就更不能在中断服务函数中使用延时函数,因为中断服务函数最基本的要求就是快进快出。 所以可以使用定时器设置好时间,就可以去跑其他的进程,当时间到了,就会触发中断,然后在中断函数中做相应的处理。
需要在图中t1 ~ t3 这个时间段消抖。设置按键为下降沿触发,因此会在t1、t2和t3这3个时刻会触发按键中断,每次进入中断处理函数都会重新开启定时器中断。但是t1 ~ t2 和 t2 ~ t3 这两个时间段是小于设定的定时器时间,所以虽然 t1 开启了定时器,但是定时器定时器时间还没到,就重置了定时器,最终只有t3时刻开启的定时器能完整的完成整个定时器周期并触发中断
定时器消抖配置步骤
- 配置按键 IO 中断
配置按键所使用的 IO,因为要使用到中断驱动按键,所以要配置 IO 的中断模式 - 初始化消抖用的定时器
- 编写中断处理函数
需要两个中断处理函数:按键对应的 GPIO 中断处理函数和 EPIT1 定时器的中断处理函数。按键中断处理函数主要是开启定时器。
程序编写
bsp_keyfilter.h
#pragma once
void filterkey_init();
void filtertimer_init(unsigned int value);
void filtertimer_stop();
void filtertimer_restart(unsigned int value);
void filtertimer_irqhandler();
void gpio1_16_31_irqhandler();
bsp_keyfilter.c
#include "bsp_key.h"
#include "bsp_gpio.h"
#include "bsp_int.h"
#include "bsp_beep.h"
#include "bsp_keyfilter.h"
void fileterkey_init()
{
gpio_pin_config_t key_config;
// 1. 初始化 IO
IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);
IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF080);
// 2. 初始化 GPIO 为中断
key_config.direction = kGPIO_DigitalInput;
key_config.interruptMode = kGPIO_IntFallingEdge;
key_config.outputLogic = 1;
gpio_init(GPIO1, 18, &key_config);
// 使能 GPIO 中断,并且注册中断处理函数
GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);
system_register_irqhandler(GPIO1_Combined_16_31_IRQn,(system_irq_handler_t)gpio1_16_31_irqhandler,NULL);
gpio_enableint(GPIO1, 18);// 使能GPIO1_IO18中断
filetertimer_init(66000000/100);// 初始化定时器10ms
}
/*
* @description : 初始化用于消抖的定时器,默认关闭定时器
* @param - value : 定时器EPIT计数值
* @return : 无
*/
void filtertimer_init(unsigned int value)
{
EPIT1->CR = 0; //先清零
/*
* CR寄存器:
* bit25:24 01 时钟源选择Peripheral clock=66MHz
* bit15:4 0 1分频
* bit3: 1 当计数器到0的话从LR重新加载数值
* bit2: 1 比较中断使能
* bit1: 1 初始计数值来源于LR寄存器值
* bit0: 0 先关闭EPIT1
*/
EPIT1->CR = (1<<24 | 1<<3 | 1<<2 | 1<<1);
/* 计数值 */
EPIT1->LR = value;
/* 比较寄存器,当计数器值和此寄存器值相等的话就会产生中断 */
EPIT1->CMPR = 0;
GIC_EnableIRQ(EPIT1_IRQn); /* 使能GIC中对应的中断 */
/* 注册中断服务函数 */
system_register_irqhandler(EPIT1_IRQn, (system_irq_handler_t)filtertimer_irqhandler, NULL);
}
void filtertimer_stop(void)
{
EPIT1->CR &= ~(1<<0); /* 关闭定时器 */
}
/*
* @description : 重启定时器
* @param - value : 定时器EPIT计数值
* @return : 无
*/
void filtertimer_restart(unsigned int value)
{
EPIT1->CR &= ~(1<<0); /* 先关闭定时器 */
EPIT1->LR = value; /* 计数值 */
EPIT1->CR |= (1<<0); /* 打开定时器 */
}
void filtertimer_irqhandler(void)
{
static unsigned char state = OFF;
if(EPIT1->SR & (1<<0)) // 判断比较事件是否发生
{
filtertimer_stop(); // 关闭定时器
if(gpio_pinread(GPIO1, 18) == 0) // KEY0
{
state = !state;
beep_switch(state); // 反转蜂鸣器
}
}
EPIT1->SR |= 1<<0; // 清除中断标志位
}
void gpio1_16_31_irqhandler(void)
{
// 开启定时器
filtertimer_restart(66000000/100);
// 清除中断标志位
gpio_clearintflags(GPIO1, 18);
}
main
int main(void)
{
unsigned char state = OFF;
int_init(); // 初始化中断(一定要最先调用)
imx6u_clkinit();
clk_enable();
led_init();
beep_init();
filterkey_init();
while(1)
{
state = !state;
led_switch(LED0, state);
delay(500);
}
return 0;
}