220 lines
6.2 KiB
C
220 lines
6.2 KiB
C
/******************************************************************************
|
||
* @file app_mode.c
|
||
* @brief 自动模式 1~6:按节拍改方向与 PWM 占空比
|
||
* @author cyWu <1917507415@qq.com>
|
||
* @date 2026.09.10
|
||
* @version V1.3.0
|
||
* @history
|
||
* - V1.0.0, 2026.08.21, cyWu, 首次发布
|
||
* - V1.1.0, 2026.08.24, cyWu, 接入 H 桥往返节拍
|
||
* - V1.2.0, 2026.08.24, cyWu, 节拍带占空比;六档波形加花样
|
||
* - V1.3.0, 2026.09.10, cyWu, 调整 1~6 玩法顺序
|
||
******************************************************************************/
|
||
|
||
#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(500, DUTY_SOFT), STEP_OUT(1200, DUTY_MID), STEP_OUT(500, DUTY_RUN),
|
||
STEP_STOP(400),
|
||
STEP_IN(500, DUTY_PUNCH), STEP_IN(500, DUTY_MID),
|
||
STEP_STOP(400),
|
||
STEP_END
|
||
};
|
||
|
||
static const AppModeStep_t s_mode2[] = { /* 飞鸟俯冲:快冲出去,2s 慢慢收回 */
|
||
STEP_OUT(500, DUTY_PUNCH), STEP_OUT(500, DUTY_RUN),
|
||
STEP_STOP(400),
|
||
STEP_IN(2800, DUTY_MID),
|
||
STEP_STOP(400),
|
||
STEP_END
|
||
};
|
||
|
||
static const AppModeStep_t s_mode3[] = { /* 闪电快闪:两下轻、两下重,短停防 LVD */
|
||
STEP_OUT(400, DUTY_MID), STEP_IN(400, DUTY_MID),
|
||
STEP_OUT(800, DUTY_RUN), STEP_IN(800, DUTY_RUN),
|
||
STEP_OUT(500, DUTY_PUNCH), STEP_IN(500, DUTY_PUNCH),
|
||
STEP_OUT(700, DUTY_PUNCH), STEP_IN(700, DUTY_PUNCH),
|
||
STEP_STOP(300),
|
||
STEP_END
|
||
};
|
||
|
||
static const AppModeStep_t s_mode4[] = { /* 弹力伪装:慢加速放、慢加速收,最长 2s */
|
||
STEP_OUT(500, DUTY_MID), STEP_OUT(1200, DUTY_RUN), STEP_OUT(500, DUTY_PUNCH),
|
||
STEP_STOP(400),
|
||
STEP_IN(500, DUTY_MID), STEP_IN(1200, DUTY_RUN), STEP_IN(500, DUTY_PUNCH),
|
||
STEP_STOP(400),
|
||
STEP_END
|
||
};
|
||
|
||
static const AppModeStep_t s_mode5[] = { /* 云阶陀螺:短步往返加长,力度一档档加 */
|
||
STEP_OUT(800, DUTY_SOFT), STEP_IN(800, DUTY_SOFT),
|
||
STEP_OUT(800, DUTY_MID), STEP_IN(800, DUTY_MID),
|
||
STEP_OUT(800, DUTY_RUN), STEP_IN(800, DUTY_RUN),
|
||
STEP_STOP(400),
|
||
STEP_END
|
||
};
|
||
|
||
static const AppModeStep_t s_mode6[] = { /* 微风拂动:轻点后歇 2s */
|
||
STEP_OUT(500, DUTY_MID), STEP_STOP(1000),
|
||
STEP_IN(500, DUTY_MID), STEP_STOP(1200),
|
||
STEP_OUT(500, DUTY_RUN), STEP_STOP(800),
|
||
STEP_IN(500, DUTY_RUN), STEP_STOP(1500),
|
||
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; /* 表尾,回到第 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; /* 从节拍表第 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(); /* 换向死区:H 桥松开后再切方向 */
|
||
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();
|
||
}
|
||
}
|