Files

220 lines
6.3 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/******************************************************************************
* @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~10000STOP 忽略 */
} 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_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_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[] = { /* 云阶陀螺:短步往返加长,力度一档档加 */
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_mode4[] = { /* 起伏攻防: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_mode5[] = { /* 微风拂动:轻点后歇 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 s_mode6[] = { /* 闪电快闪:两下轻、两下重,短停防 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
};
/* 下标 = 协议玩法编号 DP11~6 对应 s_mode1~s_mode6。短按循环顺序在 app_function.c */
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();
}
}