按键能触发

This commit is contained in:
stark1898y 2024-12-14 13:31:23 +08:00
parent 09c5acf69f
commit 3791a5d416
17 changed files with 1185 additions and 36 deletions

View File

@ -56,6 +56,7 @@
<tool id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.compiler.1731377187" name="GNU RISC-V Cross C Compiler" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.compiler"> <tool id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.compiler.1731377187" name="GNU RISC-V Cross C Compiler" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.compiler">
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.include.paths.1567947810" name="Include paths (-I)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.include.paths" useByScannerDiscovery="true" valueType="includePath"> <option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.include.paths.1567947810" name="Include paths (-I)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.include.paths" useByScannerDiscovery="true" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Startup}&quot;"/> <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Startup}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/common/FlexibleButton-2.0.1}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/common/letter-shell}&quot;"/> <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/common/letter-shell}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/common/letter-shell/extensions/log}&quot;"/> <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/common/letter-shell/extensions/log}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/common/lwrb-3.2.0}&quot;"/> <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/common/lwrb-3.2.0}&quot;"/>

View File

@ -5,7 +5,7 @@
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/> <provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/> <provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/> <provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="-689880543476938240" id="ilg.gnumcueclipse.managedbuild.cross.riscv.GCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT RISC-V Cross GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} ${cross_toolchain_flags} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true"> <provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="-782333052934719487" id="ilg.gnumcueclipse.managedbuild.cross.riscv.GCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT RISC-V Cross GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} ${cross_toolchain_flags} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/> <language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/> <language-scope id="org.eclipse.cdt.core.g++"/>
</provider> </provider>

View File

@ -2,6 +2,7 @@ eclipse.preferences.version=1
encoding//APP/peripheral.c=UTF-8 encoding//APP/peripheral.c=UTF-8
encoding//APP/peripheral_main.c=UTF-8 encoding//APP/peripheral_main.c=UTF-8
encoding//BSP/inc/bsp_valve.h=UTF-8 encoding//BSP/inc/bsp_valve.h=UTF-8
encoding//BSP/src/bsp_key.c=UTF-8
encoding//BSP/src/bsp_valve.c=UTF-8 encoding//BSP/src/bsp_valve.c=UTF-8
encoding//common/letter-shell/extensions/log/log.h=UTF-8 encoding//common/letter-shell/extensions/log/log.h=UTF-8
encoding//common/letter-shell/shell.c=UTF-8 encoding//common/letter-shell/shell.c=UTF-8

View File

@ -14,6 +14,8 @@
"atomic": "cpp", "atomic": "cpp",
"cstddef": "cpp", "cstddef": "cpp",
"type_traits": "cpp", "type_traits": "cpp",
"shell.h": "c" "shell.h": "c",
} "ch59x_common.h": "c"
},
"marscode.chatLanguage": "cn"
} }

View File

@ -36,14 +36,8 @@
// static uint8_t main_task_id = INVALID_TASK_ID; // static uint8_t main_task_id = INVALID_TASK_ID;
/*********************************************************************
* GLOBAL TYPEDEFS
*/
__attribute__((aligned(4))) uint32_t MEM_BUF[BLE_MEMHEAP_SIZE / 4];
#if (defined(BLE_MAC)) && (BLE_MAC == TRUE) __attribute__((aligned(4))) uint32_t MEM_BUF[BLE_MEMHEAP_SIZE / 4];
const uint8_t MacAddr[6] = {0x84, 0xC2, 0xE4, 0x03, 0x02, 0x02};
#endif
/********************************************************************* /*********************************************************************
* @fn Main_Circulation * @fn Main_Circulation
@ -53,12 +47,14 @@ const uint8_t MacAddr[6] = {0x84, 0xC2, 0xE4, 0x03, 0x02, 0x02};
* @return none * @return none
*/ */
__HIGH_CODE __HIGH_CODE
__attribute__((noinline)) void Main_Circulation() __attribute__((noinline))
void Main_Circulation()
{ {
while (1) while (1)
{ {
TMOS_SystemProcess(); TMOS_SystemProcess();
BSP_UART1_TxLoop(); // BSP_UART1_TxLoop();
#if 0
if (GPIOB_ReadPortPin(GPIO_Pin_7) == 0) if (GPIOB_ReadPortPin(GPIO_Pin_7) == 0)
{ {
// 12V_EN // 12V_EN
@ -122,6 +118,7 @@ __attribute__((noinline)) void Main_Circulation()
EMV_CHARGE_OFF_DEINIT; EMV_CHARGE_OFF_DEINIT;
EMV_OFF_DEINIT; EMV_OFF_DEINIT;
} }
#endif
} }
} }
@ -178,7 +175,7 @@ int main(void)
EMV_CHARGE_OFF_DEINIT; EMV_CHARGE_OFF_DEINIT;
EMV_OFF_DEINIT; EMV_OFF_DEINIT;
BSP_KEY_Init(); // BSP_KEY_Init();
BSP_ADC_Init(); BSP_ADC_Init();
CH59x_BLEInit(); CH59x_BLEInit();
@ -192,13 +189,14 @@ int main(void)
LL_AdvertiseEventRegister(BLE_AdvertiseEventCB); LL_AdvertiseEventRegister(BLE_AdvertiseEventCB);
DelayMs(100); // DelayMs(100);
GXHTC3C_Init(); GXHTC3C_Init();
while (1) // while (1)
{ // {
DelayMs(100); // DelayMs(100);
} // }
BSP_KEY_Init();
Main_Circulation(); Main_Circulation();
} }

31
BSP/inc/bsp_iwdg.h Normal file
View File

@ -0,0 +1,31 @@
/*
* @Author : stark1898y 1658608470@qq.com
* @Date : 2024-12-14 10:51:01
* @LastEditors : stark1898y 1658608470@qq.com
* @LastEditTime : 2024-12-14 10:51:10
* @FilePath : \BLE_TYQ_CH592F\BSP\inc\bsp_iwdg.h
* @Description :
*
* Copyright (c) 2024 by yzy, All Rights Reserved.
*/
#ifndef __BSP_IWDG_H__
#define __BSP_IWDG_H__
#include "CONFIG.h"
#define RB_RLR 0x0FFF // RW, watch-dog counter reload (write protect)
#define RB_PR 0x7000 // PR, prescale (write protect)
#define RB_PVU 0x8000 // RO, register update flag (write protect)
#define RB_COUNT 0xFF0000 // RO, watch-dog down counter
#define RB_STOP_EN 0x20000000 // RW, watch-dog stop enable (write protect)
#define RB_WR_PROTECT 0x40000000 // RO, write protect
#define RB_IWDG_EN 0x80000000 // RO, watch-dog enable
#define ch59x_iwdg_feed() (R32_IWDG_KR = 0xaaaa)
#endif // !__BSP_IWDG_H__

