diff --git a/apps/usr_app/app_function.c b/apps/usr_app/app_function.c index 74b4daf..ca76600 100644 --- a/apps/usr_app/app_function.c +++ b/apps/usr_app/app_function.c @@ -30,18 +30,23 @@ #include "system/includes.h" #define LOG_TAG_CONST APP -#define LOG_TAG "[APP_FUNC]" +#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~5 */ - uint32_t play_ms; /**< 当前自动模式已运行时长,超时回待机 */ - uint8_t hand_busy; /**< 手动点动进行中 */ - uint8_t hand_cmd_last; /**< 最近一次手动命令 */ +typedef struct +{ + uint8_t app_power; /**< DP0:0=软关,电机/玩法停,BLE 仍在 */ + uint8_t play_mode; /**< 当前自动模式 0~5 */ + uint32_t play_ms; /**< 当前自动模式已运行时长,超时回待机 */ + uint8_t hand_busy; /**< 手动点动进行中 */ + uint8_t hand_cmd_last; /**< 最近一次手动命令 */ uint16_t hand_ms; + uint8_t hand_queue[BOARD_HAND_QUEUE_LEN]; /**< DP2 连点排队:待执行命令 FIFO */ + uint8_t hand_queue_head; /**< 队首下标 */ + uint8_t hand_queue_count; /**< 待执行条数 */ + volatile uint8_t hand_write_pending; /**< DP2 刚写入(03 停转要靠事件) */ volatile uint8_t hand_write_cmd; volatile uint8_t power_write_pending; @@ -52,6 +57,10 @@ typedef struct { } AppFunctionCtx_t; static AppFunctionCtx_t s_func; +static void function_hand_apply(uint8_t cmd); +static uint16_t function_hand_action_ms(void); +static void function_hand_queue_push(uint8_t cmd); +static uint8_t function_hand_queue_pop(uint8_t *cmd); static void function_hand_stop(void); static void function_play_mode_apply(uint8_t mode); @@ -90,7 +99,7 @@ void app_function_dp_play_write(uint8_t mode) /** * @brief DP2 写入钩子:仅记录第 1 字节,真正执行在 app_function_poll - * @param cmd 0=停止,1=上部电机探出,2=下部电机探出 + * @param cmd 0=停止,1=上部电机探出,2=下电机顺时针转动,3=下电机逆时针转动 * @return 无 */ void app_function_dp_hand_write(uint8_t cmd) @@ -112,21 +121,31 @@ void app_function_set_shake_wake(uint8_t enable) */ void app_function_poll(void) { - if (s_func.shake_wake != !!gDevData.shake_wake) { + if (s_func.shake_wake != !!gDevData.shake_wake) + { app_function_set_shake_wake(gDevData.shake_wake); } - if (s_func.power_write_pending) { + if (s_func.power_write_pending) + { uint8_t enable = s_func.power_write_value; s_func.power_write_pending = 0; - if (enable) { - if (function_power_on(1)) { + if (enable) + { + if (function_power_on(1)) + { log_info("APP power on\n"); - } else { + } + else + { gDevData.power = s_func.app_power; } - } else if (!enable && s_func.app_power) { + } + else if (!enable && s_func.app_power) + { app_function_soft_off(); - } else { + } + else + { gDevData.power = s_func.app_power; } } @@ -145,19 +164,29 @@ void app_function_tick_1ms(uint16_t dt) /* 与业务节拍共用一次唤醒,避免震动检测另开 10ms sys_timer。 */ app_shake_wake_poll(); - if (s_func.hand_busy) { + if (s_func.hand_busy) { + uint8_t next_cmd; + bsp_motor_switch_tick(); /* 手动点动期间也要走换向死区 */ + if (gDevData.hand_mode == HAND_MODE_1) { + app_mode_run(); /* cmd=1 的上电机波形靠自动引擎推进 */ + } s_func.hand_ms = app_add_ms(s_func.hand_ms, dt); - if (s_func.hand_ms >= BOARD_HAND_PULSE_MS) { - function_hand_stop(); - gDevData.hand_mode = HAND_MODE_0; - app_led_request_mode_blink(); - log_info("manual timeout %ums\n", (unsigned)BOARD_HAND_PULSE_MS); + if (s_func.hand_ms >= function_hand_action_ms()) { + if (function_hand_queue_pop(&next_cmd)) { + s_func.hand_ms = 0; /* 接着补做排队的,不打断 */ + function_hand_apply(next_cmd); + log_info("manual next %d\n", next_cmd); + } else { + function_hand_stop(); + app_led_request_mode_blink(); + log_info("manual done\n"); + } } } else { app_mode_run(); function_play_mode_timeout(dt); /* 自动模式跑满 10 分钟回待机 */ - } + } } /** @@ -168,10 +197,88 @@ static void function_hand_stop(void) { s_func.hand_busy = 0; s_func.hand_ms = 0; + s_func.hand_queue_head = 0; + s_func.hand_queue_count = 0; gDevData.hand_mode = HAND_MODE_0; + app_mode_stop(); bsp_drive_stop(); } +/** + * @brief 把一条 DP2 命令压入排队 FIFO + * @param cmd HAND_MODE_1/2/3 + * @return 无 + */ +static void function_hand_queue_push(uint8_t cmd) +{ + uint8_t idx; + + if (s_func.hand_queue_count >= BOARD_HAND_QUEUE_LEN) { + log_info("hand queue full, drop %d\n", cmd); + return; + } + idx = (uint8_t)((s_func.hand_queue_head + s_func.hand_queue_count) + % BOARD_HAND_QUEUE_LEN); + s_func.hand_queue[idx] = cmd; + s_func.hand_queue_count++; +} + +/** + * @brief 取出队首命令 + * @param cmd 出参 + * @return 1=取到,0=队列空 + */ +static uint8_t function_hand_queue_pop(uint8_t *cmd) +{ + if (s_func.hand_queue_count == 0) { + return 0; + } + *cmd = s_func.hand_queue[s_func.hand_queue_head]; + s_func.hand_queue_head = (uint8_t)((s_func.hand_queue_head + 1) + % BOARD_HAND_QUEUE_LEN); + s_func.hand_queue_count--; + return 1; +} + +/** + * @brief 下发一轮手动动作(驱动层唯一出口) + * @param cmd HAND_MODE_1/2/3 + * @return 无 + */ +static void function_hand_apply(uint8_t cmd) +{ + app_mode_stop(); /* 清掉上一轮残留,含 cmd=1 借用的自动引擎 */ + gDevData.hand_mode = cmd; + switch (cmd) { + case HAND_MODE_1: + app_mode_start(APP_MODE_UPPER_ONLY); + break; + case HAND_MODE_2: + bsp_drive_set(BSP_MOTOR_STOP, 0, BSP_MOTOR_FORWARD, 8000); + break; + case HAND_MODE_3: + bsp_drive_set(BSP_MOTOR_STOP, 0, BSP_MOTOR_BACKWARD, 8000); + break; + default: + bsp_drive_stop(); + break; + } +} + +/** + * @brief 当前手动动作的单轮时长 + * @return 毫秒 + */ +static uint16_t function_hand_action_ms(void) +{ + if (gDevData.hand_mode == HAND_MODE_2 || gDevData.hand_mode == HAND_MODE_3) { + return BOARD_HAND_STEP_MS; /* 下电机:一轮 = 半圈 */ + } + return BOARD_HAND_PULSE_MS; /* 上电机波形:保留 5s 上限 */ +} + + + /** * @brief 停手动 + 自动模式,并清 DP1 * @return 无 @@ -260,36 +367,43 @@ static void function_play_mode_apply(uint8_t mode) old_mode, mode, gDevData.beep_trig, s_func.app_power, app_power_usb_online(), app_power_poweroff_pending(), app_led_is_ota()); - if (mode > PLAY_MODE_5) { + if (mode > PLAY_MODE_5) + { mode = 0; } /* Pairing is an online overlay; only charging, OTA and power-off stop motion. */ - if (app_power_usb_online() || !s_func.app_power || app_power_poweroff_pending() - || app_led_is_ota()) { + if (app_power_usb_online() || !s_func.app_power || app_power_poweroff_pending() || app_led_is_ota()) + { mode = 0; } - if (mode != 0 && s_func.hand_busy) { + if (mode != 0 && s_func.hand_busy) + { function_hand_stop(); /* 手动切到自动:先停手动点动,再进自动模式 */ } s_func.play_mode = mode; s_func.play_ms = 0; /* 每次切模式都重新计 10 分钟 */ gDevData.play_mode = mode; - if (mode == 0) { + if (mode == 0) + { app_mode_stop(); - } else { + } + else + { app_mode_start(mode); } // MAGIC_AUDIO_LOG("模式切换结果:旧=%u,实际=%u;当前测试不拦截DP4", old_mode, mode); - if (mode != old_mode) { - app_led_request_mode_blink(); /* 模式真变了才闪两下 */ + if (mode != old_mode) + { + app_led_request_mode_blink(); /* 模式真变了才闪两下 */ - /* DP4:0=关闭模式切换鸟叫,1=允许播放 */ - //&& gDevData.beep_trig - if ((mode != PLAY_MODE_0) && gDevData.beep_trig) { + /* DP4:0=关闭模式切换鸟叫,1=允许播放 */ + //&& gDevData.beep_trig + if ((mode != PLAY_MODE_0) && gDevData.beep_trig) + { Car_Audio_Test_Play(); + } } } -} /** * @brief 跟随 APP 下发的 DP1 自动模式 @@ -301,20 +415,23 @@ static void function_play_mode_poll(void) { uint8_t mode; - if (!s_func.play_write_pending) { + if (!s_func.play_write_pending) + { return; } s_func.play_write_pending = 0; - if (app_power_usb_online() || !s_func.app_power || app_power_poweroff_pending() - || app_led_is_ota()) { + if (app_power_usb_online() || !s_func.app_power || app_power_poweroff_pending() || app_led_is_ota()) + { gDevData.play_mode = s_func.play_mode; return; } mode = s_func.play_write_mode; - if (mode > PLAY_MODE_5) { + if (mode > PLAY_MODE_5) + { mode = 0; } - if (mode != PLAY_MODE_0 && mode == s_func.play_mode) { + if (mode != PLAY_MODE_0 && mode == s_func.play_mode) + { mode = PLAY_MODE_0; } function_play_mode_apply(mode); @@ -330,11 +447,13 @@ static void function_play_mode_poll(void) */ static void function_play_mode_timeout(uint16_t dt) { - if (s_func.play_mode == 0) { + if (s_func.play_mode == 0) + { return; } s_func.play_ms += dt; - if (s_func.play_ms < BOARD_AUTO_MODE_MAX_MS) { + if (s_func.play_ms < BOARD_AUTO_MODE_MAX_MS) + { return; } log_info("auto mode %d run %ums, timeout to standby\n", @@ -343,7 +462,7 @@ static void function_play_mode_timeout(uint16_t dt) } /** - * @brief DP2 手动:只看第 1 字节。0=空闲,1=上部探出,2=下部探出 + * @brief DP2 手动:只看第 1 字节。0=上下都停,1=仅上电机往复,2=仅下电机顺时针,3=仅下电机逆时针 * @return 无 */ static void function_hand_poll(void) @@ -352,29 +471,36 @@ static void function_hand_poll(void) uint8_t got_write = s_func.hand_write_pending; /* 优先用 DP 写入钩子带来的边沿;没有新写入才读协议层当前值 */ - if (got_write) { + if (got_write) + { s_func.hand_write_pending = 0; cmd = s_func.hand_write_cmd; - } else { + } + else + { cmd = gDevData.hand_mode; } - if (app_power_usb_online() || !s_func.app_power || app_power_poweroff_pending() - || app_led_is_ota()) { - if (s_func.hand_busy) { + if (app_power_usb_online() || !s_func.app_power || app_power_poweroff_pending() || app_led_is_ota()) + { + if (s_func.hand_busy) + { app_function_stop_all(); } s_func.hand_cmd_last = cmd; return; } - if (!got_write) { + if (!got_write) + { s_func.hand_cmd_last = cmd; return; } - if (cmd == HAND_MODE_0) { - if (s_func.hand_busy) { + if (cmd == HAND_MODE_0) + { + if (s_func.hand_busy) + { function_hand_stop(); app_led_request_mode_blink(); } @@ -382,33 +508,32 @@ static void function_hand_poll(void) return; } - if (cmd == HAND_MODE_1 || cmd == HAND_MODE_2) { + if (cmd >= HAND_MODE_1 && cmd <= HAND_MODE_3) { + if (s_func.hand_busy) { + /* 正在动作中:不打断、不清零计时,只把新指令排到队尾 */ + function_hand_queue_push(cmd); + s_func.hand_cmd_last = cmd; + return; + } s_func.play_mode = 0; s_func.play_ms = 0; gDevData.play_mode = 0; - app_mode_stop(); s_func.hand_busy = 1; s_func.hand_ms = 0; - gDevData.hand_mode = cmd; - if (cmd == HAND_MODE_1) { - bsp_drive_set(BSP_MOTOR_FORWARD, 5500, - BSP_MOTOR_STOP, 0); - } else { - bsp_drive_set(BSP_MOTOR_STOP, 0, - BSP_MOTOR_FORWARD, 5500); - } - app_led_request_mode_blink(); + function_hand_apply(cmd); + + app_led_request_mode_blink(); if (gDevData.beep_trig) { - Car_Audio_Test_Play(); + Car_Audio_Test_Play(); } } else { gDevData.hand_mode = HAND_MODE_0; if (s_func.hand_busy) { function_hand_stop(); } - } s_func.hand_cmd_last = cmd; + } /** @@ -417,12 +542,12 @@ static void function_hand_poll(void) */ static void function_on_click(void) { - if (s_func.hand_busy) { + if (s_func.hand_busy) + { log_info("key click ignored, hand busy\n"); return; } - function_play_mode_apply((s_func.play_mode >= PLAY_MODE_5) ? - PLAY_MODE_1 : (uint8_t)(s_func.play_mode + 1)); + function_play_mode_apply((s_func.play_mode >= PLAY_MODE_5) ? PLAY_MODE_1 : (uint8_t)(s_func.play_mode + 1)); log_info("key click play_mode=%d\n", s_func.play_mode); } @@ -432,20 +557,25 @@ static void function_on_click(void) */ void usr_on_key_short(void) { - if (app_power_poweroff_pending()) { + if (app_power_poweroff_pending()) + { return; } - if (app_power_usb_online()) { + if (app_power_usb_online()) + { log_info("key click ignored, charging\n"); return; } - if (app_led_is_ota()) { + if (app_led_is_ota()) + { log_info("key click ignored, ota\n"); return; } - if (!s_func.app_power) { + if (!s_func.app_power) + { /* 软关状态下短按:只唤醒功能,不切模式 */ - if (function_power_on(1)) { + if (function_power_on(1)) + { log_info("key click cancel soft off\n"); } return; @@ -459,14 +589,17 @@ void usr_on_key_short(void) */ void usr_on_key_long(void) { - if (app_power_poweroff_pending()) { + if (app_power_poweroff_pending()) + { return; } - if (app_power_usb_online()) { + if (app_power_usb_online()) + { log_info("key hold 3s ignored, charging\n"); return; } - if (app_led_is_ota()) { + if (app_led_is_ota()) + { log_info("key hold 3s, exit ota and poweroff\n"); usr_app_ota_exit(); app_led_exit_ota(); @@ -481,10 +614,12 @@ void usr_on_key_long(void) static uint8_t function_power_on(uint8_t show_hint) { if (app_power_usb_online() || app_power_poweroff_pending() || - app_led_is_ota()) { + app_led_is_ota()) + { return 0; } - if (s_func.app_power) { + if (s_func.app_power) + { gDevData.power = 1; return 1; } @@ -494,7 +629,8 @@ static uint8_t function_power_on(uint8_t show_hint) gDevData.power = 1; app_shake_wake_reset(); usr_ble_set_standby(0); - if (show_hint) { + if (show_hint) + { app_led_hint_power_on(); } return 1; @@ -512,7 +648,8 @@ static uint8_t function_shake_wake_allowed(void) static void function_shake_wake_trigger(void) { - if (function_power_on(1)) { + if (function_power_on(1)) + { log_info("shake wake power on\n"); } } diff --git a/apps/usr_app/app_mode.c b/apps/usr_app/app_mode.c index 1d81698..830d2e6 100644 --- a/apps/usr_app/app_mode.c +++ b/apps/usr_app/app_mode.c @@ -28,6 +28,7 @@ typedef struct { uint8_t initialized; uint8_t active; uint8_t output_valid; + uint8_t upper_only; /* 1=只输出上电机,下电机恒停 */ uint8_t upper_phase; uint8_t lower_pattern; uint8_t lower_segment; @@ -270,8 +271,10 @@ static void app_mode5_process(uint32_t now_ms) app_mode5_select_lower_pattern(now_ms); } } - app_mode_outputs_set(s_mode.upper_direction, s_mode.upper_speed, - s_mode.lower_direction, MAGIC_BOX_HIGH_PERCENT); + app_mode_outputs_set(s_mode.upper_direction, s_mode.upper_speed, + s_mode.upper_only ? BSP_MOTOR_STOP : s_mode.lower_direction, + s_mode.upper_only ? 0 : MAGIC_BOX_HIGH_PERCENT); + } AppMode_Ret_t app_mode_init(void) @@ -297,12 +300,13 @@ AppMode_Ret_t app_mode_start(uint8_t mode) if (!s_mode.initialized) { return APP_MODE_ERR_NOT_INIT; } - if (mode < 1 || mode > 5) { + if (mode < 1 || mode > APP_MODE_UPPER_ONLY) { /* 放宽到 6 */ app_mode_stop(); return APP_MODE_ERR_PARAM; } - s_mode.active = mode; + s_mode.upper_only = (mode == APP_MODE_UPPER_ONLY); + s_mode.active = s_mode.upper_only ? 5 : mode; /* 内部仍按模式 5 跑 */ app_mode_start_current(timer_get_ms()); log_info("magic box mode %d start\n", mode); return APP_MODE_OK; @@ -311,6 +315,7 @@ AppMode_Ret_t app_mode_start(uint8_t mode) void app_mode_stop(void) { s_mode.active = 0; + s_mode.upper_only = 0; s_mode.output_valid = 0; bsp_drive_stop(); } diff --git a/apps/usr_app/app_mode.h b/apps/usr_app/app_mode.h index 1fa136c..c09acc6 100644 --- a/apps/usr_app/app_mode.h +++ b/apps/usr_app/app_mode.h @@ -12,9 +12,14 @@ #ifndef __APP_MODE_H__ #define __APP_MODE_H__ +#define APP_MODE_UPPER_ONLY 6 #include +/** 特殊玩法编号:复用模式 5 的上电机波形,但下电机恒停(供 DP2=1 使用) */ + + + typedef enum { APP_MODE_OK = 0, APP_MODE_ERR_PARAM, diff --git a/apps/usr_jb_proto/Protocol/jb_product.c b/apps/usr_jb_proto/Protocol/jb_product.c index f60853a..78a3d2b 100644 --- a/apps/usr_jb_proto/Protocol/jb_product.c +++ b/apps/usr_jb_proto/Protocol/jb_product.c @@ -63,7 +63,7 @@ void userInit(void) gDevData.play_mode = 0; /* PLAY_MODE_0 (停止复位) */ gDevData.hand_mode = 0; /* HAND_MODE_0 (忽略) */ gDevData.alm_low_battery = 0; /* DP3 低电量报警 0=关 1=开 */ - gDevData.beep_trig = 0; /* DP4 声音开关 0=关 1=开 */ + gDevData.beep_trig = 1; /* DP4 声音开关 0=关 1=开 */ gDevData.battery = 0; /* DP5 电量上报 */ gDevData.shake_wake = 0; /* DP6 震动触发开机 0=关 1=开 */ memset(gDevData.no_disturb_enable, 0, APP_NO_DISTURB_CFG_LEN); /* DP7 勿扰模式开关 */ diff --git a/apps/usr_jb_proto/Protocol/jb_protocol.h b/apps/usr_jb_proto/Protocol/jb_protocol.h index 41e5077..e93684a 100644 --- a/apps/usr_jb_proto/Protocol/jb_protocol.h +++ b/apps/usr_jb_proto/Protocol/jb_protocol.h @@ -159,11 +159,13 @@ typedef enum /* 手动模式 */ typedef enum { - HAND_MODE_0 = 0, /* 停止 */ - HAND_MODE_1 = 1, /* 上部电机探出 */ - HAND_MODE_2 = 2, /* 下部电机探出 */ + HAND_MODE_0 = 0, /* 上下电机都停 */ + HAND_MODE_1 = 1, /* 仅上电机:按玩法5波形往复 */ + HAND_MODE_2 = 2, /* 仅下电机:顺时针 */ + HAND_MODE_3 = 3, /* 仅下电机:逆时针 */ } HAND_MODE_t; + /* 充电状态 */ typedef enum { diff --git a/apps/usr_periph/board_pin.h b/apps/usr_periph/board_pin.h index 23ca9e9..13878e7 100644 --- a/apps/usr_periph/board_pin.h +++ b/apps/usr_periph/board_pin.h @@ -107,6 +107,8 @@ #define BOARD_MOTOR_REVERSE_MS 1 /**< 换向前先松开,防 H 桥直通 */ #define BOARD_HAND_PULSE_MS 5000 /**< 手动右转/左转单次最长 5s */ +#define BOARD_HAND_STEP_MS 1500 /**< DP2 下电机单轮动作时长(=半圈),按实测标定 */ +#define BOARD_HAND_QUEUE_LEN 8 #define BOARD_MOTOR_PWM_HZ 200 /**< 弹力魔盒验证过的 MCPWM 频率(Hz) */ #define BOARD_MOTOR_PWM_DUTY 8000 /**< 占空比 0~10000=0%~100%,80% */ #define BOARD_MOTOR_PWM_IDLE_CLOSE 1 /**< 1=空闲/轻睡关 MCPWM;0=保持开着,方便对比待机电流 */