基于STM32按键的防抖和松开处理:状态机
用延时和while();去处理按键很浪费资源,这里我们用定时器来做一个按键的处理-状态机;
typedef enum {KEY_RELEASED,KEY_PRESSED,KEY_PROCESSED}KEY_STATUS;//释放 按下 处理过的
typedef struct {uint8_t byCounter;//按键是否有效KEY_STATUS eKeyStatus;//按键状态
}KEY_ATTRIB;
KEY_ATTRIB Key_up,Key0,Key1,Key2;
首先我们定义了一个枚举,里面有三个元素,这个三个元素代表按键的三种状态。,按键松开状态,,按键按下状态
,按键已被处理状态
然后我们在定义一个结构体,里面有两个类型一个按键检测标志(后面我们将通过这个标志来防抖及松开判断),一个为该按键当前状态。
定义四个按键,Key0,Key1,Key2
我们用的硬件为原子的阿波罗开发板,芯片是,上面有四个按键
这里我们将四个按键连接的引脚配置为输入,然后在开一个定时器4,周期为10ms。在将串口2打开。
这是我们的定时器回调函数
void HAL_TIM_PeriodElapsedCallback (TIM_HandleTypeDef *htim)
{if (htim->Instance == htim4.Instance){Key_up.byCounter <<= 1;Key0.byCounter <<= 1;Key1.byCounter <<= 1;Key2.byCounter <<= 1;if ( HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==1)//KEYUP{Key_up.byCounter++;if (Key_up.byCounter==0xff)//检测按键是否有效{if (Key_up.eKeyStatus != KEY_PROCESSED)//判断是否为处理过的按键Key_up.eKeyStatus = KEY_PRESSED;//标记为一个未处理的按键}}else{if ((Key_up.byCounter == 0x00)&&(Key_up.eKeyStatus == KEY_PROCESSED))Key_up.eKeyStatus = KEY_RELEASED;//标记为一个被释放的按键}if ( HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_3)==0)//KEY0{Key0.byCounter++;if (Key0.byCounter==0xff)//检测按键是否有效{if (Key0.eKeyStatus != KEY_PROCESSED)//判断是否为处理过的按键Key0.eKeyStatus = KEY_PRESSED;//标记为一个未处理的按键}}else{if ((Key0.byCounter == 0x00)&&(Key0.eKeyStatus == KEY_PROCESSED))Key0.eKeyStatus = KEY_RELEASED;//标记为一个被释放的按键}if ( HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_2)==0)//KEY1{Key1.byCounter++;if (Key1.byCounter==0xff)//检测按键是否有效{if (Key1.eKeyStatus != KEY_PROCESSED)//判断是否为处理过的按键Key1.eKeyStatus = KEY_PRESSED;//标记为一个未处理的按键}}else{if ((Key1.byCounter == 0x00)&&(Key1.eKeyStatus == KEY_PROCESSED))Key1.eKeyStatus = KEY_RELEASED;//标记为一个被释放的按键}if ( HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)==0)//KEY2{Key2.byCounter++;if (Key2.byCounter==0xff)//检测按键是否有效{if (Key2.eKeyStatus != KEY_PROCESSED)//判断是否为处理过的按键Key2.eKeyStatus = KEY_PRESSED;//标记为一个未处理的按键}}else{if ((Key2.byCounter == 0x00)&&(Key2.eKeyStatus == KEY_PROCESSED))Key2.eKeyStatus = KEY_RELEASED;//标记为一个被释放的按键}}}
在tim4的回调函数里我们将四个按键的向左1移动一位。
然后当有按键按下,比如键按下,
开始自增。
第一次进回调函数,
.左移一位,.值为0(初始默认为0)
然后.自增,
.为0x01,
第二次进入回调函数
.左移一位,.值为0x02
然后.自增,
.为0x03,
以此类推,第八次进入回调函数时.自增后为0xff,这时我们就判定他为有效按键,这样其实就是我们的防抖为80ms,在这里我们目前先将按键模式规定为按下后必须松开,再次按下才行,这样当我们判定为有效按键时,我们接着判断是不是一个处理过的按键,不是OK,标记为新的按键
if (. != )//判断按键是否被处理过
. = ;//标志为一个未处理的按键
然后当松开按键,因为每次进回调函数,都会左移,所以松开按键80ms后
会为0,也就是松开的防抖80ms。我们再去判断一下按键被处理了没,处理了就将按键标记为释放(其实这个也可以不用判断处理了没,当为0我们就可以直接标记为释放,因为80ms,对于芯片算很长一段时间了)。
Key1、Key2、Key3的原理同上。
然后就去处理按键
if(Key_up.eKeyStatus==KEY_PRESSED){HAL_UART_Transmit(&huart2, (uint8_t *)"Key_up", 6, 0xFFFF);Key_up.eKeyStatus=KEY_PROCESSED;}if(Key0.eKeyStatus==KEY_PRESSED){HAL_UART_Transmit(&huart2, (uint8_t *)"Key0", 4, 0xFFFF);Key0.eKeyStatus=KEY_PROCESSED;}if(Key1.eKeyStatus==KEY_PRESSED){HAL_UART_Transmit(&huart2, (uint8_t *)"Key1", 4, 0xFFFF);Key1.eKeyStatus=KEY_PROCESSED;}if(Key2.eKeyStatus==KEY_PRESSED){HAL_UART_Transmit(&huart2, (uint8_t *)"Key2", 4, 0xFFFF);Key2.eKeyStatus=KEY_PROCESSED;}
这里当检测到按键按下时,向串口发送其按键的名字,然后将按键标记为处理过的按键。
当然可能有些朋友觉得这样有点烦,假如我要从0加到100(假设就一个按键),就得按100下,那就是需要连续响应,这里只需你在处理完按键,将标记为处理过的按键步骤改为将清0然后,标记按键为释放。这样你一直按着就会不断的去响应,清0 主要起一个延时的作用,不然响应太快!
if(Key_up.eKeyStatus==KEY_PRESSED){test++;HAL_UART_Transmit(&huart2, (uint8_t *)&test, 1, 0xFFFF);Key_up.byCounter=0;Key_up.eKeyStatus=KEY_RELEASED;}
采用在这种机制,当按键一直在按下状态,也不会影响其他按键的操作。