Files
JBao_Sprayer_FW/apps/usr_app/key_manager.c
T
2026-09-09 15:03:50 +08:00

202 lines
5.8 KiB
C

/******************************************************************************
* @file key_manager.c
* @brief 双键管理:PB1 喷淋短按;PA0 按住分级 5s 配对 / 12s OTA
* @author cyWu <1917507415@qq.com>
* @date 2026.09.09
* @version V1.0.0
* @history
* - V1.0.0, 2026.09.09, cyWu, P0 双键版(FSM 复用 key.c,逻辑同加热垫单键)
*
* @note 规格书 5.2:喷淋键只短按(配网/OTA 分级只做在配网键上,禁止混淆)。
* PB1 满 5s 后松手被 FSM 归为 RELEASE_Event,回调为空 = 无动作;
* PA0 满 5s LONG_Event 只切红灯快闪,清绑定等动作延后到任务上下文。
******************************************************************************/
#include "system/includes.h"
#include "key_manager.h"
#include "bsp_hw.h"
#include "board_pin.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[KEY]"
#define LOG_INFO_ENABLE
#include "debug.h"
/** 模块状态:每键一个"上电按住"锁 */
typedef struct {
uint8_t initialized;
uint8_t boot_key_lock[USR_KEY_COUNT]; /**< 1=该键上电时还按着,等松开才认短按 */
uint8_t ota_fired; /**< 1=PA0 已到点触发 OTA,本次松手不再提交配对 */
} KeyManagerCtx_t;
static KeyManagerCtx_t s_km;
/**
* @brief 读喷淋键:1=按下
* @return 1=按下,0=松开
*/
static uint8_t key_spray_read(void)
{
return bsp_key_spray_is_pressed();
}
/**
* @brief 读配网键:1=按下
* @return 1=按下,0=松开
*/
static uint8_t key_pair_read(void)
{
return bsp_key_pair_is_pressed();
}
/**
* @brief 喷淋键短按(SHORT_Event,松手 <5s):上电未松手前忽略,否则启/停喷淋
* @return 无
*/
static void key_spray_short(void)
{
if (s_km.boot_key_lock[USR_KEY_SPRAY]) {
s_km.boot_key_lock[USR_KEY_SPRAY] = 0; /* 上电松手当成释放,不当短按开喷 */
log_info("spray key released at boot\n");
return;
}
usr_on_key_short();
}
/**
* @brief 配网键长按到点(LONG_Event,满 5s 仍按住):挂红灯快闪提示
* @note ⚠ 跑在 20ms 硬件定时器中断上下文:只准打标志 + 切灯效事件(纯 RAM)。
* 清绑定(写 VM)留给 app_function_loop(任务上下文)。
* @return 无
*/
static void key_pair_hint(void)
{
usr_on_key_pair_hint();
}
/**
* @brief 配网键连按到点(LAST_Event,满 12s 仍按住,到点即触发不等松手):进 OTA
* @note ⚠ 同上,只置标志;usr_app_ota_init 由 app_function_loop 执行。
* key.c 对 LAST_Event 不清 eventType,松手前每 20ms 都会再派发一次,
* 用 ota_fired 挡住重复调用。
* @return 无
*/
static void key_pair_ota_start(void)
{
if (s_km.ota_fired) {
return;
}
s_km.ota_fired = 1;
usr_on_key_ota_start();
}
/**
* @brief 配网键释放(RELEASE_Event,按满 5s~12s 间松手):未进 OTA 则提交配对
* @note 按满 12s 已触发 OTA 的这次按住,松手不再提交配对(ota_fired 拦住)。
* @return 无
*/
static void key_pair_release(void)
{
s_km.boot_key_lock[USR_KEY_PAIR] = 0;
if (!s_km.ota_fired) {
usr_on_key_pair_start();
}
s_km.ota_fired = 0;
}
static keyCategory_t s_keys[USR_KEY_COUNT] = {
[USR_KEY_SPRAY] = {
/* 只短按:<5s 松手 SHORT_Event;满 5s 的 LONG/RELEASE、12s 的 LAST 全部空回调 */
.fsm.keyShield = KEY_ENABLE,
.fsm.keyDownLevel = Bit_SET,
.fsm.eventType = NULL_Event,
.fsm.keyLongTime = BOARD_KEY_SPRAY_SHORT_MS, /* 短按窗口上限 */
.fsm.keyLastTime = BOARD_KEY_OTA_MS,
.fsm.keyReadValue = key_spray_read,
.func.ShortPressCb = key_spray_short,
},
[USR_KEY_PAIR] = {
.fsm.keyShield = KEY_ENABLE,
.fsm.keyDownLevel = Bit_SET,
.fsm.eventType = NULL_Event,
.fsm.keyLongTime = BOARD_KEY_PAIR_MS, /* 满 5s 仍按住 -> longPressCb */
.fsm.keyLastTime = BOARD_KEY_OTA_MS, /* 满 12s 仍按住 -> lastPressCb */
.fsm.keyReadValue = key_pair_read,
.func.releasePressCb = key_pair_release,
.func.longPressCb = key_pair_hint,
.func.lastPressCb = key_pair_ota_start,
},
};
/**
* @brief 装载按键表
* @return 无
*/
void user_key_init(void)
{
if (s_km.initialized) {
return;
}
keyParaInit(s_keys);
s_km.boot_key_lock[USR_KEY_SPRAY] = 0;
s_km.boot_key_lock[USR_KEY_PAIR] = 0;
s_km.ota_fired = 0;
s_km.initialized = 1;
log_info("key init spray=PB1 pair=PA0\n");
}
/**
* @brief 上电后:键还按着则锁住短按,松开才认短按
* @note 只锁短按,**不锁长按分级**:上电就按住 PA0 5s 进配对是受支持的用法。
* @return 无
*/
void user_key_lock_if_held(void)
{
uint8_t i;
for (i = 0; i < USR_KEY_COUNT; i++) {
reset_key_Status((keyList)i);
}
if (bsp_key_spray_is_pressed()) {
s_km.boot_key_lock[USR_KEY_SPRAY] = 1; /* 防一上电就开喷 */
log_info("spray key held at boot, wait release\n");
}
if (bsp_key_pair_is_pressed()) {
s_km.boot_key_lock[USR_KEY_PAIR] = 1; /* PA0 无短按动作,仅保持状态一致 */
log_info("pair key held at boot\n");
}
}
/**
* @brief 按真实毫秒推进状态机
* @param dt 距上次调用的毫秒数
* @return 无
*/
void user_key_scan(uint16_t dt)
{
uint16_t i;
if (!s_km.initialized) {
return;
}
if (dt == 0) {
dt = 1;
}
/* 按键库按 1ms 步进,业务节拍是 20ms,这里循环 dt 次对齐真实时间 */
for (i = 0; i < dt; i++) {
keyCheckProcess();
}
}
/**
* @brief 派发按键回调
* @return 无
*/
void user_key_handle(void)
{
if (!s_km.initialized) {
return;
}
keyHandle();
}