View File

@ -1,17 +1,33 @@
#ifndef __BSP_KEY_H__ #ifndef __BSP_KEY_H__
#define __BSP_KEY_H__ #define __BSP_KEY_H__
#include "CONFIG.h"
#include "CH59x_common.h" #include <stdint.h>
#include <stdbool.h>
#define KEY_SCAN_EVT (0x0001 << 0)
#define DEMO_TASK_TMOS_EVT_TEST_2 (0x0001 << 1)
#define DEMO_TASK_TMOS_EVT_TEST_3 (0x0001 << 2)
#define DEMO_TASK_TMOS_EVT_TEST_4 (0x0001 << 3)
#define DEMO_TASK_TMOS_EVT_TEST_5 (0x0001 << 4)
// KEY_B_PIN at PB7,low->active
#define KEY_B_PIN GPIO_Pin_7
#define IS_KEY_Vaild() ((GPIOB_ReadPortPin(KEY_B_PIN) ? 0 : 1))
typedef enum
{
kKeyShort = 0,
kKeyLong,
kKeyRelease,
} TeKeyEvtType;
void BSP_KEY_EnterLowpower(void);
void BSP_KEY_ExitLowpower(void);
void BSP_KEY_Init(void); void BSP_KEY_Init(void);
#endif // !__BSP_KEY_H__ #endif // !__BSP_KEY_H__

View File

@ -70,6 +70,7 @@ void BSP_ADC_Init(void)
logDebug("AverageCountBat = %d", CountBat); logDebug("AverageCountBat = %d", CountBat);
#endif #endif
#if 0
// 默认情况下ADC 引脚和所在 GPIO 引脚的数字功能是同时存在的 // 默认情况下ADC 引脚和所在 GPIO 引脚的数字功能是同时存在的
// ,在进行 ADC 测量时候,需要吧 GPIO 设置为高阻输入 // ,在进行 ADC 测量时候,需要吧 GPIO 设置为高阻输入
// ,当 ADC 的电平处于中间态的时候,这时候往往会导致数字部分漏电 // ,当 ADC 的电平处于中间态的时候,这时候往往会导致数字部分漏电
@ -130,4 +131,7 @@ void BSP_ADC_Init(void)
voltage = (double)(countadc) / 2048 * 1.05; voltage = (double)(countadc) / 2048 * 1.05;
logDebug("voltage=%1.3lf V", voltage); logDebug("voltage=%1.3lf V", voltage);
logDebug("VIN: %1.3lf V", voltage * 2); logDebug("VIN: %1.3lf V", voltage * 2);
#endif
} }

View File

@ -349,13 +349,13 @@ uint8_t GXHTC3C_GetTempHumi(float *humi, float *temp)
uint8_t humi_raw_data[2]; uint8_t humi_raw_data[2];
uint8_t temp_raw_data[2]; uint8_t temp_raw_data[2];
humi_raw_data[0] = i2c_rx_data[0]; humi_raw_data[0] = i2c_rx_data[0];
humi_raw_data[1] = i2c_rx_data[1]; humi_raw_data[1] = i2c_rx_data[1];
uint16_t raw_humi = (humi_raw_data[0] << 8) | humi_raw_data[1]; uint16_t raw_humi = (humi_raw_data[0] << 8) | humi_raw_data[1];
uint8_t crc_humi = GXHTC3C_CRC_8(humi_raw_data, 2); uint8_t crc_humi = GXHTC3C_CRC_8(humi_raw_data, 2);
temp_raw_data[0] = i2c_rx_data[3]; temp_raw_data[0] = i2c_rx_data[3];
temp_raw_data[1] = i2c_rx_data[4]; temp_raw_data[1] = i2c_rx_data[4];
uint16_t raw_temp = (temp_raw_data[0] << 8) | temp_raw_data[1]; uint16_t raw_temp = (temp_raw_data[0] << 8) | temp_raw_data[1];
uint8_t crc_temp = GXHTC3C_CRC_8(temp_raw_data, 2); uint8_t crc_temp = GXHTC3C_CRC_8(temp_raw_data, 2);
@ -364,7 +364,7 @@ uint8_t GXHTC3C_GetTempHumi(float *humi, float *temp)
logDebug("crc ok"); logDebug("crc ok");
logHexDumpAll(i2c_rx_data, 6); logHexDumpAll(i2c_rx_data, 6);
*humi = (100.0 * raw_humi) / 65536.0; // 湿度真实值 *humi = (100.0 * raw_humi) / 65536.0; // 湿度真实值
*temp = (175.0 * raw_temp) / 65536.0 - 45.0; // 温度真实值 *temp = (175.0 * raw_temp) / 65536.0 - 45.0; // 温度真实值
// logDebug("humi %f, temp %f", *humi, *temp); // logDebug("humi %f, temp %f", *humi, *temp);
} }
@ -399,6 +399,8 @@ void GXHTC3C_Init(void)
logDebug("humi %.2f %, temp %.2f C", humi, temp); logDebug("humi %.2f %, temp %.2f C", humi, temp);
} }
GXHTC3C_Sleep();
#endif #endif
} }

23
BSP/src/bsp_iwdg.c Normal file
View File

@ -0,0 +1,23 @@
#include "bsp_iwdg.h"
#include "bsp_uart.h"
#include "log.h"
#undef LOG_ENABLE
#define LOG_ENABLE 1
#undef LOG_TAG
#define LOG_TAG "idwg"
// 32k rc
// div 0:4 | 1:8 | 2:16 | 3:32 |
// 4:64 | 5:128 | 6:256 | 7:512 |
// reload max 4095
void ch59x_iwdg_init(uint8_t div, uint16_t reload)
{
R32_IWDG_KR = 0x5555;
R32_IWDG_CFG = (RB_PR & (div << 12)) | (RB_STOP_EN) | (reload & RB_RLR);
R32_IWDG_KR = 0xcccc; // enable
}

View File

