/****************************************************************************** * @file app_function.c * @brief 设备功能开关 + 自动玩法 + 手动点动 + 按键动作实现 * @author cyWu <1917507415@qq.com> * @date 2026.08.25 * @version V1.0.0 * @history * - V1.0.0, 2026.08.25, cyWu, 从 usr_jb_main 拆出 ******************************************************************************/ #include "app_function.h" #include "app_power.h" #include "app_pair.h" #include "app_mode.h" #include "app_led.h" #include "app_time_util.h" #include "bsp_hw.h" #include "jb_product.h" #include "system/includes.h" #define LOG_TAG_CONST APP #define LOG_TAG "[APP_FUNC]" #define LOG_INFO_ENABLE #include "debug.h" /** 功能模块总状态,替代原来一堆散落的 static 变量 */ typedef struct { uint8_t app_power; /**< DP0:0=软关,电机/玩法停,BLE 仍在 */ uint8_t play_mode; /**< 当前自动模式 0~6 */ uint8_t hand_busy; /**< 手动点动进行中 */ uint8_t hand_cmd_last; /**< 边沿:同一 1/2 不重复启动 */ uint16_t hand_ms; volatile uint8_t hand_write_pending; /**< DP2 刚写入(03 停转要靠事件) */ volatile uint8_t hand_write_cmd; } AppFunctionCtx_t; static AppFunctionCtx_t s_func; static void function_hand_stop(void); static void function_play_mode_apply(uint8_t mode); static void function_play_mode_poll(void); static void function_hand_poll(void); static uint8_t function_on_click(void); /** * @brief 初始化功能模块状态 * @return 无 */ void app_function_init(void) { memset(&s_func, 0, sizeof(s_func)); } /** * @brief DP2 写入钩子:仅记录第 1 字节,真正执行在 app_function_poll * @param cmd 0=空闲,1=顺时针,2=逆时针,3=停 * @return 无 */ void app_function_dp_hand_write(uint8_t cmd) { s_func.hand_write_cmd = cmd; s_func.hand_write_pending = 1; } /** * @brief 跟随 APP 下发的 DP0/DP1,并处理 DP2 手动点动 * @return 无 */ void app_function_poll(void) { /* DP0:APP 下发开关 */ if (!app_power_usb_online() && !app_power_poweroff_pending()) { if (gDevData.power && !s_func.app_power) { s_func.app_power = 1; app_led_hint_power_on(); log_info("APP power on\n"); } else if (!gDevData.power && s_func.app_power) { app_function_soft_off(); } } function_play_mode_poll(); function_hand_poll(); } /** * @brief 推进当前玩法节拍或手动刹车 * @param dt 距上次调用的毫秒数 * @return 无 */ void app_function_tick_1ms(uint16_t dt) { if (s_func.hand_busy) { bsp_motor_brake_tick(); s_func.hand_ms = app_add_ms(s_func.hand_ms, dt); } else { app_mode_run(); } } /** * @brief 停手动点动(走电机反转刹车) * @return 无 */ static void function_hand_stop(void) { s_func.hand_busy = 0; s_func.hand_ms = 0; bsp_motor_set(BSP_MOTOR_STOP); } /** * @brief 停手动 + 自动模式,并清 DP1 * @return 无 */ void app_function_stop_all(void) { s_func.play_mode = 0; gDevData.play_mode = 0; app_mode_stop(); function_hand_stop(); } /** * @brief 软关:电机停、DP0=0,等 APP/短按再开。不断 BLE * @return 无 */ void app_function_soft_off(void) { s_func.app_power = 0; gDevData.power = 0; s_func.play_mode = 0; gDevData.play_mode = 0; app_mode_stop(); function_hand_stop(); log_info("soft off, wait APP power on\n"); } /** * @brief 上电默认开启功能(DP0=1),不带开机灯效提示 * @return 无 */ void app_function_power_on_silent(void) { s_func.app_power = 1; gDevData.power = 1; } /** * @brief 查询功能开关状态(DP0) * @return 1=开,0=关 */ uint8_t app_function_is_power_on(void) { return s_func.app_power; } /** * @brief 查询手动点动是否进行中 * @return 1=进行中,0=空闲 */ uint8_t app_function_is_hand_busy(void) { return s_func.hand_busy; } /** * @brief 查询当前自动玩法编号 * @return 0=停转,1~6=对应玩法 */ uint8_t app_function_play_mode(void) { return s_func.play_mode; } /** * @brief 切换自动模式 0~6 * @param mode 0=停转,1~6=对应玩法 * @return 无 */ static void function_play_mode_apply(uint8_t mode) { if (mode > 6) { mode = 0; } if (app_power_usb_online() || !s_func.app_power || app_power_poweroff_pending() || s_func.hand_busy || app_pair_is_active()) { mode = 0; } s_func.play_mode = mode; gDevData.play_mode = mode; if (mode == 0) { app_mode_stop(); } else { app_mode_start(mode); } } /** * @brief 跟随 APP 下发的 DP1 自动模式 * @return 无 */ static void function_play_mode_poll(void) { uint8_t mode; if (app_power_usb_online() || !s_func.app_power || app_power_poweroff_pending() || s_func.hand_busy || app_pair_is_active()) { return; } mode = gDevData.play_mode; if (mode > 6) { mode = 0; gDevData.play_mode = 0; } if (mode == s_func.play_mode) { return; } function_play_mode_apply(mode); log_info("APP play_mode=%d\n", mode); } /** * @brief DP2 手动:只看第 1 字节。0=空闲,1=顺时针,2=逆时针,3=停 * @note 00 00 是 DP 默认值,不当停止。百分比/力度、DP3、DP4 预留不接。 * @return 无 */ static void function_hand_poll(void) { uint8_t cmd; uint8_t got_write = s_func.hand_write_pending; if (got_write) { s_func.hand_write_pending = 0; cmd = s_func.hand_write_cmd; } else { cmd = gDevData.hand_mode[0]; } if (app_power_usb_online() || !s_func.app_power || app_power_poweroff_pending() || app_pair_is_active()) { if (s_func.hand_busy || (got_write && (cmd == 3))) { app_function_stop_all(); } s_func.hand_cmd_last = cmd; return; } if (cmd == 0) { s_func.hand_cmd_last = 0; return; } if (cmd == 3) { if (got_write || s_func.hand_busy || app_mode_get_active()) { app_function_stop_all(); log_info("hand stop cmd=3\n"); } s_func.hand_cmd_last = 3; return; } if ((cmd == 1 || cmd == 2) && (got_write || (cmd != s_func.hand_cmd_last))) { s_func.play_mode = 0; gDevData.play_mode = 0; app_mode_stop(); s_func.hand_busy = 1; s_func.hand_ms = 0; /* 1=顺时针收绳,2=逆时针放绳(BOARD_MOTOR_IN_INA_HIGH=1) */ bsp_motor_set((cmd == 1) ? BSP_MOTOR_IN : BSP_MOTOR_OUT); log_info("hand cmd=%d %s until 03\n", cmd, (cmd == 1) ? "cw" : "ccw"); } s_func.hand_cmd_last = cmd; } /** * @brief 短按:自动模式 0→1→…→6→0 * @return 1=已切换,0=手动点动中被忽略 */ static uint8_t function_on_click(void) { if (s_func.hand_busy) { log_info("key click ignored, hand busy\n"); return 0; } function_play_mode_apply((uint8_t)((s_func.play_mode + 1) % 7)); log_info("key click play_mode=%d\n", s_func.play_mode); return 1; } /** * @brief 短按回调:充电/配对忽略;软关时唤醒;否则切模式 * @return 无 */ void usr_on_key_short(void) { if (app_power_poweroff_pending()) { return; } if (app_power_usb_online()) { log_info("key click ignored, charging\n"); return; } if (app_pair_is_active()) { log_info("key click ignored, pairing\n"); return; } if (!s_func.app_power) { s_func.app_power = 1; gDevData.power = 1; app_led_hint_power_on(); log_info("key click cancel soft off\n"); return; } if (function_on_click()) { app_led_request_mode_blink(); } } /** * @brief 长按 3s:运行中深睡(开机 3s/5s 不走这里) * @return 无 */ void usr_on_key_long(void) { if (app_power_poweroff_pending()) { return; } if (app_power_usb_online()) { log_info("key hold 3s ignored, charging\n"); return; } if (app_pair_is_active()) { log_info("key hold 3s ignored, pairing\n"); return; } log_info("key hold 3s, poweroff\n"); app_power_request_poweroff(); }