/****************************************************************************** * @file app_mode.c * @brief 自动模式 1~6:按节拍改方向与 PWM 占空比 * @author cyWu <1917507415@qq.com> * @date 2026.08.24 * @version V1.2.0 * @history * - V1.0.0, 2026.08.21, cyWu, 首次发布 * - V1.1.0, 2026.08.24, cyWu, 接入 H 桥往返节拍 * - V1.2.0, 2026.08.24, cyWu, 节拍带占空比;六档波形加花样 ******************************************************************************/ #include "system/includes.h" #include "app_mode.h" #include "bsp_hw.h" #define LOG_TAG_CONST APP #define LOG_TAG "[APP_MODE]" #define LOG_INFO_ENABLE #include "debug.h" typedef struct { BspMotorDir_t dir; uint16_t ms; uint16_t duty; /**< 0~10000,STOP 忽略 */ } AppModeStep_t; #define DUTY_SOFT 3500 /* 轻 */ #define DUTY_MID 5500 /* 中 */ #define DUTY_RUN 8000 /* 主速度 80% */ #define DUTY_PUNCH 9500 /* 猛一下 */ #define STEP_OUT(ms, duty) { BSP_MOTOR_OUT, (ms), (duty) } #define STEP_IN(ms, duty) { BSP_MOTOR_IN, (ms), (duty) } #define STEP_STOP(ms) { BSP_MOTOR_STOP, (ms), 0 } #define STEP_END { BSP_MOTOR_STOP, 0, 0 } /* 节拍以 ms=0 结尾,循环播放。同向连续步进只改占空比,换向才刹车。 */ static const AppModeStep_t s_mode1[] = { /* 弹力呼吸:慢加速放、慢加速收,最长 2s */ STEP_OUT(600, DUTY_MID), STEP_OUT(2000, DUTY_RUN), STEP_OUT(500, DUTY_PUNCH), STEP_STOP(400), STEP_IN(600, DUTY_MID), STEP_IN(2000, DUTY_RUN), STEP_IN(500, DUTY_PUNCH), STEP_STOP(400), STEP_END }; static const AppModeStep_t s_mode2[] = { /* 飞鸟俯冲:快冲出去,2s 慢慢收回 */ STEP_OUT(150, DUTY_PUNCH), STEP_OUT(250, DUTY_RUN), STEP_STOP(150), STEP_IN(2000, DUTY_SOFT), STEP_IN(800, DUTY_MID), STEP_STOP(400), STEP_END }; static const AppModeStep_t s_mode3[] = { /* 云阶陀螺:短步往返加长,力度一档档加 */ STEP_OUT(220, DUTY_SOFT), STEP_IN(220, DUTY_SOFT), STEP_OUT(220, DUTY_MID), STEP_IN(220, DUTY_MID), STEP_OUT(220, DUTY_RUN), STEP_IN(220, DUTY_RUN), STEP_STOP(400), STEP_END }; static const AppModeStep_t s_mode4[] = { /* 起伏攻防:2s 慢逗再猛收 */ STEP_OUT(500, DUTY_SOFT), STEP_OUT(2000, DUTY_MID), STEP_OUT(400, DUTY_RUN), STEP_STOP(250), STEP_IN(180, DUTY_PUNCH), STEP_IN(400, DUTY_MID), STEP_STOP(500), STEP_END }; static const AppModeStep_t s_mode5[] = { /* 微风拂动:轻点后歇 2s */ STEP_OUT(200, DUTY_MID), STEP_STOP(2000), STEP_IN(200, DUTY_MID), STEP_STOP(2000), STEP_OUT(400, DUTY_RUN), STEP_STOP(800), STEP_IN(400, DUTY_RUN), STEP_STOP(1500), STEP_END }; static const AppModeStep_t s_mode6[] = { /* 闪电快闪:两下轻、两下重,短停防 LVD */ STEP_OUT(200, DUTY_MID), STEP_IN(200, DUTY_MID), STEP_OUT(400, DUTY_RUN), STEP_IN(400, DUTY_RUN), STEP_OUT(100, DUTY_PUNCH), STEP_IN(100, DUTY_PUNCH), STEP_OUT(100, DUTY_PUNCH), STEP_IN(100, DUTY_PUNCH), STEP_STOP(300), STEP_END }; static const AppModeStep_t *const s_tables[7] = { 0, s_mode1, s_mode2, s_mode3, s_mode4, s_mode5, s_mode6 }; /** 模块状态,替代原来散落的 4 个 static */ typedef struct { uint8_t initialized; uint8_t active; /**< 0=停转,1~6=当前玩法 */ uint8_t step; /**< 当前节拍在表里的下标 */ u32 step_t0; /**< 当前节拍起始时间戳,用于判断是否该跳下一步 */ } AppModeCtx_t; static AppModeCtx_t s_mode; extern u32 timer_get_ms(void); /** * @brief 输出当前节拍的方向与占空比;ms=0 表示表尾,回到第 0 步循环 * @return 无 */ static void app_mode_apply_step(void) { const AppModeStep_t *tbl = s_tables[s_mode.active]; if (!tbl) { bsp_motor_set(BSP_MOTOR_STOP); return; } if (tbl[s_mode.step].ms == 0) { s_mode.step = 0; } s_mode.step_t0 = timer_get_ms(); if (tbl[s_mode.step].dir == BSP_MOTOR_STOP) { bsp_motor_set(BSP_MOTOR_STOP); } else { bsp_motor_set_pwm(tbl[s_mode.step].dir, tbl[s_mode.step].duty); } } /** * @brief 玩法初始化,电机停 * @return APP_MODE_OK=成功 */ AppMode_Ret_t app_mode_init(void) { s_mode.active = 0; s_mode.step = 0; s_mode.step_t0 = timer_get_ms(); s_mode.initialized = 1; bsp_motor_set(BSP_MOTOR_STOP); return APP_MODE_OK; } /** * @brief 查询模块是否已初始化 * @return APP_MODE_OK=已初始化,APP_MODE_ERR_NOT_INIT=未初始化 */ AppMode_Ret_t app_mode_get_status(void) { return s_mode.initialized ? APP_MODE_OK : APP_MODE_ERR_NOT_INIT; } /** * @brief 读取当前自动模式 * @return 0=停转,1~6=对应玩法 */ uint8_t app_mode_get_active(void) { return s_mode.active; } /** * @brief 启动自动模式 1~6,按节拍表循环 * @param mode 玩法编号,范围 1~6 * @return APP_MODE_OK=成功,APP_MODE_ERR_NOT_INIT=未初始化,APP_MODE_ERR_PARAM=参数非法 */ AppMode_Ret_t app_mode_start(uint8_t mode) { if (!s_mode.initialized) { return APP_MODE_ERR_NOT_INIT; } if (mode < 1 || mode > 6) { app_mode_stop(); return APP_MODE_ERR_PARAM; } s_mode.active = mode; s_mode.step = 0; app_mode_apply_step(); log_info("auto mode %d start\n", mode); return APP_MODE_OK; } /** * @brief 停止当前玩法并刹车 * @return 无 */ void app_mode_stop(void) { if (s_mode.active) { log_info("auto mode %d stop\n", s_mode.active); } s_mode.active = 0; s_mode.step = 0; s_mode.step_t0 = timer_get_ms(); bsp_motor_set(BSP_MOTOR_STOP); } /** * @brief 推进当前节拍(按 timer_get_ms 真实时间) * @return 无 */ void app_mode_run(void) { const AppModeStep_t *tbl; bsp_motor_switch_tick(); if (!s_mode.initialized || s_mode.active == 0) { return; } tbl = s_tables[s_mode.active]; if (!tbl) { return; } if ((timer_get_ms() - s_mode.step_t0) >= tbl[s_mode.step].ms) { s_mode.step++; app_mode_apply_step(); } }