摘要:前面章节在中断中使用延时实现了消抖。但这种方式并不适合在真正项目中使用,因为中断中不可以让CPU执行无意义的耗时操作,导致占用了CPU,CPU没办法执行其它更重要的任务。
前面章节在中断中使用延时实现了消抖。但这种方式并不适合在真正项目中使用,因为中断中不可以让CPU执行无意义的耗时操作,导致占用了CPU,CPU没办法执行其它更重要的任务。
使用中断实现消抖可以降低CPU的资源占用。实现思路如下:
当检测到按键电平变化时,启动定时器开始计数。设置一个合适的计数时间,这个时间应该大于按键抖动的持续时间。一般来说,按键抖动时间在 5 - 10ms 左右,所以可以设置定时器的计数时间为 10ms。
在定时器计数期间,如果按键电平再次发生变化,就重新启动定时器计数。只有当定时器计数完成(即经过了设定的消抖时间)且按键电平保持稳定时,才认为是一次有效的按键动作。
在主程序中,通过读取按键对应的 GPIO 引脚电平来检测按键状态。当检测到按键电平发生变化(从高到低或者从低到高)时,启动定时器开始计数。
当定时器计数完成产生中断时,进入中断服务程序。在中断服务程序中,首先读取按键的当前电平。如果按键电平与之前检测到的变化后的电平一致,说明按键已经稳定,此时可以认为是一次有效的按键动作,执行相应的按键处理程序,比如执行某个功能函数。
如果在中断服务程序中读取到按键电平与之前变化后的电平不一致,说明在定时期间按键又发生了抖动,此时就不进行按键处理,并且重新启动定时器计数,等待下一次稳定的按键电平。
复制上一章的项目,创建文件 bsp_keyfilter.c 和 bsp_keyfilter.h。
//// Created by Xundh on 2024/11/21.//#include "bsp_keyfilter.h"#include "bsp_gpio.h"#include "bsp_int.h"#include "led.h"/*** @brief 初始化按键过滤器*/void keyfilter_init(void) {gpio_pin_config_t key_config;IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF080);// GPIO 初始化key_config.direction = kGPIO_DigitalInput;key_config.interruptMode = kGPIO_IntFallingEdge;gpio_init(GPIO1, 18, &key_config);// GIC 中断初始化GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);sys_irq_handle_register(GPIO1_Combined_16_31_IRQn, (system_irq_handler_t)keyfilter_irqhandler, NULL);gpio_enableint(GPIO1, 18);// 定时器初始化filter_timer_init(1, 66000000 / 100);}/*** @brief GPIO 中断服务函数*/void keyfilter_irqhandler(unsigned int gicciar, void *param) {// 开启定时器,延时10msfilter_timer_restart(66000000 / 100);// 清除中断标志位gpio_clearintflags(GPIO1, 18);}/*** @brief 关闭EPIT定时器*/void filter_timer_stop(void) {// 关闭定时器EPIT1->CR &= ~(1 CR &= ~(1 LR = value;EPIT1->CR |= 1 CR = 0;// 设置初始值为加载值 | 使能EPIT模块 | 使能中断 | 分频值 1分频 | 使能时钟EPIT1->CR = (1 LR = value;// 设置比较值EPIT1->CMPR = 0;// 使能GIC中断GIC_EnableIRQ(EPIT1_IRQn);// 注册中断服务函数sys_irq_handle_register(EPIT1_IRQn, (system_irq_handler_t)filter_timer_irqhandler, NULL);}/*** @brief EPIT中断服务函数*/void filter_timer_irqhandler(unsigned int gicciar, void *param) {static unsigned char state = 0;// 关闭定时器filter_timer_stop;// 清除中断标志位if(EPIT1->SR & (1 SR |= 1 #include "inc/main.h"#include "bsp_clk.h"#include "bsp_delay.h"#include "led.h"#include "beep.h"#include "key.h"#include "bsp_int.h"//#include "bsp_exti.h"#include "bsp_epit.h"#include "bsp_keyfilter.h"int main(void){bsp_int_init; /* 初始化中断 */imx6u_clkinit; /* 初始化系统时钟 */clk_enable; /* 使能外设时钟 */led_init; /* 初始化LED */beep_init; /* 初始化蜂鸣器 */// exti_init; /* 初始化外部中断 */// epit_init(EPIT1, 0, 66000000 / 2); /* 初始化EPIT1, 1分频, 500ms中断一次 */keyfilter_init; /* 初始化按键 */while(1) {delay(10);}return 0;}编译运行后烧写到开发板。
本节Makefile加了一个编译任务: make eject,用于推出USB设备。
本程序效果是:按下key0键时, LED1 切换亮灭。
来源:编程圈子