@ -1,14 +1,270 @@
#include "bsp_key.h" #include "bsp_key.h"
#include "flexible_button.h"
#include "bsp_uart.h"
#include "log.h"
// https://www.cnblogs.com/iot-fan/p/14304943.html
#undef LOG_ENABLE
#define LOG_ENABLE 1
#undef LOG_TAG
#define LOG_TAG "key"
typedef enum
{
USER_BUTTON_0 = 0,
USER_BUTTON_MAX
} user_button_t;
#if 1
#define ENUM_TO_STR(e) (#e)
static char *enum_event_string[] = {
ENUM_TO_STR(FLEX_BTN_PRESS_DOWN),
ENUM_TO_STR(FLEX_BTN_PRESS_CLICK),
ENUM_TO_STR(FLEX_BTN_PRESS_DOUBLE_CLICK),
ENUM_TO_STR(FLEX_BTN_PRESS_REPEAT_CLICK),
ENUM_TO_STR(FLEX_BTN_PRESS_SHORT_START),
ENUM_TO_STR(FLEX_BTN_PRESS_SHORT_UP),
ENUM_TO_STR(FLEX_BTN_PRESS_LONG_START),
ENUM_TO_STR(FLEX_BTN_PRESS_LONG_UP),
ENUM_TO_STR(FLEX_BTN_PRESS_LONG_HOLD),
ENUM_TO_STR(FLEX_BTN_PRESS_LONG_HOLD_UP),
ENUM_TO_STR(FLEX_BTN_PRESS_MAX),
ENUM_TO_STR(FLEX_BTN_PRESS_NONE),
};
static char *enum_btn_id_string[] = {
ENUM_TO_STR(USER_BUTTON_0),
ENUM_TO_STR(USER_BUTTON_MAX),
};
#endif
static flex_button_t user_button[USER_BUTTON_MAX];
tmosTaskID key_task_id = INVALID_TASK_ID;
volatile uint8_t key_process_flag = 0;
static void KEY_Task_ProcessTmosMsg(tmos_event_hdr_t *pMsg)
{
switch (pMsg->event)
{
default:
logDebug("pMsg->event %04x", pMsg->event);
break;
}
}
#if 0
static uint16_t KEY_Task_ProcessEvent(uint8_t task_id, uint16_t events)
{
if (events & SYS_EVENT_MSG)
{
uint8_t *pMsg;
if ((pMsg = tmos_msg_receive(key_task_id)) != NULL)
{
KEY_Task_ProcessTmosMsg((tmos_event_hdr_t *)pMsg);
// Release the TMOS message
tmos_msg_deallocate(pMsg);
}
// return unprocessed events
return (events ^ SYS_EVENT_MSG);
}
if (events & KEY_SCAN_EVT)
{
static volatile uint8_t key_vaild_times = 0;
static volatile bool key_vaild_for_long_press = false;
if (IS_KEY_Vaild())
{
if (key_vaild_times > 100)
{ //> 20ms*100=2000ms
if (false == key_vaild_for_long_press)
{
if (NULL != p_handler)
{
p_handler(kKeyLong);
}
// PRINT("WE should power switch here\r\n");
key_vaild_for_long_press = true;
}
}
else
{
key_vaild_times++;
}
key_timeout_cnt = 10;
}
else
{ // button release
if (key_vaild_times)
{
if ((key_vaild_times) < 100)
{
p_handler(kKeyShort);
}
key_vaild_times = 0;
// PRINT("KEY VAILED\r\n");
}
key_vaild_for_long_press = false;
}
if (key_timeout_cnt)
{
key_timeout_cnt--;
tmos_start_task(key_task_id, KEY_SCAN_EVT, 64); // 40ms
}
else
{
p_handler(kKeyRelease);
}
return (events ^ KEY_SCAN_EVT);
}
// Discard unknown events
return 0;
}
#endif
// 由按键中断唤醒后开启按键扫描
void BSP_KEY_EnterLowpower(void)
{
key_process_flag = 0;
tmos_stop_task(key_task_id, KEY_SCAN_EVT);
// R16_PB_INT_MODE |= KEY_B_PIN; // edge mode
// GPIOB_ResetBits(KEY_B_PIN); // edge fall
// R16_PB_INT_IF = KEY_B_PIN;
// R16_PB_INT_EN |= KEY_B_PIN;
// TODO:按键电平触发设置
// 下降沿触发
GPIOB_ITModeCfg(KEY_B_PIN, GPIO_ITMode_FallEdge);
// 开启GPIO的睡眠唤醒,如果需要的话
// PWR_PeriphWakeUpCfg(ENABLE, RB_SLP_GPIO_WAKE, Long_Delay);
PWR_PeriphWakeUpCfg(ENABLE, RB_GPIO_WAKE_MODE | RB_SLP_GPIO_WAKE, Long_Delay);
// 开启GPIOB中断
PFIC_EnableIRQ(GPIO_B_IRQn);
}
void BSP_KEY_ExitLowpower(void)
{
// 关闭GPIOB中断
PFIC_DisableIRQ(GPIO_B_IRQn);
PWR_PeriphWakeUpCfg(DISABLE, RB_GPIO_WAKE_MODE | RB_SLP_GPIO_WAKE, Long_Delay);
tmos_set_event(key_task_id, KEY_SCAN_EVT);
key_process_flag = 1;
}
void KEY_ProcessLoop(void)
{
if (key_process_flag)
{
/* code */
}
}
static uint8_t common_btn_read(void *arg)
{
uint8_t value = 0;
flex_button_t *btn = (flex_button_t *)arg;
switch (btn->id)
{
case USER_BUTTON_0:
value = GPIOB_ReadPortPin(KEY_B_PIN);
break;
default:
logAssert(0,while(1));
}
return value;
}
static void common_btn_evt_cb(void *arg)
{
flex_button_t *btn = (flex_button_t *)arg;
logDebug("id: [%d - %s] event: [%d - %30s] repeat: %d",
btn->id, enum_btn_id_string[btn->id],
btn->event, enum_event_string[btn->event],
btn->click_cnt);
}
static uint16_t KEY_Task_ProcessEvent(uint8_t task_id, uint16_t events)
{
if (events & SYS_EVENT_MSG)
{
uint8_t *pMsg;
if ((pMsg = tmos_msg_receive(key_task_id)) != NULL)
{
KEY_Task_ProcessTmosMsg((tmos_event_hdr_t *)pMsg);
// Release the TMOS message
tmos_msg_deallocate(pMsg);
}
// return unprocessed events
return (events ^ SYS_EVENT_MSG);
}
if (events & KEY_SCAN_EVT)
{
flex_button_scan();
tmos_start_task(key_task_id, KEY_SCAN_EVT, MS1_TO_SYSTEM_TIME(30));
return (events ^ KEY_SCAN_EVT);
}
// Discard unknown events
return 0;
}
void BSP_KEY_Init(void) void BSP_KEY_Init(void)
{ {
// KEY /* 初始化按键数据结构 */
GPIOB_SetBits(GPIO_Pin_7); tmos_memset(&user_button[0], 0x0, sizeof(user_button));
// 由外部上拉电阻了 // 由外部上拉电阻了
GPIOB_ModeCfg(GPIO_Pin_7, GPIO_ModeIN_Floating); // 设置为浮空输入模式
GPIOB_ModeCfg(KEY_B_PIN, GPIO_ModeIN_PU);
for (uint8_t i = 0; i < USER_BUTTON_MAX; i ++)
{
user_button[i].id = i;
user_button[i].usr_button_read = common_btn_read;
user_button[i].cb = common_btn_evt_cb;
user_button[i].pressed_logic_level = 0;
user_button[i].short_press_start_tick = FLEX_MS_TO_SCAN_CNT(1500);
user_button[i].long_press_start_tick = FLEX_MS_TO_SCAN_CNT(3000);
user_button[i].long_hold_start_tick = FLEX_MS_TO_SCAN_CNT(4500);
flex_button_register(&user_button[i]);
}
key_task_id = TMOS_ProcessEventRegister(KEY_Task_ProcessEvent);
tmos_start_task(key_task_id, KEY_SCAN_EVT, MS1_TO_SYSTEM_TIME(30));
}
__INTERRUPT // 告诉编译器使用硬件压栈
__HIGH_CODE // 放到RAM里
void GPIOB_IRQHandler(void)
{
if (R16_PB_INT_IF & KEY_B_PIN)
{
R16_PB_INT_IF = KEY_B_PIN;
tmos_set_event(key_task_id, KEY_SCAN_EVT);
BSP_KEY_ExitLowpower();
logDebug("KEY IRQ");
}
// GPIOB_ClearITFlagBit(KEY_B_PIN);
// tmos_set_event(key_task_id,KEY_SCAN_EVT);
} }

