好的,以下是一个使用状态机方式运行的,能够识别5个独立按键并且能够识别双按键同时按下、长按、短按、单按键双击、单按键长按的程序。这个程序使用了STM32G030为硬件平台,使用了HAL库进行编程。
```c
#include "stm32g0xx_hal.h"
#include <stdbool.h>
// 定义按键名称
#define KEY_UP 0x01
#define KEY_DOWN 0x02
#define KEY_LEFT 0x04
#define KEY_RIGHT 0x08
#define KEY_CENTER 0x10
// 定义按键状态
typedef enum {
KEY_RELEASED = 0, // 按键释放状态
KEY_PRESSED = 1 // 按键按下状态
} KeyState;
// 定义按键信息结构体
typedef struct {
GPIO_TypeDef* port; // GPIO端口
uint16_t pin; // GPIO引脚
bool inverted; // 是否反相
KeyState state; // 按键状态
uint32_t time; // 按键按下的时间
uint8_t name; // 按键名称
} KeyInfo;
// 初始化按键
void KeyInit(KeyInfo* key, GPIO_TypeDef* port, uint16_t pin, bool inverted, uint8_t name)
{
key->port = port;
key->pin = pin;
key->inverted = inverted;
key->state = KEY_RELEASED;
key->time = 0;
key->name = name;
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(port, &GPIO_InitStruct);
}
// 获取按键状态
KeyState GetKeyState(KeyInfo* key)
{
bool isPressed = HAL_GPIO_ReadPin(key->port, key->pin) ^ key->inverted;
if (isPressed && (key->state == KEY_RELEASED)) {
key->state = KEY_PRESSED;
key->time = HAL_GetTick();
} else if (!isPressed && (key->state == KEY_PRESSED)) {
key->state = KEY_RELEASED;
}
return key->state;
}
// 判断是否为长按事件
bool IsLongPress(KeyInfo* key)
{
if (GetKeyState(key) == KEY_PRESSED) {
uint32_t timeDiff = HAL_GetTick() - key->time;
if (timeDiff >= 1000) {
return true;
}
}
return false;
}
// 判断是否为短按事件
bool IsShortPress(KeyInfo* key)
{
if (GetKeyState(key) == KEY_RELEASED) {
uint32_t timeDiff = HAL_GetTick() - key->time;
if (timeDiff < 1000) {
return true;
}
}
return false;
}
// 判断是否为双击事件
bool IsDoubleClick(KeyInfo* key)
{
if (GetKeyState(key) == KEY_RELEASED) {
uint32_t timeDiff = HAL_GetTick() - key->time;
if ((timeDiff > 100) && (timeDiff < 300)) {
return true;
}
}
return false;
}
// 判断是否为双键同时按下事件
bool IsKeyCombination(KeyInfo* key1, KeyInfo* key2)
{
if ((GetKeyState(key1) == KEY_PRESSED) && (GetKeyState(key2) == KEY_PRESSED)) {
return true;
}
return false;
}
int main(void)
{
// 初始化GPIO
HAL_Init();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
// 初始化按键
KeyInfo key_up, key_down, key_left, key_right, key_center;
KeyInit(&key_up, GPIOA, GPIO_PIN_0, false, KEY_UP);
KeyInit(&key_down, GPIOA, GPIO_PIN_1, false, KEY_DOWN);
KeyInit(&key_left, GPIOA, GPIO_PIN_2, false, KEY_LEFT);
KeyInit(&key_right, GPIOA, GPIO_PIN_3, false, KEY_RIGHT);
KeyInit(&key_center, GPIOB, GPIO_PIN_4, false, KEY_CENTER);
// 进入状态机
KeyState state = KEY_RELEASED;
uint8_t curKey = 0, prevKey = 0;
uint32_t curTime = 0, prevTime = 0;
while (1)
{
switch (state) {
case KEY_RELEASED:
if (IsKeyCombination(&key_up, &key_down)) {
state = KEY_UP_DOWN;
} else if (IsKeyCombination(&key_left, &key_right)) {
state = KEY_LEFT_RIGHT;
} else if (IsLongPress(&key_center)) {
state = KEY_CENTER_LONG;
} else if (IsShortPress(&key_center)) {
state = KEY_CENTER_SHORT;
} else if (IsDoubleClick(&key_center)) {
state = KEY_CENTER_DOUBLE;
} else if (GetKeyState(&key_up) == KEY_PRESSED) {
state = KEY_UP;
} else if (GetKeyState(&key_down) == KEY_PRESSED) {
state = KEY_DOWN;
} else if (GetKeyState(&key_left) == KEY_PRESSED) {
state = KEY_LEFT;
} else if (GetKeyState(&key_right) == KEY_PRESSED) {
state = KEY_RIGHT;
}
break;
case KEY_UP:
curKey = KEY_UP;
curTime = HAL_GetTick();
if (IsShortPress(&key_up)) {
printf("Key Up Short Pressed\n");
state = KEY_RELEASED;
} else if (IsLongPress(&key_up)) {
printf("Key Up Long Pressed\n");
state = KEY_RELEASED;
} else if (IsDoubleClick(&key_up)) {
printf("Key Up Double Clicked\n");
state = KEY_RELEASED;
}
break;
case KEY_DOWN:
curKey = KEY_DOWN;
curTime = HAL_GetTick();
if (IsShortPress(&key_down)) {
printf("Key Down Short Pressed\n");
state = KEY_RELEASED;
} else if (IsLongPress(&key_down)) {
printf("Key Down Long Pressed\n");
state = KEY_RELEASED;
} else if (IsDoubleClick(&key_down)) {
printf("Key Down Double Clicked\n");
state = KEY_RELEASED;
}
break;
case KEY_LEFT:
curKey = KEY_LEFT;
curTime = HAL_GetTick();
if (IsShortPress(&key_left)) {
printf("Key Left Short Pressed\n");
state = KEY_RELEASED;
} else if (IsLongPress(&key_left)) {
printf("Key Left Long Pressed\n");
state = KEY_RELEASED;
} else if (IsDoubleClick(&key_left)) {
printf("Key Left Double Clicked\n");
state = KEY_RELEASED;
}
break;
case KEY_RIGHT:
curKey = KEY_RIGHT;
curTime = HAL_GetTick();
if (IsShortPress(&key_right)) {
printf("Key Right Short Pressed\n");
state = KEY_RELEASED;
} else if (IsLongPress(&key_right)) {
printf("Key Right Long Pressed\n");
state = KEY_RELEASED;
} else if (IsDoubleClick(&key_right)) {
printf("Key Right Double Clicked\n");
state = KEY_RELEASED;
}
break;
case KEY_CENTER_SHORT:
printf("Key Center Short Pressed\n");
state = KEY_RELEASED;
break;
case KEY_CENTER_LONG:
printf("Key Center Long Pressed\n");
state = KEY_RELEASED;
break;
case KEY_CENTER_DOUBLE:
printf("Key Center Double Clicked\n");
state = KEY_RELEASED;
break;
case KEY_UP_DOWN:
printf("Key Up and Down Pressed\n");
state = KEY_RELEASED;
break;
case KEY_LEFT_RIGHT:
printf("Key Left and Right Pressed\n");
state = KEY_RELEASED;
break;
}
// 判断单按键双击
if ((curKey != 0) && (curKey == prevKey)) {
uint32_t timeDiff = curTime - prevTime;
if ((timeDiff > 100) && (timeDiff < 300)) {
printf("Key %d Double Clicked\n", curKey);
curKey = 0;
}
}
// 记录上一个按下的按键和时间
if (curKey != 0) {
prevKey = curKey;
prevTime = curTime;
}
}
}
```
在这个程序中,我们首先定义了按键名称以及按键状态枚举类型。然后我们定义了包含按键信息的结构体`KeyInfo`,包含GPIO端口、引脚、状态、按下时间以及按键名称等信息。接着我们定义了一些函数,包括初始化按键、获取按键状态、判断是否为长按事件、短按事件、双击事件以及双键同时按下事件等。最后我们通过状态机的方式来处理按键事件,包括单按键短按、长按、双击事件以及双键同时按下事件等。同时我们还记录了上一个按下的按键以及时间,以便判断单按键双击事件。