一、简介
1.1 功能需求
- 短按:按键按下时间 < 1s,触发一次短按事件;
- 长按:按键按下时间 ≥ 1s,触发一次长按事件;
- 3秒内统计按键次数:在3秒内统计短按次数(长按不计入)。
1.2 软件设计思路
- 使用 状态机 检测按键状态(按下、释放、长按、短按);
- 使用 滴答定时器 记录按下时间;
- 使用 软件定时器 实现3秒统计窗口;
二、代码
2.1 按键驱动头文件 key.h
#ifndef __KEY_H
#define __KEY_H#include "main.h"
#include <stdbool.h>
#define RCC_BUTTON_USER_CLK RCC_PERIPH_CLK_GPIOB
#define BUTTON_USER_PORT GPIOB
#define BUTTON_USER_PIN GPIO_PIN_10
#define KEY_PRESSED_LEVEL 0
#define KEY_LONG_MS 1000
#define KEY_STAT_WINDOW_MS 3000
typedef enum {KEY_STA_IDLE = 0,KEY_STA_PRESSED,KEY_STA_SHORT,KEY_STA_LONG
} KeyStateEnum;
typedef struct {GPIO_t *port;uint16_t pin;uint8_t pressedLevel; uint32_t longThd; uint32_t winThd; KeyStateEnum state;uint32_t tPress; uint32_t tRelease; uint8_t shortCnt; uint32_t winStart; bool winActive;
} Key_t;extern Key_t gKey;void KEY_Init(Key_t *k);
void KEY_Scan(Key_t *k);
void KEY_Process(Key_t *k);#endif
2.2 按键驱动源文件 key.c
#include "key.h"
#include <stdio.h>
void KeyExtiInit(void)
{std_exti_init_t exti_init_config = {0};std_gpio_init_t button_init_config = {0}; std_rcc_gpio_clk_enable(RCC_BUTTON_USER_CLK);button_init_config.pin = BUTTON_USER_PIN;button_init_config.mode = GPIO_MODE_INPUT;button_init_config.pull = GPIO_PULLUP;std_gpio_init(BUTTON_USER_PORT, &button_init_config);exti_init_config.line_id = BUTTON_USER_EXTI_LINE;exti_init_config.mode = EXTI_MODE_INTERRUPT;exti_init_config.trigger = EXTI_TRIGGER_FALLING; exti_init_config.gpio_id = BUTTON_USER_EXTI_PORT;std_exti_init(&exti_init_config);NVIC_SetPriority(EXTI4_15_IRQn, NVIC_PRIO_0); NVIC_EnableIRQ(EXTI4_15_IRQn);
}Key_t gKey;
static bool KEY_Read(const Key_t *k) {return std_gpio_get_input_pin(k->port, k->pin) == k->pressedLevel;
}
void KEY_Init(Key_t *k) {k->port = BUTTON_USER_PORT;k->pin = BUTTON_USER_PIN;k->pressedLevel= KEY_PRESSED_LEVEL; k->longThd = KEY_LONG_MS; k->winThd = KEY_STAT_WINDOW_MS; k->state = KEY_STA_IDLE;k->tPress = 0; k->tRelease = 0; k->shortCnt = 0; k->winStart = 0; k->winActive = false;
}
void KEY_Scan(Key_t *k)
{static bool lastRaw = false;bool raw = KEY_Read(k);uint32_t now = osKernelGetTickCount();switch (k->state) {case KEY_STA_IDLE:if (raw && !lastRaw) { k->tPress = now;k->state = KEY_STA_PRESSED;}break;case KEY_STA_PRESSED: if (!raw) { k->tRelease = now;if ((k->tRelease - k->tPress) < k->longThd)k->state = KEY_STA_SHORT;elsek->state = KEY_STA_IDLE;} else if ((now - k->tPress) >= k->longThd) {k->state = KEY_STA_LONG;}break;case KEY_STA_SHORT:if (!k->winActive) {k->winActive = true;k->winStart = now;k->shortCnt = 1;} else {k->shortCnt++;}k->state = KEY_STA_IDLE;break;case KEY_STA_LONG:k->state = KEY_STA_IDLE;break;}lastRaw = raw;
}
void KEY_Process(Key_t *k)
{if (!k->winActive) return;uint32_t now = osKernelGetTickCount();if ((now - k->winStart) >= k->winThd) {printf("3 s 内短按次数:%d\n", k->shortCnt);k->state = KEY_STA_IDLE;k->tPress = 0; k->tRelease = 0; k->shortCnt = 0; k->winStart = 0; k->winActive = false; if(system_info.flags.key_isr_flag == 1){if(KeyIsPressed() == false){ printf("KeyIsPressed-2 \r\n");system_info.flags.key_isr_flag = 0;}} }
}
2.3 主循环中调用
void AppTaskStart(void *argument)
{const uint16_t usFrequency = 10; uint32_t tick;tick = osKernelGetTickCount(); KEY_Init(&gKey);while(1){KEY_Scan(&gKey); KEY_Process(&gKey);tick += usFrequency; osDelayUntil(tick);}
}
2.4 示例输出(串口):
