|
|
|
|
/******************************************************************************
|
|
|
|
|
|
|
|
|
|
版权所有 (C), 2018-2099, Radkil
|
|
|
|
|
|
|
|
|
|
******************************************************************************
|
|
|
|
|
文 件 名 : button.c
|
|
|
|
|
版 本 号 : 初稿
|
|
|
|
|
作 者 : Radkil
|
|
|
|
|
生成日期 : 2026年3月25日星期三
|
|
|
|
|
最近修改 :
|
|
|
|
|
功能描述 : 按键时间驱动处理(含优先级机制)
|
|
|
|
|
|
|
|
|
|
修改历史 :
|
|
|
|
|
1.日 期 : 2026年3月25日星期三
|
|
|
|
|
作 者 : Radkil
|
|
|
|
|
修改内容 : 创建文件
|
|
|
|
|
|
|
|
|
|
******************************************************************************/
|
|
|
|
|
#include "button.h"
|
|
|
|
|
#include "rd_thread.h"
|
|
|
|
|
#include "rd_time.h"
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------*
|
|
|
|
|
* 外部变量说明 *
|
|
|
|
|
*----------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------*
|
|
|
|
|
* 外部函数原型说明 *
|
|
|
|
|
*----------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------*
|
|
|
|
|
* 内部函数原型说明 *
|
|
|
|
|
*----------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------*
|
|
|
|
|
* 全局变量 *
|
|
|
|
|
*----------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------*
|
|
|
|
|
* 模块级变量 *
|
|
|
|
|
*----------------------------------------------*/
|
|
|
|
|
#define RD_BUTTON_ELIMINATION_TIME_DEFAULT 15 /**< 按键消抖默认时间 15ms */
|
|
|
|
|
#define RD_BUTTON_TWO_INTERVAL_TIME_DEFAULT 500 /**< 两次按键按下间隔超过500ms清零重复计数 */
|
|
|
|
|
#define RD_BUTTON_HOLD_CYCLE_TIME_DEFAULT 500 /**< 按键按下后持续调用回调函数的周期 */
|
|
|
|
|
|
|
|
|
|
static rd_slist_t g_sBtnListHead = RD_SLIST_OBJECT_INIT(g_sBtnListHead);
|
|
|
|
|
static rd_mutex_t g_fpBtnCtrl = RD_MUTEX_INITIALIZER;
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------*
|
|
|
|
|
* 模块级变量 - 优先级控制 *
|
|
|
|
|
*----------------------------------------------*/
|
|
|
|
|
static int g_iPriorityLock = 0; /**< 优先级锁:>0 时屏蔽低于该值的按键 */
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------*
|
|
|
|
|
* 常量定义 *
|
|
|
|
|
*----------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------*
|
|
|
|
|
* 宏定义 *
|
|
|
|
|
*----------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 判断按键是否被优先级锁屏蔽
|
|
|
|
|
* @return 1=被屏蔽,0=允许处理
|
|
|
|
|
*/
|
|
|
|
|
static int rd_BtnIsPriorityLocked(rd_btn_t *_pBtn)
|
|
|
|
|
{
|
|
|
|
|
if (g_iPriorityLock <= 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* 优先级数值越小越高,被锁的按键优先级低于锁值则屏蔽 */
|
|
|
|
|
if (_pBtn->m_iPriority >= g_iPriorityLock)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 更新当前最高活跃优先级(用于自动加锁/解锁)
|
|
|
|
|
*/
|
|
|
|
|
static void rd_BtnUpdatePriorityLock(void)
|
|
|
|
|
{
|
|
|
|
|
rd_slist_t *pNode;
|
|
|
|
|
rd_slist_t *pN;
|
|
|
|
|
int highest = -1; /* -1 表示没有按键处于按下状态 */
|
|
|
|
|
|
|
|
|
|
rd_slist_for_each(pNode, pN, &g_sBtnListHead)
|
|
|
|
|
{
|
|
|
|
|
rd_btn_t *_pBtn = rd_slist_entry(pNode, rd_btn_t, m_tSlist);
|
|
|
|
|
if (_pBtn->m_iActive &&
|
|
|
|
|
(_pBtn->m_tState == STATE_PRESS_DOWN ||
|
|
|
|
|
_pBtn->m_tState == STATE_PRESS_HOLD))
|
|
|
|
|
{
|
|
|
|
|
if (highest < 0 || _pBtn->m_iPriority < highest)
|
|
|
|
|
highest = _pBtn->m_iPriority;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (highest < 0)
|
|
|
|
|
{
|
|
|
|
|
/* 没有按键按下,自动解锁 */
|
|
|
|
|
g_iPriorityLock = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* 有按键按下,锁定比它优先级低的按键 */
|
|
|
|
|
g_iPriorityLock = highest + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rd_btn_t *rd_BtnCreate(int _iActiveLogic, rd_btn_read_func pReadFunc, int _iPriority)
|
|
|
|
|
{
|
|
|
|
|
rd_btn_t *pBtn = (rd_btn_t *)RD_MALLOC(sizeof(rd_btn_t));
|
|
|
|
|
if (pBtn == NULL) return NULL;
|
|
|
|
|
|
|
|
|
|
RD_MEMSET(pBtn, 0, sizeof(rd_btn_t));
|
|
|
|
|
pBtn->m_iActive = 0;
|
|
|
|
|
pBtn->m_iRepeatCnt = 0;
|
|
|
|
|
pBtn->m_iEliminationTime = RD_BUTTON_ELIMINATION_TIME_DEFAULT;
|
|
|
|
|
pBtn->m_tEvent = EVENT_NUM;
|
|
|
|
|
pBtn->m_tState = STATE_NONE_PRESS;
|
|
|
|
|
pBtn->m_iHoldTime = 0;
|
|
|
|
|
pBtn->m_iPrevHoldTime = 0;
|
|
|
|
|
pBtn->m_iHoldCycleTime = RD_BUTTON_HOLD_CYCLE_TIME_DEFAULT;
|
|
|
|
|
pBtn->m_iActiveLogic = _iActiveLogic;
|
|
|
|
|
pBtn->m_iTimeout = Rd_GetTime();
|
|
|
|
|
pBtn->m_pReadFunc = pReadFunc;
|
|
|
|
|
pBtn->m_iPriority = _iPriority; /* 新增:初始化优先级 */
|
|
|
|
|
rd_slist_init(&(pBtn->m_tSlist));
|
|
|
|
|
|
|
|
|
|
return pBtn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rd_BtnDelete(rd_btn_t *_pBtn)
|
|
|
|
|
{
|
|
|
|
|
if(NULL == _pBtn)
|
|
|
|
|
{
|
|
|
|
|
return RD_INVALUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rd_MutexLock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
rd_slist_remove(&g_sBtnListHead, &(_pBtn->m_tSlist));
|
|
|
|
|
_pBtn->m_iActive = 0;
|
|
|
|
|
_pBtn->m_tSlist.next = NULL;
|
|
|
|
|
Rd_MutexUnlock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
RD_FREE(_pBtn);
|
|
|
|
|
|
|
|
|
|
return RD_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rd_BtnStart(rd_btn_t *_pBtn)
|
|
|
|
|
{
|
|
|
|
|
if(NULL == _pBtn)
|
|
|
|
|
{
|
|
|
|
|
return RD_INVALUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rd_MutexLock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
if (_pBtn->m_iActive)
|
|
|
|
|
{
|
|
|
|
|
Rd_MutexUnlock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
return RD_INVALUE;
|
|
|
|
|
}
|
|
|
|
|
_pBtn->m_iRepeatCnt = 0;
|
|
|
|
|
_pBtn->m_tEvent = EVENT_NUM;
|
|
|
|
|
_pBtn->m_tState = STATE_NONE_PRESS;
|
|
|
|
|
_pBtn->m_iHoldTime = 0;
|
|
|
|
|
_pBtn->m_iPrevHoldTime = 0;
|
|
|
|
|
_pBtn->m_iTimeout = Rd_GetTime();
|
|
|
|
|
rd_slist_append(&g_sBtnListHead, &(_pBtn->m_tSlist));
|
|
|
|
|
_pBtn->m_iActive = 1;
|
|
|
|
|
|
|
|
|
|
Rd_MutexUnlock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
return RD_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rd_BtnStop(rd_btn_t *_pBtn)
|
|
|
|
|
{
|
|
|
|
|
if(NULL == _pBtn)
|
|
|
|
|
{
|
|
|
|
|
return RD_INVALUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rd_MutexLock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
if (!_pBtn->m_iActive)
|
|
|
|
|
{
|
|
|
|
|
Rd_MutexUnlock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
return RD_INVALUE;
|
|
|
|
|
}
|
|
|
|
|
rd_slist_remove(&g_sBtnListHead, &(_pBtn->m_tSlist));
|
|
|
|
|
_pBtn->m_tSlist.next = NULL;
|
|
|
|
|
_pBtn->m_iActive = 0;
|
|
|
|
|
|
|
|
|
|
Rd_MutexUnlock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
return RD_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rd_BtnSetEliminationTime(rd_btn_t *_pBtn, uint8_t _iEliminationTime)
|
|
|
|
|
{
|
|
|
|
|
if(NULL == _pBtn)
|
|
|
|
|
{
|
|
|
|
|
return RD_INVALUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rd_MutexLock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
_pBtn->m_iEliminationTime = _iEliminationTime;
|
|
|
|
|
Rd_MutexUnlock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
|
|
|
|
|
return RD_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rd_BtnSetHoldCycleTime(rd_btn_t *_pBtn, uint32_t _iHoldCycleTime)
|
|
|
|
|
{
|
|
|
|
|
if(NULL == _pBtn)
|
|
|
|
|
{
|
|
|
|
|
return RD_INVALUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rd_MutexLock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
_pBtn->m_iHoldCycleTime = _iHoldCycleTime;
|
|
|
|
|
Rd_MutexUnlock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
|
|
|
|
|
return RD_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int rd_BtnSetEventCbk(rd_btn_t *_pBtn, rd_BtnEvent _eEvent, void (*_pEventCbk)(rd_btn_t *pBtn))
|
|
|
|
|
{
|
|
|
|
|
if(NULL == _pBtn)
|
|
|
|
|
{
|
|
|
|
|
return RD_INVALUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_eEvent >= EVENT_NUM) return RD_INVALUE;
|
|
|
|
|
|
|
|
|
|
Rd_MutexLock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
_pBtn->m_pEventCbk[_eEvent] = _pEventCbk;
|
|
|
|
|
Rd_MutexUnlock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
|
|
|
|
|
return RD_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 手动设置按键优先级
|
|
|
|
|
*/
|
|
|
|
|
int rd_BtnSetPriority(rd_btn_t *_pBtn, int _iPriority)
|
|
|
|
|
{
|
|
|
|
|
if (NULL == _pBtn)
|
|
|
|
|
return RD_INVALUE;
|
|
|
|
|
|
|
|
|
|
Rd_MutexLock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
_pBtn->m_iPriority = _iPriority;
|
|
|
|
|
Rd_MutexUnlock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
|
|
|
|
|
return RD_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 手动加优先级锁(屏蔽优先级 >= _iLockValue 的按键)
|
|
|
|
|
* @param _iLockValue 锁值,优先级 >= 此值的按键被屏蔽,0=解锁
|
|
|
|
|
*/
|
|
|
|
|
void rd_BtnPriorityLock(int _iLockValue)
|
|
|
|
|
{
|
|
|
|
|
Rd_MutexLock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
g_iPriorityLock = _iLockValue;
|
|
|
|
|
Rd_MutexUnlock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 手动解锁优先级锁
|
|
|
|
|
*/
|
|
|
|
|
void rd_BtnPriorityUnlock(void)
|
|
|
|
|
{
|
|
|
|
|
rd_BtnPriorityLock(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void rd_BtnCalHoldTime(rd_btn_t *_pBtn)
|
|
|
|
|
{
|
|
|
|
|
if(NULL == _pBtn)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_pBtn->m_iHoldTime = Rd_GetTime() - _pBtn->m_iTimeout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void rd_BtnProcess()
|
|
|
|
|
{
|
|
|
|
|
rd_slist_t *pNode;
|
|
|
|
|
rd_slist_t *pN;
|
|
|
|
|
|
|
|
|
|
Rd_MutexLock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
rd_slist_for_each(pNode, pN, &g_sBtnListHead)
|
|
|
|
|
{
|
|
|
|
|
rd_btn_t *_pBtn = rd_slist_entry(pNode, rd_btn_t, m_tSlist);
|
|
|
|
|
|
|
|
|
|
/* ===== 新增:优先级屏蔽判断 ===== */
|
|
|
|
|
if (rd_BtnIsPriorityLocked(_pBtn))
|
|
|
|
|
continue; /* 被屏蔽的按键直接跳过,不更新状态 */
|
|
|
|
|
|
|
|
|
|
int level = _pBtn->m_pReadFunc();
|
|
|
|
|
switch (_pBtn->m_tState)
|
|
|
|
|
{
|
|
|
|
|
case STATE_NONE_PRESS:
|
|
|
|
|
{
|
|
|
|
|
if (level == _pBtn->m_iActiveLogic)
|
|
|
|
|
{
|
|
|
|
|
_pBtn->m_iTimeout = Rd_GetTime() + _pBtn->m_iEliminationTime;
|
|
|
|
|
_pBtn->m_tState = STATE_CHECK_PRESS;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* 间隔过大,清零 */
|
|
|
|
|
if (_pBtn->m_iRepeatCnt)
|
|
|
|
|
{
|
|
|
|
|
if ((Rd_GetTime() - _pBtn->m_iTimeout) > (UINT32_MAX / 2))
|
|
|
|
|
{
|
|
|
|
|
_pBtn->m_iRepeatCnt = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
case STATE_CHECK_PRESS:
|
|
|
|
|
{
|
|
|
|
|
if (level == _pBtn->m_iActiveLogic)
|
|
|
|
|
{
|
|
|
|
|
if ((Rd_GetTime() - _pBtn->m_iTimeout) < (UINT32_MAX / 2))
|
|
|
|
|
{
|
|
|
|
|
_pBtn->m_tState = STATE_PRESS_DOWN;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_pBtn->m_tState = STATE_NONE_PRESS;
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
case STATE_PRESS_DOWN:
|
|
|
|
|
{
|
|
|
|
|
_pBtn->m_iHoldTime = 0;
|
|
|
|
|
_pBtn->m_iPrevHoldTime = 0;
|
|
|
|
|
_pBtn->m_iRepeatCnt++;
|
|
|
|
|
_pBtn->m_tEvent = EVENT_PRESS_DOWN;
|
|
|
|
|
RD_BUTTON_EVENT_CBK(_pBtn, _pBtn->m_tEvent);
|
|
|
|
|
|
|
|
|
|
_pBtn->m_iTimeout = Rd_GetTime();
|
|
|
|
|
_pBtn->m_tState = STATE_PRESS_HOLD;
|
|
|
|
|
|
|
|
|
|
/* ===== 新增:按下后更新优先级锁 ===== */
|
|
|
|
|
rd_BtnUpdatePriorityLock();
|
|
|
|
|
} break;
|
|
|
|
|
case STATE_PRESS_HOLD:
|
|
|
|
|
{
|
|
|
|
|
if (level == _pBtn->m_iActiveLogic)
|
|
|
|
|
{
|
|
|
|
|
rd_BtnCalHoldTime(_pBtn);
|
|
|
|
|
if (_pBtn->m_iHoldTime - _pBtn->m_iPrevHoldTime >= _pBtn->m_iHoldCycleTime)
|
|
|
|
|
{
|
|
|
|
|
_pBtn->m_tEvent = EVENT_HOLD;
|
|
|
|
|
RD_BUTTON_EVENT_CBK(_pBtn, _pBtn->m_tEvent);
|
|
|
|
|
_pBtn->m_iPrevHoldTime = _pBtn->m_iHoldTime;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_pBtn->m_tState = STATE_PRESS_UP;
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
case STATE_PRESS_UP:
|
|
|
|
|
{
|
|
|
|
|
_pBtn->m_tEvent = EVENT_PRESS_UP;
|
|
|
|
|
RD_BUTTON_EVENT_CBK(_pBtn, _pBtn->m_tEvent);
|
|
|
|
|
_pBtn->m_tEvent = EVENT_CLICK;
|
|
|
|
|
RD_BUTTON_EVENT_CBK(_pBtn, _pBtn->m_tEvent);
|
|
|
|
|
|
|
|
|
|
_pBtn->m_iTimeout = Rd_GetTime() + RD_BUTTON_TWO_INTERVAL_TIME_DEFAULT;
|
|
|
|
|
_pBtn->m_tState = STATE_NONE_PRESS;
|
|
|
|
|
|
|
|
|
|
/* ===== 新增:释放后更新优先级锁 ===== */
|
|
|
|
|
rd_BtnUpdatePriorityLock();
|
|
|
|
|
} break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Rd_MutexUnlock(&g_fpBtnCtrl, __func__, NULL);
|
|
|
|
|
}
|
|
|
|
|
|