Files
linear-Slide/Interface/Int_Key.c

149 lines
3.7 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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;
}