一: 前言
当项目或工程有多个按键时,可以考虑使用FIFO 方案实现按键检测和处理。
二:简介
FIFO也就是First in, First out。就是先进先出,后进后出,不进不出。FIFO相当于是一个缓冲环节,可以防止数据丢失。 一般用到不同时域之间的数据传输。比如说单片机按键的速度和CPU去处理按键对应的任务的速度显然是不同的,如果任务非常庞大,可能还没有处理完一个任务,我们已经按了好几次。如果没有FIFO机制,那么后面几次的按键可能都会被忽略,但又不能让CPU去等待按键。所以说可以使用FIFO机制去存储按键事件,等待CPU挨个处理。在单片机的串口也会使用这种方法,挨个发送数据,因为CPU处理的速度显然比串口发送数据的速度高。
三: 配置
通过STM32CubeMX 配置相应GPIO 为输入。
四: 代码
本例中设置了4个按键, 记得要配置gpio,在MX_GPIO_Init()中初始化,然后在主函数每隔10ms扫描一次。10ms可以通过systick 处理。
key_bsp.c
#include "bsp_key.h"
#include "stm32f1xx_hal.h"
#include "main.h"
#define HARD_KEY_NUM 4
#define KEY_COUNT (HARD_KEY_NUM + 0)
static KEY_T KEYS[KEY_COUNT] = {0};
static KEY_FIFO_T KEY_FIFO;
//Put key to FIFO
void Key_Put(uint8_t _KeyCode)
{
KEY_FIFO.Buf[KEY_FIFO.Wp] = _KeyCode;
if (++KEY_FIFO.Wp >= KEY_FIFO_SIZE)
{
KEY_FIFO.Wp = 0;
}
}
//Get key from FIFO
uint8_t Key_Get(){
uint8_t ret;
if (KEY_FIFO.Rp == KEY_FIFO.Wp)
{
return KEY_NONE;
}
else
{
ret = KEY_FIFO.Buf[KEY_FIFO.Rp];
if (++KEY_FIFO.Rp >= KEY_FIFO_SIZE)
{
KEY_FIFO.Rp = 0;
}
return ret;
}
}
static void Key_Detect(uint8_t i)
{
KEY_T *pBtn;
uint8_t pr;
pBtn = &KEYS[i];
if(pBtn->IsPress == NULL) return;
pr = pBtn->IsPress();
if (pr) //whether press down
{
if (pBtn->Count < KEY_FILTER_PERIOD) //press down filter
{
pBtn->Count = KEY_FILTER_PERIOD;
}
else if(pBtn->Count < 2 * KEY_FILTER_PERIOD)//press down filter
{
pBtn->Count++;
}
else
{
if (pBtn->State == 0)
{
// uint16_t temp;
pBtn->State = 1;
/* send press down */
Key_Put((uint8_t)(3 * i + 1));
}
if (pBtn->LongPressPeriod > 0) // Long press set
{
if (pBtn->LongPressCount < pBtn->LongPressPeriod)
{
/* if LongPressCount = LongTime */
if (++pBtn->LongPressCount == pBtn->LongPressPeriod)
{
/* send LongPress */
Key_Put((uint8_t)(3 * i + 3));
}
}
else //repeat send after long press
{
// if set repeatSpeed
if (pBtn->RepeatPeriod > 0)
{
if (++pBtn->RepeatCount >= pBtn->RepeatPeriod)
{
pBtn->RepeatCount = 0;
/* repeat send */
Key_Put((uint8_t)(3 * i + 1));
}
}
}
}
}
}
else //key up
{
if(pBtn->Count > KEY_FILTER_PERIOD) //Count > KEY_FILTER_PERIOD
{
pBtn->Count = KEY_FILTER_PERIOD;
}
else if(pBtn->Count != 0) //Count <= KEY_FILTER_PERIOD
{
pBtn->Count--;
}
else // Count = 0
{
if (pBtn->State == 1)
{
pBtn->State = 0;
/* send key up*/
Key_Put((uint8_t)(3 * i + 2));
}
}
pBtn->LongPressCount = 0;
pBtn->RepeatCount = 0;
}
}
static void Key_InitAllVar(){
uint8_t i;
KEY_FIFO.Rp = 0;
KEY_FIFO.Wp = 0;
for (i = 0; i < KEY_COUNT; i++)
{
KEYS[i].IsPress = NULL;
KEYS[i].LongPressPeriod = KEY_LONG_PERIOD;
KEYS[i].Count = KEY_FILTER_PERIOD / 2;
KEYS[i].State = 0;
KEYS[i].RepeatPeriod = 0;
KEYS[i].RepeatCount = 0;
KEYS[i].LastPressTime = 0;
}
}
void Key_SetParam(uint8_t _ucKeyID, uint16_t _LongPressPeriod, uint8_t _RepeatPeriod, uint8_t (*_IsPress)(void) )
{
KEYS[_ucKeyID].LongPressPeriod = _LongPressPeriod;
KEYS[_ucKeyID].RepeatPeriod = _RepeatPeriod;
KEYS[_ucKeyID].RepeatCount = 0;
KEYS[_ucKeyID].IsPress = _IsPress;
}
static uint8_t Key1_PressedCallback(){
return !HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin);
}
static uint8_t Key2_PressedCallback(){
return !HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin);
}
static uint8_t Key3_PressedCallback(){
return !HAL_GPIO_ReadPin(KEY3_GPIO_Port,KEY3_Pin);
}
static uint8_t Key4_PressedCallback(){
return !HAL_GPIO_ReadPin(KEY4_GPIO_Port,KEY4_Pin);
}
void Key_Init(void)
{
Key_InitAllVar();
Key_SetParam(KID_K1_SET,100,0,Key1_PressedCallback);
Key_SetParam(KID_K2_MODE,100,0,Key2_PressedCallback);
Key_SetParam(KID_K3_START,100,0,Key3_PressedCallback);
Key_SetParam(KID_K4_STOP,100,0,Key4_PressedCallback);
}
void Key_Scan10ms(){
uint8_t i;
for (i = 0; i < KEY_COUNT; i++)
{
Key_Detect(i);
}
}
key_bsp.h
#ifndef _BSP_KEY_H
#define _BSP_KEY_H
#include "main.h"
#define KEY_DOWN_K1_SET KEY_1_DOWN
#define KEY_UP_K1_SET KEY_1_UP
#define KEY_LONG_K1_SET KEY_1_LONG
#define KEY_DOWN_K2_MODE KEY_2_DOWN
#define KEY_UP_K2_MODE KEY_2_UP
#define KEY_LONG_K2_MODE KEY_2_LONG
#define KEY_DOWN_K3_START KEY_3_DOWN
#define KEY_UP_K3_START KEY_3_UP
#define KEY_LONG_K3_START KEY_3_LONG
#define KEY_DOWN_K4_STOP KEY_4_DOWN
#define KEY_UP_K4_STOP KEY_4_UP
#define KEY_LONG_K4_STOP KEY_4_LONG
typedef enum
{
KEY_NONE = 0,
KEY_1_DOWN,
KEY_1_UP,
KEY_1_LONG,
KEY_2_DOWN,
KEY_2_UP,
KEY_2_LONG,
KEY_3_DOWN,
KEY_3_UP,
KEY_3_LONG,
KEY_4_DOWN,
KEY_4_UP,
KEY_4_LONG,
}KEY_ENUM;
typedef enum
{
KID_K1_SET = 0,
KID_K2_MODE,
KID_K3_START,
KID_K4_STOP,
}KEY_ID_E;
//Set scan parameter
#define KEY_SCAN_PERIOR 10
#define KEY_FILTER_PERIOD 50/KEY_SCAN_PERIOR
#define KEY_LONG_PERIOD 1000/KEY_SCAN_PERIOR
参考文章: