12. 定时器按键消抖

12. 定时器按键消抖

  • 定时器按键消抖简介
  • 定时器消抖配置步骤
  • 程序编写
    • bsp_keyfilter.h
    • bsp_keyfilter.c
    • main

定时器按键消抖简介

使用延时函数消抖会浪费 CPU 性能,因为延时函数就是空跑。如果按键是使用中断的方式实现的,就更不能在中断服务函数中使用延时函数,因为中断服务函数最基本的要求就是快进快出。 所以可以使用定时器设置好时间,就可以去跑其他的进程,当时间到了,就会触发中断,然后在中断函数中做相应的处理。
12. 定时器按键消抖_第1张图片
需要在图中t1 ~ t3 这个时间段消抖。设置按键为下降沿触发,因此会在t1、t2和t3这3个时刻会触发按键中断,每次进入中断处理函数都会重新开启定时器中断。但是t1 ~ t2 和 t2 ~ t3 这两个时间段是小于设定的定时器时间,所以虽然 t1 开启了定时器,但是定时器定时器时间还没到,就重置了定时器,最终只有t3时刻开启的定时器能完整的完成整个定时器周期并触发中断

定时器消抖配置步骤

  1. 配置按键 IO 中断
    配置按键所使用的 IO,因为要使用到中断驱动按键,所以要配置 IO 的中断模式
  2. 初始化消抖用的定时器
  3. 编写中断处理函数
    需要两个中断处理函数:按键对应的 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;
}

你可能感兴趣的:(嵌入式裸机ARM驱动开发,嵌入式硬件)