149 lines
3.7 KiB
C
149 lines
3.7 KiB
C
|
|
#include "Int_Key.h"
|
|||
|
|
#include <stddef.h>
|
|||
|
|
|
|||
|
|
/* 单个按键扫描上下文:
|
|||
|
|
* raw_level 原始采样电平
|
|||
|
|
* stable_level 去抖后的稳定电平
|
|||
|
|
* long_started 是否已上报过长按开始事件
|
|||
|
|
*/
|
|||
|
|
typedef struct
|
|||
|
|
{
|
|||
|
|
GPIO_TypeDef *port;
|
|||
|
|
uint16_t pin;
|
|||
|
|
Key_t key;
|
|||
|
|
uint8_t stable_level;
|
|||
|
|
uint8_t raw_level;
|
|||
|
|
uint8_t long_started;
|
|||
|
|
uint32_t last_change_ms;
|
|||
|
|
uint32_t press_ms;
|
|||
|
|
} KeyScanCtx_t;
|
|||
|
|
|
|||
|
|
#define KEY_COUNT (5u)
|
|||
|
|
#define KEY_SCAN_PERIOD_MS (5u)
|
|||
|
|
#define KEY_DEBOUNCE_MS (20u)
|
|||
|
|
#define KEY_LONG_MS (400u)
|
|||
|
|
#define KEY_QUEUE_SIZE (12u)
|
|||
|
|
|
|||
|
|
static KeyScanCtx_t s_keys[KEY_COUNT] = {
|
|||
|
|
{KEY1_GPIO_Port, KEY1_Pin, KEY_1, 1u, 1u, 0u, 0u, 0u},
|
|||
|
|
{KEY2_GPIO_Port, KEY2_Pin, KEY_2, 1u, 1u, 0u, 0u, 0u},
|
|||
|
|
{KEY3_GPIO_Port, KEY3_Pin, KEY_3, 1u, 1u, 0u, 0u, 0u},
|
|||
|
|
{KEY4_GPIO_Port, KEY4_Pin, KEY_4, 1u, 1u, 0u, 0u, 0u},
|
|||
|
|
{KEY5_GPIO_Port, KEY5_Pin, KEY_5, 1u, 1u, 0u, 0u, 0u},
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
static KeyAction_t s_queue[KEY_QUEUE_SIZE];
|
|||
|
|
static uint8_t s_q_head = 0u;
|
|||
|
|
static uint8_t s_q_tail = 0u;
|
|||
|
|
static uint32_t s_last_scan_ms = 0u;
|
|||
|
|
|
|||
|
|
static uint8_t queue_next(uint8_t v)
|
|||
|
|
{
|
|||
|
|
/* 环形队列下标递增。 */
|
|||
|
|
v++;
|
|||
|
|
if (v >= KEY_QUEUE_SIZE)
|
|||
|
|
{
|
|||
|
|
v = 0u;
|
|||
|
|
}
|
|||
|
|
return v;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void push_action(Key_t key, KeyEvt_t evt)
|
|||
|
|
{
|
|||
|
|
/* 队列满时丢弃新事件,避免阻塞主循环。 */
|
|||
|
|
uint8_t next = queue_next(s_q_head);
|
|||
|
|
if (next == s_q_tail)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
s_queue[s_q_head].key = key;
|
|||
|
|
s_queue[s_q_head].evt = evt;
|
|||
|
|
s_q_head = next;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void Int_Key_Task(void)
|
|||
|
|
{
|
|||
|
|
uint32_t now = HAL_GetTick();
|
|||
|
|
/* 固定周期扫描,避免过高频率浪费CPU。 */
|
|||
|
|
if ((uint32_t)(now - s_last_scan_ms) < KEY_SCAN_PERIOD_MS)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
s_last_scan_ms = now;
|
|||
|
|
|
|||
|
|
for (uint8_t i = 0u; i < KEY_COUNT; i++)
|
|||
|
|
{
|
|||
|
|
KeyScanCtx_t *k = &s_keys[i];
|
|||
|
|
uint8_t raw = (HAL_GPIO_ReadPin(k->port, k->pin) == GPIO_PIN_RESET) ? 0u : 1u;
|
|||
|
|
|
|||
|
|
if (raw != k->raw_level)
|
|||
|
|
{
|
|||
|
|
k->raw_level = raw;
|
|||
|
|
k->last_change_ms = now;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ((uint32_t)(now - k->last_change_ms) >= KEY_DEBOUNCE_MS)
|
|||
|
|
{
|
|||
|
|
if (k->stable_level != k->raw_level)
|
|||
|
|
{
|
|||
|
|
k->stable_level = k->raw_level;
|
|||
|
|
|
|||
|
|
if (k->stable_level == 0u)
|
|||
|
|
{
|
|||
|
|
/* 稳定按下:记录按下时间,等待长按判断。 */
|
|||
|
|
k->press_ms = now;
|
|||
|
|
k->long_started = 0u;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
/* 稳定释放:若未触发长按则上报CLICK,否则上报RELEASE。 */
|
|||
|
|
push_action(k->key, k->long_started ? KEY_EVT_RELEASE : KEY_EVT_CLICK);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (k->stable_level == 0u)
|
|||
|
|
{
|
|||
|
|
uint32_t hold_ms = (uint32_t)(now - k->press_ms);
|
|||
|
|
if (hold_ms >= KEY_LONG_MS)
|
|||
|
|
{
|
|||
|
|
if (k->long_started == 0u)
|
|||
|
|
{
|
|||
|
|
/* 长按开始事件只上报一次。 */
|
|||
|
|
k->long_started = 1u;
|
|||
|
|
push_action(k->key, KEY_EVT_LONG_START);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool Int_Key_PopAction(KeyAction_t *action)
|
|||
|
|
{
|
|||
|
|
/* 从事件队列弹出一个事件供上层消费。 */
|
|||
|
|
if (action == NULL)
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (s_q_tail == s_q_head)
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
*action = s_queue[s_q_tail];
|
|||
|
|
s_q_tail = queue_next(s_q_tail);
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Key_t Int_key_read(void)
|
|||
|
|
{
|
|||
|
|
/* 兼容旧接口:仅返回按键编号,不区分事件类型。 */
|
|||
|
|
KeyAction_t action;
|
|||
|
|
if (Int_Key_PopAction(&action))
|
|||
|
|
{
|
|||
|
|
return action.key;
|
|||
|
|
}
|
|||
|
|
return KEY_NONE;
|
|||
|
|
}
|