View File

@ -14,6 +14,10 @@
/* 头文件包含 */ /* 头文件包含 */
#include "HAL.h" #include "HAL.h"
#include "bsp_key.h"
/******************************************************************************* /*******************************************************************************
* @fn CH59x_LowPower * @fn CH59x_LowPower
* *
@ -76,10 +80,15 @@ uint32_t CH59x_LowPower(uint32_t time)
// LOW POWER-sleep模式 // LOW POWER-sleep模式
if (!RTCTigFlag) if (!RTCTigFlag)
{ {
BSP_KEY_EnterLowpower();
LowPower_Sleep(RB_PWR_RAM2K | RB_PWR_RAM24K | RB_PWR_EXTEND | RB_XT_PRE_EN); LowPower_Sleep(RB_PWR_RAM2K | RB_PWR_RAM24K | RB_PWR_EXTEND | RB_XT_PRE_EN);
HSECFG_Current(HSE_RCur_100); // 降为额定电流(低功耗函数中提升了HSE偏置电流) HSECFG_Current(HSE_RCur_100); // 降为额定电流(低功耗函数中提升了HSE偏置电流)
i = RTC_GetCycle32k(); i = RTC_GetCycle32k();
while (i == RTC_GetCycle32k()); while (i == RTC_GetCycle32k());
// BSP_KEY_ExitLowpower();
return 0; return 0;
} }
#endif #endif

View File

@ -124,7 +124,7 @@
#define CLK_OSC32K 1 // 该项请勿在此修改必须在工程配置里的预处理中修改如包含主机角色必须使用外部32K #define CLK_OSC32K 1 // 该项请勿在此修改必须在工程配置里的预处理中修改如包含主机角色必须使用外部32K
#endif #endif
#ifndef BLE_MEMHEAP_SIZE #ifndef BLE_MEMHEAP_SIZE
#define BLE_MEMHEAP_SIZE (1024*6) #define BLE_MEMHEAP_SIZE (1024*4)
#endif #endif
#ifndef BLE_BUFF_MAX_LEN #ifndef BLE_BUFF_MAX_LEN
#define BLE_BUFF_MAX_LEN 251 #define BLE_BUFF_MAX_LEN 251

View File

@ -0,0 +1,325 @@
# FlexibleButton
FlexibleButton 是一个基于标准 C 语言的小巧灵活的按键处理库,支持单击、连击、短按、长按、自动消抖,可以自由设置组合按键,可用于中断和低功耗场景。
该按键库解耦了具体的按键硬件结构理论上支持轻触按键与自锁按键并可以无限扩展按键数量。另外FlexibleButton 使用扫描的方式一次性读取所有所有的按键状态,然后通过事件回调机制上报按键事件。核心的按键扫描代码仅有三行,没错,就是经典的 **三行按键扫描算法**。使用 C 语言标准库 API 编写,也使得该按键库可以无缝兼容任意的处理器平台,并且支持任意 OS 和 non-OS裸机编程
## 获取
### Git 方式
```SHELL
git clone https://github.com/murphyzhao/FlexibleButton.git
```
### RT-Thread menuconfig 方式
```
RT-Thread online packages --->
miscellaneous packages --->
[*] FlexibleButton: Small and flexible button driver --->
[*] Enable flexible button demo
version (latest) --->
```
配置完成后,输入 `pkgs --update` 下载软件包。
## 资源统计
ARMCC -O0 优化的情况下FlexibleButton 资源占用如下:
- CODE798 字节
- RO DATA0
- RW DATA13 字节
- ZI DATA0
## 快速体验
FlexibleButton 库中提供了一个测试例程 [`./examples/demo_rtt_iotboard.c`](./examples/demo_rtt_iotboard.c),该例程基于 RT-Thread OS 进行测试,硬件平台选择了 *RT-Thread IoT Board Pandora v2.51* 开发板。当然你可以选择使用其他的 OS或者使用裸机测试只需要移除 OS 相关的特性即可。
如果你使用自己的硬件平台,只需要将 FlexibleButton 库源码和例程加入你既有的工程下即可。
## DEMO 程序说明
该示例程序可以直接在 RT-Thread [`stm32l475-atk-pandora`](https://github.com/RT-Thread/rt-thread/tree/master/bsp/stm32/stm32l475-atk-pandora) BSP 中运行,可以在该 BSP 目录下,使用 menuconfig 获取本软件包。
### 确定用户按键
```C
typedef enum
{
USER_BUTTON_0 = 0, // 对应 IoT Board 开发板的 PIN_KEY0
USER_BUTTON_1, // 对应 IoT Board 开发板的 PIN_KEY1
USER_BUTTON_2, // 对应 IoT Board 开发板的 PIN_KEY2
USER_BUTTON_3, // 对应 IoT Board 开发板的 PIN_WK_UP
USER_BUTTON_MAX
} user_button_t;
static flex_button_t user_button[USER_BUTTON_MAX];
```
上述代码定义了 4 个按键,数据结构存储在 `user_button` 数组中。
### 程序入口
```C
int flex_button_main(void)
{
rt_thread_t tid = RT_NULL;
user_button_init();
/* 创建按键扫描线程 flex_btn线程栈 1024 byte优先级 10 */
tid = rt_thread_create("flex_btn", button_scan, RT_NULL, 1024, 10, 10);
if(tid != RT_NULL)
{
rt_thread_startup(tid);
}
return 0;
}
/* 使用 RT-Thread 的自动初始化 */
INIT_APP_EXPORT(flex_button_main);
```
如上代码所示,首先使用 `user_button_init();` 初始化用户按键硬件,该步骤将用户按键绑定到 FlexibleButton 库。然后,使用 RT-Thread 的 `INIT_APP_EXPORT` 接口导出为上电自动初始化,创建了一个 “flex_btn” 名字的按键扫描线程,线程里扫描检查按键事件。
### 按键初始化代码
`user_button_init();` 初始化代码如下所示:
```
static void user_button_init(void)
{
int i;
/* 初始化按键数据结构 */
rt_memset(&user_button[0], 0x0, sizeof(user_button));
/* 初始化 IoT Board 按键引脚,使用 rt-thread PIN 设备框架 */
rt_pin_mode(PIN_KEY0, PIN_MODE_INPUT_PULLUP); /* 设置 GPIO 为上拉输入模式 */
rt_pin_mode(PIN_KEY1, PIN_MODE_INPUT_PULLUP); /* 设置 GPIO 为上拉输入模式 */
rt_pin_mode(PIN_KEY2, PIN_MODE_INPUT_PULLUP); /* 设置 GPIO 为上拉输入模式 */
rt_pin_mode(PIN_WK_UP, PIN_MODE_INPUT_PULLDOWN); /* 设置 GPIO 为下拉输入模式 */
for (i = 0; i < USER_BUTTON_MAX; i ++)
{
user_button[i].id = i;
user_button[i].usr_button_read = common_btn_read;
user_button[i].cb = common_btn_evt_cb;
user_button[i].pressed_logic_level = 0;
user_button[i].short_press_start_tick = FLEX_MS_TO_SCAN_CNT(1500);
user_button[i].long_press_start_tick = FLEX_MS_TO_SCAN_CNT(3000);
user_button[i].long_hold_start_tick = FLEX_MS_TO_SCAN_CNT(4500);
if (i == USER_BUTTON_3)
{
user_button[USER_BUTTON_3].pressed_logic_level = 1;
}
flex_button_register(&user_button[i]);
}
}
```
核心的配置如下:
|配置项|说明|
| :---- | :----|
| id | 按键编号 |
| usr_button_read | 设置按键读值回调函数 |
| cb | 设置按键事件回调函数 |
| pressed_logic_level | 设置按键按下时的逻辑电平 |
| short_press_start_tick | 短按起始 tick使用 FLEX_MS_TO_SCAN_CNT 宏转化为扫描次数 |
| long_press_start_tick | 长按起始 tick使用 FLEX_MS_TO_SCAN_CNT 宏转化为扫描次数 |
| long_hold_start_tick | 超长按起始 tick使用 FLEX_MS_TO_SCAN_CNT 宏转化为扫描次数 |
注意short_press_start_tick、long_press_start_tick 和 long_hold_start_tick 必须使用 `FLEX_MS_TO_SCAN_CNT` 将毫秒时间转化为扫描次数。
`user_button[i].short_press_start_tick = FLEX_MS_TO_SCAN_CNT(1500);` 表示按键按下开始计时1500 ms 后按键依旧是按下状态的话,就断定为短按开始。
### 事件处理代码
```C
static void common_btn_evt_cb(void *arg)
{
flex_button_t *btn = (flex_button_t *)arg;
rt_kprintf("id: [%d - %s] event: [%d - %30s] repeat: %d\n",
btn->id, enum_btn_id_string[btn->id],
btn->event, enum_event_string[btn->event],
btn->click_cnt);
if (flex_button_event_read(&user_button[USER_BUTTON_0]) == flex_button_event_read(&user_button[USER_BUTTON_1]) == FLEX_BTN_PRESS_CLICK)
{
rt_kprintf("[combination]: button 0 and button 1\n");
}
}
```
示例代码中,将所有的按键事件回调均绑定到 `common_btn_evt_cb` 函数,在该函数中打印了按键 ID 和按键事件,以及按键连击次数,并演示了如何使用组合按键。
## FlexibleButton 代码说明
### 按键事件定义
按键事件的定义并没有使用 Windows 驱动上的定义,主要是方便嵌入式设备中的应用场景(也可能是我理解的偏差),按键事件定义如下:
```C
typedef enum
{
FLEX_BTN_PRESS_DOWN = 0, // 按下事件
FLEX_BTN_PRESS_CLICK, // 单击事件
FLEX_BTN_PRESS_DOUBLE_CLICK, // 双击事件
FLEX_BTN_PRESS_REPEAT_CLICK, // 连击事件,使用 flex_button_t 中的 click_cnt 断定连击次数
FLEX_BTN_PRESS_SHORT_START, // 短按开始事件
FLEX_BTN_PRESS_SHORT_UP, // 短按抬起事件
FLEX_BTN_PRESS_LONG_START, // 长按开始事件
FLEX_BTN_PRESS_LONG_UP, // 长按抬起事件
FLEX_BTN_PRESS_LONG_HOLD, // 长按保持事件
FLEX_BTN_PRESS_LONG_HOLD_UP, // 长按保持的抬起事件
FLEX_BTN_PRESS_MAX,
FLEX_BTN_PRESS_NONE,
} flex_button_event_t;
```
其中 `FLEX_BTN_PRESS_LONG_HOLD` 事件可以用来实现长按累加的应用场景。
### 按键数据结构
```
typedef struct flex_button
{
struct flex_button* next;
uint8_t (*usr_button_read)(void *);
flex_button_response_callback cb;
uint16_t scan_cnt;
uint16_t click_cnt;
uint16_t max_multiple_clicks_interval;
uint16_t debounce_tick;
uint16_t short_press_start_tick;
uint16_t long_press_start_tick;
uint16_t long_hold_start_tick;
uint8_t id;
uint8_t pressed_logic_level : 1;
uint8_t event : 4;
uint8_t status : 3;
} flex_button_t;
```
| 序号 | 数据成员 | 是否需要用户初始化 | 说明 |
| :----: | :---- | :----: | :---- |
| 1 | next | 否 | 按键库使用单向链表串起所有的按键 |
| 2 | usr_button_read | 是 | 用户设备的按键引脚电平读取函数,**重要** |
| 3 | cb | 是 | 设置按键事件回调,用于应用层对按键事件的分类处理 |
| 4 | scan_cnt | 否 | 用于记录扫描次数,按键按下是开始从零计数 |
| 5 | click_cnt | 否 | 记录单击次数,用于判定单击、连击 |
| 6 | max_multiple_clicks_interval | 是 | 连击间隙,用于判定是否结束连击计数,有默认值 `MAX_MULTIPLE_CLICKS_INTERVAL` |
| 7 | debounce_tick | 否 | 消抖时间,暂未使用,依靠扫描间隙进行消抖 |
| 8 | short_press_start_tick | 是 | 设置短按事件触发的起始 tick |
| 9 | long_press_start_tick | 是 | 设置长按事件触发的起始 tick |
| 10 | long_hold_start_tick | 是 | 设置长按保持事件触发的起始 tick |
| 11 | id | 是 | 当多个按键使用同一个回调函数时,用于断定属于哪个按键 |
| 12 | pressed_logic_level | 是 | 设置按键按下的逻辑电平。1标识按键按下的时候为高电平0标识按键按下的时候未低电平**重要** |
| 13 | event | 否 | 用于记录当前按键事件 |
| 14 | status | 否 | 用于记录当前按键的状态,用于内部状态机 |
注意,在使用 `max_multiple_clicks_interval`、`debounce_tick`、`short_press_start_tick`、`long_press_start_tick`、`long_hold_start_tick` 的时候,注意需要使用宏 `**FLEX_MS_TO_SCAN_CNT(ms)**` 将毫秒值转换为扫描次数。因为按键库基于扫描次数运转。示例如下:
```
user_button[1].short_press_start_tick = FLEX_MS_TO_SCAN_CNT(1500); // 1500 毫秒
```
上述代码表示表示按键按下后开始计时1500ms 的时候,按键依旧按下,则断定为短按开始,并上报 `FLEX_BTN_PRESS_SHORT_START` 事件。
### 按键注册接口
使用该接口注册一个用户按键,入参为一个 flex_button_t 结构体实例的地址。
```C
int8_t flex_button_register(flex_button_t *button);
```
### 按键事件读取接口
使用该接口获取指定按键的事件。
```C
flex_button_event_t flex_button_event_read(flex_button_t* button);
````
### 按键扫描接口
按键扫描的核心函数,需要放到应用程序中定时扫描,扫描间隔建议 20 毫秒。
```C
void flex_button_scan(void);
```
## 注意事项
- 阻塞问题
因为按键事件回调函数以及按键键值读取函数是在按键扫描的过程中执行的,因此请不要在这类函数中使用阻塞接口,不要进行延时操作。
- 按键扫描函数栈需求
按键扫描函数本身对栈的需求小于 300 字节,但是按键事件回调函数和按键键值读取函数都是在按键扫描函数的上下文中执行的,请格外关心按键事件回调函数与按键键值读取函数对栈空间的需求。
## 其它
### 关于低功耗
本按键库是通过不间断扫描的方式来检查按键状态,因此会一直占用 CPU 资源这对低功耗应用场景是不友好的。为了降低正常工作模式下的功耗建议合理配置扫描周期5ms - 20ms扫描间隙里 CPU 可以进入轻度睡眠。
该按键库不在底层实现低功耗处理,应用层可以根据自己的功耗模式灵活处理,通常会有以下两种方式:
1. 进入低功耗前,挂起按键扫描线程;退出低功耗后,唤醒按键扫描。
2. 增加按键中断模式,所有的按键中断来,就触发一次按键扫描,以确认所有的按键状态。
> 低功耗相关的探讨参考 [issue 1](https://github.com/murphyzhao/FlexibleButton/issues/1) 中的讨论。
### 关于按键中断模式
由于该按键库一次扫描可以确定所有的按键状态,因此可以将所有的按键中断通过 “**或**” 的方式转化为一个中断,然后在中断处理函数中执行一次按键扫描。
中断 “**或**” 的方式可以通过硬件来完成,也可以通过软件来完成。
硬件方式,需要使用一个 **或门** 芯片,多个输入条件转化为一个输出条件,然后通过一个外部中断即可完成所有按键的中断方式检测。
软件方式,需要为每一个按键配置为中断触发模式,然后在每一个按键中断的中断处理函数中执行按键扫描。
为了在降低中断处理函数中执行按键扫描带来的时延,可以通过信号量的方式来异步处理,仅在中断处理函数中释放一个按键扫描的信号量,然后在按键扫描线程中监测该信号量。
### 关于组合按键
该按键库仅做了底层的按键扫描处理,一次扫描可以确定所有的按键状态,并上报对应的按键事件,如果需要支持组合按键,请再封一层,根据按键库返回的事件封装需要的组合按键。[示例程序](./examples/demo_rtt_iotboard.c)提供了简单的实现。
### 关于矩阵键盘
不管你的矩阵键盘是通过什么通信方式获取按键状态的,只要你将读取按键状态的函数对接到 Flexible_button 数据结构中的 `uint8_t (*usr_button_read)(void*);` 函数上即可。
> 参考 [issue 2](https://github.com/murphyzhao/FlexibleButton/issues/2) 中的讨论。
## 问题和建议
如果有什么问题或者建议欢迎提交 [Issue](https://github.com/murphyzhao/FlexibleButton/issues) 进行讨论。
## 维护
- [MurphyZhao](https://github.com/murphyzhao)
## 感谢
感谢所有一起探讨的朋友,感谢所有使用 flexible_button 的朋友,感谢你们的 Star 和 Fork谢谢你们的支持。
- 感谢 [BOBBOM](https://github.com/BOBBOM) 发现 flex_button_register 函数中的逻辑问题
- 感谢 [BOBBOM](https://github.com/BOBBOM) 解除 flexible_button 中对按键数量的限制
- 感谢 [**rt-thread**](https://mp.weixin.qq.com/s/HJEcSXhykBq1T5Hx0TdjMw) 的支持
- 感谢 [**电子发烧友**](https://mp.weixin.qq.com/s/mQFyrPAvz_TSktQLrSqQfA) 的支持
- 感谢 [**威驰电子**](https://mp.weixin.qq.com/s/oAwFXPostMFBtb2EGxTdig) 的支持
## 友情链接
- RT-Thread [IoT Board](https://github.com/RT-Thread/IoT_Board) 开发板

View File

@ -0,0 +1,317 @@
/**
* @File: flexible_button.c
* @Author: MurphyZhao
* @Date: 2018-09-29
*
* Copyright (c) 2018-2019 MurphyZhao <d2014zjt@163.com>
* https://github.com/murphyzhao
* All rights reserved.
* License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Change logs:
* Date Author Notes
* 2018-09-29 MurphyZhao First add
* 2019-08-02 MurphyZhao Migrate code to github.com/murphyzhao account
* 2019-12-26 MurphyZhao Refactor code and implement multiple clicks
*
*/
#include "flexible_button.h"
#ifndef NULL
#define NULL 0
#endif
#define EVENT_SET_AND_EXEC_CB(btn, evt) \
do \
{ \
btn->event = evt; \
if(btn->cb) \
btn->cb((flex_button_t*)btn); \
} while(0)
/**
* BTN_IS_PRESSED
*
* 1: is pressed
* 0: is not pressed
*/
#define BTN_IS_PRESSED(i) (g_btn_status_reg & (1 << i))
enum FLEX_BTN_STAGE
{
FLEX_BTN_STAGE_DEFAULT = 0,
FLEX_BTN_STAGE_DOWN = 1,
FLEX_BTN_STAGE_MULTIPLE_CLICK = 2
};
typedef uint32_t btn_type_t;
static flex_button_t *btn_head = NULL;
/**
* g_logic_level
*
* The logic level of the button pressed,
* Each bit represents a button.
*
* First registered button, the logic level of the button pressed is
* at the low bit of g_logic_level.
*/
btn_type_t g_logic_level = (btn_type_t)0;
/**
* g_btn_status_reg
*
* The status register of all button, each bit records the pressing state of a button.
*
* First registered button, the pressing state of the button is
* at the low bit of g_btn_status_reg.
*/
btn_type_t g_btn_status_reg = (btn_type_t)0;
static uint8_t button_cnt = 0;
/**
* @brief Register a user button
*
* @param button: button structure instance
* @return Number of keys that have been registered
*/
int8_t flex_button_register(flex_button_t *button)
{
flex_button_t *curr = btn_head;
if (!button || (button_cnt > sizeof(btn_type_t) * 8))
{
return -1;
}
while (curr)
{
if(curr == button)
{
return -1; /* already exist. */
}
curr = curr->next;
}
/**
* First registered button is at the end of the 'linked list'.
* btn_head points to the head of the 'linked list'.
*/
button->next = btn_head;
button->status = FLEX_BTN_STAGE_DEFAULT;
button->event = FLEX_BTN_PRESS_NONE;
button->scan_cnt = 0;
button->click_cnt = 0;
button->max_multiple_clicks_interval = MAX_MULTIPLE_CLICKS_INTERVAL;
btn_head = button;
/**
* First registered button, the logic level of the button pressed is
* at the low bit of g_logic_level.
*/
g_logic_level |= (button->pressed_logic_level << button_cnt);
button_cnt ++;
return button_cnt;
}
/**
* @brief Read all key values in one scan cycle
*
* @param void
* @return none
*/
static void flex_button_read(void)
{
uint8_t i;
flex_button_t* target;
/* The button that was registered first, the button value is in the low position of raw_data */
btn_type_t raw_data = 0;
for(target = btn_head, i = button_cnt - 1;
(target != NULL) && (target->usr_button_read != NULL);
target = target->next, i--)
{
raw_data = raw_data | ((target->usr_button_read)(target) << i);
}
g_btn_status_reg = (~raw_data) ^ g_logic_level;
}
/**
* @brief Handle all key events in one scan cycle.
* Must be used after 'flex_button_read' API
*
* @param void
* @return none
*/
static void flex_button_process(void)
{
uint8_t i;
flex_button_t* target;
for (target = btn_head, i = 0; target != NULL; target = target->next, i ++)
{
if (target->status > FLEX_BTN_STAGE_DEFAULT)
{
target->scan_cnt ++;
if (target->scan_cnt >= ((1 << (sizeof(target->scan_cnt) * 8)) - 1))
{
target->scan_cnt = target->long_hold_start_tick;
}
}
switch (target->status)
{
case FLEX_BTN_STAGE_DEFAULT: /* stage: default(button up) */
if (BTN_IS_PRESSED(i)) /* is pressed */
{
target->scan_cnt = 0;
target->click_cnt = 0;
EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_DOWN);
/* swtich to button down stage */
target->status = FLEX_BTN_STAGE_DOWN;
}
else
{
target->event = FLEX_BTN_PRESS_NONE;
}
break;
case FLEX_BTN_STAGE_DOWN: /* stage: button down */
if (BTN_IS_PRESSED(i)) /* is pressed */
{
if (target->click_cnt > 0) /* multiple click */
{
if (target->scan_cnt > target->max_multiple_clicks_interval)
{
EVENT_SET_AND_EXEC_CB(target,
target->click_cnt < FLEX_BTN_PRESS_REPEAT_CLICK ?
target->click_cnt :
FLEX_BTN_PRESS_REPEAT_CLICK);
/* swtich to button down stage */
target->status = FLEX_BTN_STAGE_DOWN;
target->scan_cnt = 0;
target->click_cnt = 0;
}
}
else if (target->scan_cnt >= target->long_hold_start_tick)
{
if (target->event != FLEX_BTN_PRESS_LONG_HOLD)
{
EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_HOLD);
}
}
else if (target->scan_cnt >= target->long_press_start_tick)
{
if (target->event != FLEX_BTN_PRESS_LONG_START)
{
EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_START);
}
}
else if (target->scan_cnt >= target->short_press_start_tick)
{
if (target->event != FLEX_BTN_PRESS_SHORT_START)
{
EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_SHORT_START);
}
}
}
else /* button up */
{
if (target->scan_cnt >= target->long_hold_start_tick)
{
EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_HOLD_UP);
target->status = FLEX_BTN_STAGE_DEFAULT;
}
else if (target->scan_cnt >= target->long_press_start_tick)
{
EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_UP);
target->status = FLEX_BTN_STAGE_DEFAULT;
}
else if (target->scan_cnt >= target->short_press_start_tick)
{
EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_SHORT_UP);
target->status = FLEX_BTN_STAGE_DEFAULT;
}
else
{
/* swtich to multiple click stage */
target->status = FLEX_BTN_STAGE_MULTIPLE_CLICK;
target->click_cnt ++;
}
}
break;
case FLEX_BTN_STAGE_MULTIPLE_CLICK: /* stage: multiple click */
if (BTN_IS_PRESSED(i)) /* is pressed */
{
/* swtich to button down stage */
target->status = FLEX_BTN_STAGE_DOWN;
target->scan_cnt = 0;
}
else
{
if (target->scan_cnt > target->max_multiple_clicks_interval)
{
EVENT_SET_AND_EXEC_CB(target,
target->click_cnt < FLEX_BTN_PRESS_REPEAT_CLICK ?
target->click_cnt :
FLEX_BTN_PRESS_REPEAT_CLICK);
/* swtich to default stage */
target->status = FLEX_BTN_STAGE_DEFAULT;
}
}
break;
}
}
}
/**
* flex_button_event_read
*
* @brief Get the button event of the specified button.
*
* @param button: button structure instance
* @return button event
*/
flex_button_event_t flex_button_event_read(flex_button_t* button)
{
return (flex_button_event_t)(button->event);
}
/**
* flex_button_scan
*
* @brief Start key scan.
* Need to be called cyclically within the specified period.
* Sample cycle: 5 - 20ms
*
* @param void
* @return none
*/
void flex_button_scan(void)
{
flex_button_read();
flex_button_process();
}

View File

@ -0,0 +1,164 @@
/*
* @Author : stark1898y 1658608470@qq.com
* @Date : 2024-07-04 16:13:57
* @LastEditors : stark1898y 1658608470@qq.com
* @LastEditTime : 2024-09-25 14:33:23
* @FilePath : \JT-DT-YD4N02A_RTT_MRS\packages\FlexibleButton-2.0.1\flexible_button.h
* @Description :
*
* Copyright (c) 2024 by yzy, All Rights Reserved.
*/
/**
* @File: flexible_button.h
* @Author: MurphyZhao
* @Date: 2018-09-29
*
* Copyright (c) 2018-2019 MurphyZhao <d2014zjt@163.com>
* https://github.com/murphyzhao
* All rights reserved.
* License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Change logs:
* Date Author Notes
* 2018-09-29 MurphyZhao First add
* 2019-08-02 MurphyZhao Migrate code to github.com/murphyzhao account
* 2019-12-26 MurphyZhao Refactor code and implement multiple clicks
*
*/
#ifndef __FLEXIBLE_BUTTON_H__
#define __FLEXIBLE_BUTTON_H__
#include "stdint.h"
#define FLEX_BTN_SCAN_FREQ_HZ 40 // How often flex_button_scan () is called
#define FLEX_MS_TO_SCAN_CNT(ms) (ms / (1000 / FLEX_BTN_SCAN_FREQ_HZ)) //
/* Multiple clicks interval, default 300ms */
#define MAX_MULTIPLE_CLICKS_INTERVAL (FLEX_MS_TO_SCAN_CNT(300))
typedef void (*flex_button_response_callback)(void*);
typedef enum
{
FLEX_BTN_PRESS_DOWN = 0,
FLEX_BTN_PRESS_CLICK,
FLEX_BTN_PRESS_DOUBLE_CLICK,//
FLEX_BTN_PRESS_REPEAT_CLICK,
FLEX_BTN_PRESS_SHORT_START,
FLEX_BTN_PRESS_SHORT_UP,
FLEX_BTN_PRESS_LONG_START,
FLEX_BTN_PRESS_LONG_UP,
FLEX_BTN_PRESS_LONG_HOLD,// 长按
FLEX_BTN_PRESS_LONG_HOLD_UP,
FLEX_BTN_PRESS_MAX,
FLEX_BTN_PRESS_NONE,
} flex_button_event_t;
/**
* flex_button_t
*
* @brief Button data structure
* Below are members that need to user init before scan.
*
* @member next
* Internal use.
* One-way linked list, pointing to the next button.
*
* @member usr_button_read
* User function is used to read button vaule.
*
* @member cb
* Button event callback function.
*
* @member scan_cnt
* Internal use, user read-only.
* Number of scans, counted when the button is pressed, plus one per scan cycle.
*
* @member click_cnt
* Internal use, user read-only.
* Number of button clicks
*
* @member max_multiple_clicks_interval
* Multiple click interval. Default 'MAX_MULTIPLE_CLICKS_INTERVAL'.
* Need to use FLEX_MS_TO_SCAN_CNT to convert milliseconds into scan cnts.
*
* @member debounce_tick
* Debounce. Not used yet.
* Need to use FLEX_MS_TO_SCAN_CNT to convert milliseconds into scan cnts.
*
* @member short_press_start_tick
* Short press start time. Requires user configuration.
* Need to use FLEX_MS_TO_SCAN_CNT to convert milliseconds into scan cnts.
*
* @member long_press_start_tick
* Long press start time. Requires user configuration.
* Need to use FLEX_MS_TO_SCAN_CNT to convert milliseconds into scan cnts.
*
* @member long_hold_start_tick
* Long hold press start time. Requires user configuration.
*
* @member id
* Button id. Requires user configuration.
* When multiple buttons use the same button callback function,
* they are used to distinguish the buttons.
* Each button id must be unique.
*
* @member pressed_logic_level
* Requires user configuration.
* The logic level of the button pressed, each bit represents a button.
*
* @member event
* Internal use, users can call 'flex_button_event_read' to get current button event.
* Used to record the current button event.
*
* @member status
* Internal use, user unavailable.
* Used to record the current state of buttons.
*
*/
typedef struct flex_button
{
struct flex_button* next;
uint8_t (*usr_button_read)(void *);
flex_button_response_callback cb;
uint16_t scan_cnt;
uint16_t click_cnt;
uint16_t max_multiple_clicks_interval;
uint16_t debounce_tick;
uint16_t short_press_start_tick;
uint16_t long_press_start_tick;
uint16_t long_hold_start_tick;
uint8_t id;
uint8_t pressed_logic_level : 1;
uint8_t event : 4;
uint8_t status : 3;
} flex_button_t;
#ifdef __cplusplus
extern "C" {
#endif
int8_t flex_button_register(flex_button_t *button);
flex_button_event_t flex_button_event_read(flex_button_t* button);
void flex_button_scan(void);
#ifdef __cplusplus
}
#endif
#endif /* __FLEXIBLE_BUTTON_H__ */