hpw421初始版本

This commit is contained in:
xushaoxiang
2026-06-06 16:49:48 +08:00
commit 2339bbfd1f
3283 changed files with 860261 additions and 0 deletions
@@ -0,0 +1,405 @@
/*!
* \file key.c
*
* \brief Target key implementation
*
* \copyright Revised BSD License, see section \ref LICENSE.
*次
* \code 版本:只有单击,双击,四击,六击功能,三击,五击没有。大于等于六击强制六击,清零计数,连按八次触发双击
*
* _ __ _ ________ _
* | |/ /(_)___ / ____/ /_ (_)___
* | // / __ \/ / / __ \/ / __ \
* / |/ / / / / /___/ / / / / /_/ /
* /_/|_/_/_/ /_/\____/_/ /_/_/ .___/
* /_/
* (C) 2022-2025 XinChip
*
* \endcode
*
* \author ( XinChip ) Alex-J
*
* \author ( XinChip )
*/
/*-----------------------------------------------------------------------------------
INCLUDE HEADE FILES
------------------------------------------------------------------------------------*/
#include "xc_drv_gpio.h" // GPIO驱动接口
#include "xc60xx.h" // 芯片寄存器定义
#include "key.h" // 按键模块头文件
#include "message.h" // 消息定义
#include "rgblight.h" // RGB灯控制
/*------------------------------------------------------------------------------------
Macros
-------------------------------------------------------------------------------------*/
/* 按键按下检测宏:GPIO读取为低电平时表示按键按下(按键接地,默认上拉) */
#define IS_KEY_DOWN(n) (!(xc_gpio_read_pin(n)))
/*------------------------------------------------------------------------------------
Global Variables
-------------------------------------------------------------------------------------*/
static uint16_t last_key_val = NO_KEY; // 上一轮扫描到的按键值(用于检测按键状态变化)
static uint16_t key_down_count = 0; // 按键按下持续时间计数(每10ms+1)
static uint16_t key_up_count = 0; // 按键抬起持续时间计数(每10ms+1)
KeyMessageCallback key_callback = NULL; // 按键消息回调函数指针
/* 全局连击状态变量:记录连续点击的次数和超时状态 */
KeyClickState_t g_key_click_state = {
.click_count = 0, // 初始化连击次数为0
.click_interval_cnt = 0, // 初始化间隔计数为0
.last_click_key = NO_KEY, // 初始化上次按键为无按键
.is_waiting_click = 0 // 初始化为非等待状态
};
/*------------------------------------------------------------------------------------
Func Prototype
-------------------------------------------------------------------------------------*/
static uint16_t app_key_click_detection(uint16_t key_val); // 连击检测函数声明
static void key_click_state_reset(void); // 连击状态重置函数声明
static uint16_t get_click_event(uint16_t click_cnt, uint16_t key_val); // 连击事件转换函数
/*------------------------------------------------------------------------------------
Functions
-------------------------------------------------------------------------------------*/
/**
* @brief key_click_state_reset
* @details 重置连击状态结构体的所有字段为初始值
* 在连击超时确定最终结果后、或长按触发后、或无效按键时调用
* @param void
* @retval void
*/
static void key_click_state_reset(void)
{
g_key_click_state.click_count = 0; // 清零连击次数
g_key_click_state.click_interval_cnt = 0; // 清零间隔计数
g_key_click_state.last_click_key = NO_KEY; // 清零上次按键编号
g_key_click_state.is_waiting_click = 0; // 退出等待连击状态
}
/**
* @brief get_click_event
* @details 根据连击次数转换为对应事件(核心修复:明确计数边界)
* @param click_cnt 连击次数
* @param key_val 按键编号
* @retval 组合后的按键事件值
*/
static uint16_t get_click_event(uint16_t click_cnt, uint16_t key_val)
{
uint16_t event = NO_KEY;
if (click_cnt == 1)
{
event = key_val | KEY_SHORT_UP; // 单击
}
else if (click_cnt == 2)
{
event = key_val | KEY_DOUBLE_CLICK; // 双击
}
else if (click_cnt == 4)
{
event = key_val | KEY_QUAD_CLICK; // 四击
}
else if (click_cnt >= 6)
{
event = key_val | KEY_SIX_CLICK; // 六击(6次及以上统一判定为六击)
}
return event;
}
/**
* @brief app_key_register_callback
* @details 注册按键消息回调函数,当按键事件触发时通过该回调通知上层
* @param callback 回调函数指针
* @retval void
*/
void app_key_register_callback(KeyMessageCallback callback)
{
key_callback = callback; // 保存回调函数指针
}
/**
* @brief app_key_hold_detection
* @details 按键按住检测:处理按键持续按下的情况
* 通过key_down_count计时,区分短按下和长按下事件
* @param curren_key_val 当前扫描到的按键值
* @retval key_num 按键事件值(带事件类型的组合键值)
*/
__RAM_CODE uint16_t app_key_hold_detection(uint16_t curren_key_val)
{
uint16_t key_num = NO_KEY; // 初始化返回值为无按键
if (key_down_count < 0xff) // 防止计数器溢出
key_down_count++; // 按下持续时间+1(每10ms递增)
/* 短按下判定:按下时间达到消抖阈值(20ms) */
if (key_down_count == KEY_DOWN_TIMER)
{
key_num = (curren_key_val | KEY_SHORT_DOWN); // 生成短按下事件
}
/* 长按下判定:按下时间达到长按阈值(600ms) */
else if(key_down_count == KEY_LONG_TIMER)
{
key_num = (curren_key_val | KEY_LONG_DOWN); // 生成长按下事件
key_click_state_reset(); // 长按触发时重置连击状态(长按不算连击)
}
key_up_count = 0; // 按键未抬起,清零抬起计数器
return key_num; // 返回按键事件
}
/**
* @brief app_new_key_detection
* @details 新按键检测:处理按键状态变化(按键释放、按键切换等)
* 当按键状态改变时(按键释放或切换到另一个按键),判断之前的按键事件类型
* @param curren_key_val 当前扫描到的按键值
* @retval key_num 按键事件值
*/
__RAM_CODE uint16_t app_new_key_detection(uint16_t curren_key_val)
{
uint16_t key_num = NO_KEY; // 初始化返回值为无按键
/* 按键已释放(当前无按键按下),开始计数抬起时间 */
if (curren_key_val == NO_KEY && last_key_val != NO_KEY)
{
key_up_count++; // 抬起持续时间+1(每10ms递增)
/* 抬起消抖完成判定:抬起时间达到消抖阈值(20ms),确认按键已稳定释放 */
if (key_up_count >= KEY_UP_TIMER)
{
/* 情况1:之前是长按(按下时长 >= 600ms),现在松手 */
if (key_down_count >= KEY_LONG_TIMER)
{
key_num = (last_key_val | KEY_LONG_UP); // 生成长按抬起事件
key_click_state_reset(); // 长按抬起后重置连击状态
}
/* 情况2:之前是短按(按下时长在20ms~600ms之间),进入连击检测 */
else if (key_down_count >= KEY_DOWN_TIMER && key_down_count < KEY_LONG_TIMER)
{
/* 调用连击检测函数:记录本次松手并判断是否需要等待更多连击 */
key_num = app_key_click_detection(last_key_val);
}
// 重置计数器
key_down_count = 0;
key_up_count = 0;
last_key_val = NO_KEY;
}
}
/* 按键切换检测:在当前按键未完全释放时,按下了另一个按键 */
if (curren_key_val != NO_KEY && curren_key_val != last_key_val)
{
/* 切换时先结算上一个按键:如果是长按则发长按抬起事件 */
if (last_key_val != NO_KEY)
{
if (key_down_count >= KEY_LONG_TIMER)
{
key_num = (last_key_val | KEY_LONG_UP);
key_click_state_reset();
}
/* 切换时上一个按键是短按:走连击检测 */
else if (key_down_count >= KEY_DOWN_TIMER && key_down_count < KEY_LONG_TIMER)
{
key_num = app_key_click_detection(last_key_val);
}
}
last_key_val = curren_key_val;
key_down_count = 0;
key_up_count = 0;
}
return key_num;
}
/**
* @brief app_key_click_detection
* @details 连击检测核心函数:修复计数逻辑,明确边界
* 每次短按松手时调用,累加click_count计数
* - 第1次松手:进入等待状态
* - 第2次松手:暂存,超时后判定双击
* - 第4次松手:暂存,超时后判定四击
* - 第6次及以上:直接判定六击(防止计数溢出)
* @param key_val 当前松手的按键编号
* @retval key_num 需要立即返回的按键事件(仅在按键切换时返回)
*/
static uint16_t app_key_click_detection(uint16_t key_val)
{
uint16_t key_num = NO_KEY;
if (key_val == NO_KEY)
{
key_click_state_reset();
return NO_KEY;
}
/* 情况1:首次点击(之前没有在等待连击) */
if (g_key_click_state.is_waiting_click == 0)
{
g_key_click_state.click_count = 1;
g_key_click_state.last_click_key = key_val;
g_key_click_state.is_waiting_click = 1;
g_key_click_state.click_interval_cnt = 0;
return NO_KEY;
}
/* 情况2:按下了不同的按键(按键切换) */
if (g_key_click_state.last_click_key != key_val)
{
/* 立即结算上一个按键的连击结果 */
key_num = get_click_event(g_key_click_state.click_count, g_key_click_state.last_click_key);
/* 重置状态,并开始跟踪新按键的第1次松手 */
key_click_state_reset();
g_key_click_state.click_count = 1;
g_key_click_state.last_click_key = key_val;
g_key_click_state.is_waiting_click = 1;
g_key_click_state.click_interval_cnt = 0;
return key_num;
}
/* 情况3:同一按键再次松手,累加点击次数 */
g_key_click_state.click_count++;
g_key_click_state.click_interval_cnt = 0;
/* 核心修复:6次及以上直接判定六击,防止无限累加 */
if (g_key_click_state.click_count >= 6)
{
key_num = get_click_event(6, key_val); // 强制六击
key_click_state_reset(); // 重置状态
return key_num;
}
return NO_KEY;
}
/**
* @brief key_check_process
* @details 按键检测主流程:根据当前按键和上一轮按键是否相同,分发到不同处理函数
* @param curren_key_val 当前扫描到的按键值
* @retval key_num 按键事件值
*/
__RAM_CODE uint16_t key_check_process(uint16_t curren_key_val)
{
uint16_t key_num = NO_KEY;
/* 当前按键与上一轮相同且非NO_KEY:按键持续按下中 */
if (curren_key_val == last_key_val && last_key_val != NO_KEY)
{
key_num = app_key_hold_detection(curren_key_val);
}
/* 按键状态发生变化(按下/抬起/切换) */
else
{
// 新按键按下(从无到有)
if (curren_key_val != NO_KEY && last_key_val == NO_KEY)
{
last_key_val = curren_key_val;
key_down_count = 0;
key_up_count = 0;
}
key_num = app_new_key_detection(curren_key_val);
}
return key_num;
}
/**
* @brief gpio_key_check
* @details GPIO按键扫描:读取所有按键GPIO的电平状态,返回当前被按下的按键编号
* 当前仅启用MODE键(GPIO18),ON_OFFGPIO19)和SPEEDGPIO17)已注释预留
* @param void
* @retval key_val 当前按下的按键编号,无按键时返回NO_KEY
*/
uint16_t gpio_key_check(void)
{
uint16_t key_val = NO_KEY;
/* 检测MODE键(GPIO18)是否按下 */
if (IS_KEY_DOWN(KEY_MODE))
{
key_val = REC_KEY_UP;
}
/* ON_OFF键和SPEED键检测(预留,当前未启用) */
// else if ( IS_KEY_DOWN(KEY_ON_OFF) )
// {
// key_val = REC_KEY;
// }
// else if ( IS_KEY_DOWN(KEY_SPEED) )
// {
// key_val = REC_KEY_DOWN;
// }
return key_val;
}
/**
* @brief app_key_scan
* @details 按键扫描主函数:每10ms调用一次
* 核心修复:
* 1. 连击计时仅在无按键按下时递增(按下阶段停止计时)
* 2. 超时判定时严格按计数边界生成事件
* @param void
* @retval void
*/
void app_key_scan(void)
{
uint16_t key_num = NO_KEY;
uint16_t key_val = NO_KEY;
/* 步骤1:读取GPIO获取当前按键状态 */
key_val = gpio_key_check();
/* 步骤2:进行按键消抖和事件判定 */
key_num = key_check_process(key_val);
/* 步骤3:如果有需要立即发出的事件,通过回调通知上层 */
if (key_num != NO_KEY && key_callback != NULL)
{
key_callback(key_num);
}
/* 步骤4:连击超时判定 —— 仅在无按键按下时计时 */
if (g_key_click_state.is_waiting_click && key_val == NO_KEY)
{
g_key_click_state.click_interval_cnt++;
/* 超时判定:距离最后一次松手已超过200ms */
if (g_key_click_state.click_interval_cnt >= KEY_CLICK_TIMEOUT)
{
/* 根据最终连击次数确定事件类型 */
key_num = get_click_event(g_key_click_state.click_count, g_key_click_state.last_click_key);
/* 通过回调发送最终判定的事件 */
if (key_num != NO_KEY && key_callback != NULL)
{
key_callback(key_num);
}
/* 发送完毕后重置状态 */
key_click_state_reset();
}
}
}
/**
* @brief app_key_init
* @details 按键模块初始化:配置按键GPIO为上拉输入模式,并注册按键回调函数
* @param void
* @retval void
*/
void app_key_init(void)
{
GPIO_InitCfg_t GPIO_InitCfg = { 0 };
GPIO_InitCfg.Mux = GPIO_Mux0;
GPIO_InitCfg.FunSel = GPIO_Dx;
GPIO_InitCfg.Pull = GPIO_PULLUP;
GPIO_InitCfg.Dir = GPIO_DIR_INPUT;
GPIO_InitCfg.Int = NOT_INT;
GPIO_InitCfg.Pin = GPIO_18;
xc_gpio_init(&GPIO_InitCfg);
app_key_register_callback(app_key_callback_handler);
}