前言
一开始在执行按键扫描是都会使用延时来进行消抖,使用这种方式的消抖会严重的浪费单片机的性能,这里介绍使用使用有限状态机的方式来扫描按键。
一、简介
按键扫描的消抖一般会延时10ms,按键扫描程序的状态一般分为:
状态一、判断按键是否按下,如果按下则跳转到状态二。
状态二、再次判断按键是否按下,如果有就跳转到状态三,否则跳转到状态一。
状态三、等待按键释放,并累加时间判断是否是长按,如果超过设置的长按的时间,跳转到状态四,否则跳转到状态一,并返回键值。
状态四、继续累加长按时间,连续触发按键并返回键值,否则跳转到状态一。
二、程序代码
1.扫描俺键
/**
* @brief 获取IO口的值
* @param
* @retval
*/
static uint8_t IndependentKey_Get(void)
{
uint8_t key = 0x00;
if(HAL_GPIO_Read(&KEY1)) key = 0x01;
else if(HAL_GPIO_Read(&KEY2)) key = 0x02;
else if(HAL_GPIO_Read(&KEY3)) key = 0x04;
else if(HAL_GPIO_Read(&KEY4)) key = 0x08;
return key;
}
2.获取键值
/**
* @brief 扫描按键
* @param
* @retval 约定返回的低8位为按键按下的编号,高8位bit0为长按标志
*/
uint16_t IndependentKey_Scan(void)
{
static uint8_t state = 0;
static uint16_t clickTime = 0;
static uint16_t keyValue;//按键值
uint16_t tempKeyValue = 0;//临时按键
uint16_t reValue = 0;//返回值
tempKeyValue = IndependentKey_Get();//获取键值
switch (state)
{
case 0://状态0 等待按键按下
if (tempKeyValue != 0)//检测按键是否按下,不为0表示有按键按下
{
state = 1;//迁移到状态1
keyValue = tempKeyValue;//保存当前键值
}
break;
case 1://状态1 消抖
if (tempKeyValue == keyValue)//检测按键是否改变,如果没有改变迁移到状态2,否则迁移到状态0重新检测
{
clickTime = 0;
state = 2;
}
else
state = 0;
break;
case 2://状态2 已确认按下,判断是否是长按
if (tempKeyValue == keyValue)//当前键值没有释放并且并且键值相同,开始累加长按计时
{
if (++clickTime > LONG_CLICK_TIME_1)//还在按下,计时判断是否是长按
{
state = 3;//长按条件成立,迁移到状态3,
clickTime = 0;
reValue = keyValue | 0x0100;//放回长按键值
}
}
else//按键已释放或键值改变,返回当前键值
{
state = 0;
reValue = keyValue;
}
break;
case 3://状态三,长按中
if (tempKeyValue == keyValue)//键值未改变,累加长按计时
{
if (++clickTime > LONG_CLICK_TIME_2)//还在按下,计时判断是否是长按
{
clickTime = 0;
reValue = keyValue | 0x0100;//触发长按,返回键值
}
}
else
{
state = 0;
}
break;
}
return reValue;
}
三、IndependentKey.h
/**
* @Author: 时间世纪
* @Date: 2022-08-14 20:34:43
* @Description:
*/
#ifndef _INDEPENDENT_KEY_H_
#define _INDEPENDENT_KEY_H_
#include "stm32f4xx.h"
#include "HAL_Driver.h"
#define LONG_CLICK_TIME_1 100 //第一次触发长按,为IndependentKey_Scan函数调用的间隔时间*LONG_CLICK_TIME_1
#define LONG_CLICK_TIME_2 50//触发长按后的连续触发时间
/**
* @brief 扫描按键,应每10ms调用一次
* @param
* @retval 约定返回的低8位为按键按下的编号,高8位bit0为长按标志
*/
extern uint16_t IndependentKey_Scan(void);
#endif