【STM32】定时器中断实现按键检测及消抖(标准库)

之前也写过一篇利用定时器实现按键检测及消抖的文章,但是那篇代码量太大,而且不便于移植,还有一堆不明变量,简直就是屎山代码,这次使用结构体数组优化一下,原理不变

一、基本原理

这是一个按键从按下到松开的过程

传统用while死循环实现按键的松手检测,但是这样会使程序卡死,浪费CPU资源,在延时函数里只进行+1+1+1+1的操作,对程序运行的实时性很不友好

我们使用定时器每5ms扫描一次按键,当检测到按键被按下时,我们的按键计数器(LongPressCount)就开始计数,每5ms增加一次,当检测到按键被松开时停止计数,接着通过判断LongPressCount的值来判断按键按下的状态(没按下、短按和长按),当LongPressCount的值在0-4之间时就意味着按键在0-20ms之间就松开了,就判定为没有按键按下,计数器继续计数,当LongPressCount的值在4-200之间,意味着按键在20-1000ms之间松开,判定为短按,将按键的状态设置为短按并清除LongPressCount,那长按就是LongPressCount大于200了。

现在假如我们的按键没有抖动,我们要实现按键按下一次a++但是不许使用delay和while,最简单的逻辑是这样的(伪代码)

while (1)
{
    if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_14) == 0)
    {
	    a++;
    }
}

但是问题也随之而来,当我按键按下的时候a会一直++,所以我们要定义一个变量Key_flag,默认为0,当按键按下后flag变为1,不再执行a++,逻辑是这样的

while (1)
{
	if(Key_flag == 0)
	{
		if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_14) == 0)
		{
			a++;
			Key_flag = 1;
		}			
	}
}

这样的话第一次按下正常,第二次按下就没反应,因为按键松开的时候还要恢复标志位,当标志位还是被置位(flag==1)时,如果按键是松开的就清除标志位

while (1)
{
	if(Key_flag == 0)
	{
		if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_14) == 0)
		{
			a++;
			Key_flag = 1;
		}			
	}
	if(Key_flag == 1)
	{
		if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_14) == 1)
		{
			Key_flag = 0;
		}			
	}
}

以上就是一个不使用while死循环实现松手检测的例子,记住这种思想,下面要用

二、程序设计

1、定时器中断

void Timer_Init(uint16_t Psc, uint16_t Per)
{
	/*开启时钟*/	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
	
	TIM_InternalClockConfig(TIM6);
	
	/*时基单元初始化*/	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = Per - 1;
	TIM_TimeBaseInitStructure.TIM_Prescaler = Psc - 1;
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM6, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM6, TIM_FLAG_Update);
	TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
	
	/*中断配置*/	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 
以下是使用标准库写的stm32按键中断代码: 首先,需要配置GPIO口作为输入,设置中断触发方式为下降沿或上升沿触发。这可以通过HAL库的GPIO_Init函数来实现。 ```c GPIO_InitTypeDef GPIO_InitStruct; // 配置PA0作为输入,下拉电阻,中断触发方式为下降沿触发 GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLDOWN; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 配置PA0的中断触发方式为下降沿触发 HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI0_IRQn); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); ``` 接下来,需要在中断处理函数中实现按键。这里使用了一个定时器,每次按键按下时启动定时器定时器到达设定的时间后再进行判断,如果按键仍然处于按下状态,就认为是有效按下。 ```c // 定义定时器相关变量 TIM_HandleTypeDef htim; // 定义按键状态变量 uint8_t button_state = 0; uint32_t button_press_time = 0; void EXTI0_IRQHandler(void) { // 判断中断是否来自PA0 if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET) { // 清除中断标志位 __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0); // 按键按下 if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET) { // 启动定时器 HAL_TIM_Base_Start_IT(&htim); button_press_time = HAL_GetTick(); } // 按键松开 else { // 停止定时器 HAL_TIM_Base_Stop_IT(&htim); // 判断按键是否有效按下 if (button_state == 1) { // 进行按键操作 // ... } // 复位按键状态变量 button_state = 0; } } } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { // 判断定时器是否为htim if (htim == &htim) { // 如果按键仍然处于按下状态 if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET) { // 设置按键状态变量 button_state = 1; } // 如果按键已经松开 else { // 停止定时器 HAL_TIM_Base_Stop_IT(&htim); // 复位按键状态变量 button_state = 0; } } } ``` 需要注意的是,需要在程序初始化时配置定时器,并启动定时器时钟。 ```c // 配置定时器 htim.Instance = TIM3; htim.Init.Prescaler = 72 - 1; htim.Init.CounterMode = TIM_COUNTERMODE_UP; htim.Init.Period = 1000; htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(&htim); // 启动定时器时钟 HAL_TIM_Base_Start_IT(&htim); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值