From 86af0d2373d049de789fa2f842296cb12895a4dc Mon Sep 17 00:00:00 2001 From: skiinder Date: Wed, 11 Mar 2026 10:44:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E6=A8=A1=E5=9D=97=E6=94=AF=E6=8C=81=E5=A4=9A?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE=E9=94=AE=E7=9B=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加了完整的模式切换功能,通过ADC采样检测模式拨码开关, 实现USB、BLE和2.4G三种工作模式的自动识别和切换。 - 新增mode_event事件用于传递模式状态 - 实现mode_switch_module模块,包含ADC初始化、 模式识别算法和状态管理逻辑 - 配置CMakeLists.txt添加新源文件和头文件目录 - 更新设备树配置启用ADC和IO通道 - 添加Kconfig选项CONFIG_ADC=y - 实现防抖机制和稳定的模式检测逻辑 - 集成到CAF事件系统,支持电源管理状态切换 --- CMakeLists.txt | 3 + app.overlay | 14 +- prj.conf | 2 + src/events/mode_event.c | 34 +++++ src/events/mode_event.h | 23 +++ src/modules/mode_switch_module.c | 237 +++++++++++++++++++++++++++++++ 6 files changed, 311 insertions(+), 2 deletions(-) create mode 100644 src/events/mode_event.c create mode 100644 src/events/mode_event.h create mode 100644 src/modules/mode_switch_module.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c2db34..81e8c06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,8 +4,11 @@ find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(new_kbd) zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc) +zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/events) target_sources(app PRIVATE src/main.c + src/events/mode_event.c src/modules/button_map_module.c + src/modules/mode_switch_module.c ) diff --git a/app.overlay b/app.overlay index 010064b..21872e9 100644 --- a/app.overlay +++ b/app.overlay @@ -1,4 +1,9 @@ -/{}; +/ { + zephyr,user { + io-channels = <&adc 5>, <&adc 7>; + }; +}; + &gpio0 { status = "okay"; }; @@ -13,4 +18,9 @@ &led_0 { status = "okay"; -}; \ No newline at end of file +}; + +/* 使能 SAADC,mode_switch_module 使用 channel 7 采样模式拨码电压。 */ +&adc { + status = "okay"; +}; diff --git a/prj.conf b/prj.conf index d52a2d3..eef80bc 100644 --- a/prj.conf +++ b/prj.conf @@ -17,3 +17,5 @@ CONFIG_CAF_BUTTONS=y CONFIG_CAF_BUTTONS_DEF_PATH="buttons_def.h" CONFIG_CAF_BUTTONS_SCAN_INTERVAL=5 CONFIG_CAF_BUTTONS_DEBOUNCE_INTERVAL=10 + +CONFIG_ADC=y diff --git a/src/events/mode_event.c b/src/events/mode_event.c new file mode 100644 index 0000000..f983f8e --- /dev/null +++ b/src/events/mode_event.c @@ -0,0 +1,34 @@ +#include "mode_event.h" + +static const char *const mode_name[] = { + [MODE_TYPE_USB] = "USB", + [MODE_TYPE_BLE] = "BLE", + [MODE_TYPE_2G4] = "2.4G", +}; + +static void log_mode_event(const struct app_event_header *aeh) +{ + const struct mode_event *event = cast_mode_event(aeh); + + __ASSERT_NO_MSG(event->mode_type < MODE_TYPE_COUNT); + + APP_EVENT_MANAGER_LOG(aeh, "mode=%s(%u)", mode_name[event->mode_type], event->mode_type); +} + +static void profile_mode_event(struct log_event_buf *buf, + const struct app_event_header *aeh) +{ + const struct mode_event *event = cast_mode_event(aeh); + + nrf_profiler_log_encode_uint8(buf, (uint8_t)event->mode_type); +} + +APP_EVENT_INFO_DEFINE(mode_event, + ENCODE(NRF_PROFILER_ARG_U8), + ENCODE("mode"), + profile_mode_event); + +APP_EVENT_TYPE_DEFINE(mode_event, + log_mode_event, + &mode_event_info, + APP_EVENT_FLAGS_CREATE(APP_EVENT_TYPE_FLAGS_INIT_LOG_ENABLE)); diff --git a/src/events/mode_event.h b/src/events/mode_event.h new file mode 100644 index 0000000..6fa80de --- /dev/null +++ b/src/events/mode_event.h @@ -0,0 +1,23 @@ +#ifndef MODE_EVENT_H +#define MODE_EVENT_H + +#include +#include + +typedef enum +{ + MODE_TYPE_USB, + MODE_TYPE_BLE, + MODE_TYPE_2G4, + MODE_TYPE_COUNT, +} mode_type_t; + +struct mode_event +{ + struct app_event_header header; + mode_type_t mode_type; +}; + +APP_EVENT_TYPE_DECLARE(mode_event); + +#endif diff --git a/src/modules/mode_switch_module.c b/src/modules/mode_switch_module.c new file mode 100644 index 0000000..182b910 --- /dev/null +++ b/src/modules/mode_switch_module.c @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MODULE mode_switch +#include + +#include "mode_event.h" + +#include +LOG_MODULE_REGISTER(MODULE); + +#define MODE_USER_NODE DT_PATH(zephyr_user) +#define MODE_ADC_IO_CH_IDX 0 +#define MODE_SAMPLE_INTERVAL_MS 50 + +static const struct adc_dt_spec mode_adc = + ADC_DT_SPEC_GET_BY_IDX(MODE_USER_NODE, MODE_ADC_IO_CH_IDX); + +static struct k_work_delayable mode_sample_work; +static int16_t adc_sample_buffer; + +static atomic_t active; +static mode_type_t current_mode; +static uint8_t mode_stable_status; + +static int init_adc(void) +{ + if (!adc_is_ready_dt(&mode_adc)) + { + LOG_ERR("ADC device not ready"); + return -ENODEV; + } + + int err = adc_channel_setup_dt(&mode_adc); + if (err) + { + LOG_ERR("ADC channel setup failed (err=%d)", err); + return err; + } + + return 0; +} + +static mode_type_t classify_mode_from_mv(int32_t mv) +{ + /* + * 使用“距离最近中心点”的分类方式,避免阈值边界附近抖动时出现模式跳变。 + * 三个中心点直接对应硬件设计目标电压:USB=0mV、2.4G=1650mV、BLE=3300mV。 + */ + static const int32_t centers[MODE_TYPE_COUNT] = { + [MODE_TYPE_USB] = 0, + [MODE_TYPE_BLE] = 3300, + [MODE_TYPE_2G4] = 1650, + }; + + int32_t best_diff = INT32_MAX; + mode_type_t best_mode = MODE_TYPE_USB; + + for (size_t i = 0; i < MODE_TYPE_COUNT; i++) + { + int32_t diff = abs(mv - centers[i]); + + if (diff < best_diff) + { + best_diff = diff; + best_mode = (mode_type_t)i; + } + } + + return best_mode; +} + +static int read_mode(mode_type_t *mode) +{ + struct adc_sequence sequence = {0}; + int err = adc_sequence_init_dt(&mode_adc, &sequence); + if (err) + { + return err; + } + + sequence.buffer = &adc_sample_buffer; + sequence.buffer_size = sizeof(adc_sample_buffer); + + err = adc_read_dt(&mode_adc, &sequence); + if (err) + { + return err; + } + + int32_t v = adc_sample_buffer; + err = adc_raw_to_millivolts_dt(&mode_adc, &v); + if (err) + { + return err; + } + + *mode = classify_mode_from_mv(v); + + return 0; +} + +static void publish_mode_event(mode_type_t mode) +{ + if (current_mode == mode) + return; + + current_mode = mode; + struct mode_event *event = new_mode_event(); + + event->mode_type = mode; + APP_EVENT_SUBMIT(event); + /* + * 模式切换是明确的人机交互动作。这里同步上报 keep_alive_event, + * 让 power manager 重置休眠倒计时,避免用户刚切换模式就进入省电流程。 + */ + keep_alive(); +} + +static void mode_sample_fn(struct k_work *work) +{ + ARG_UNUSED(work); + + if (!atomic_get(&active)) + { + return; + } + + mode_type_t sampled_mode; + int err = read_mode(&sampled_mode); + if (err) + { + LOG_ERR("ADC read failed (err=%d)", err); + module_set_state(MODULE_STATE_ERROR); + return; + } + + mode_stable_status = mode_stable_status << 2; + mode_stable_status |= (sampled_mode & 0x3); + + switch (mode_stable_status) + { + case 0b00000000: + publish_mode_event(MODE_TYPE_USB); + break; + case 0b01010101: + publish_mode_event(MODE_TYPE_BLE); + break; + case 0b10101010: + publish_mode_event(MODE_TYPE_2G4); + break; + default: + break; + } + + k_work_reschedule(&mode_sample_work, K_MSEC(MODE_SAMPLE_INTERVAL_MS)); +} + +static void mode_switch_suspend(void) +{ + atomic_set(&active, false); + (void)k_work_cancel_delayable(&mode_sample_work); + module_set_state(MODULE_STATE_STANDBY); +} + +static void mode_switch_resume(void) +{ + if (atomic_get(&active)) + return; + + atomic_set(&active, true); + k_work_reschedule(&mode_sample_work, K_NO_WAIT); + module_set_state(MODULE_STATE_READY); +} + +static void init_mode_switch(void) +{ + if (atomic_get(&active)) + return; + + if (init_adc()) + { + module_set_state(MODULE_STATE_ERROR); + return; + } + + mode_stable_status = 0xFF; + current_mode = MODE_TYPE_COUNT; + atomic_set(&active, false); + k_work_init_delayable(&mode_sample_work, mode_sample_fn); + + mode_switch_resume(); +} + +static bool app_event_handler(const struct app_event_header *aeh) +{ + if (is_module_state_event(aeh)) + { + const struct module_state_event *event = cast_module_state_event(aeh); + + if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) + { + init_mode_switch(); + } + + return false; + } + + if (is_power_down_event(aeh)) + { + mode_switch_suspend(); + return false; + } + + if (is_wake_up_event(aeh)) + { + mode_switch_resume(); + return false; + } + + __ASSERT_NO_MSG(false); + return false; +} + +APP_EVENT_LISTENER(MODULE, app_event_handler); +APP_EVENT_SUBSCRIBE(MODULE, module_state_event); +APP_EVENT_SUBSCRIBE_EARLY(MODULE, power_down_event); +APP_EVENT_SUBSCRIBE(MODULE, wake_up_event);