1、低电 3.7V 只闪红灯不再 1 分钟后关机;电量低于约 5% 自动关机,未充电开机先闪 5 下红灯再睡回,只能充电后才能开机
2、自动/手动互相切换都闪 2 下灯;APP 发停止后再发自动模式可正常启动,手动残留停止不再挡住自动 3、自动模式 1~6 单次最长跑 10 分钟后回待机;关机时长按 12 秒进 OTA,OTA 期间红绿交替闪
This commit is contained in:
+81
-22
@@ -2,10 +2,15 @@
|
||||
* @file app_function.c
|
||||
* @brief 设备功能开关 + 自动玩法 + 手动点动 + 按键动作实现
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.25
|
||||
* @version V1.0.0
|
||||
* @date 2026.08.26
|
||||
* @version V1.1.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.25, cyWu, 从 usr_jb_main 拆出
|
||||
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现 DP0 开关、自动玩法、手动点动与按键动作
|
||||
* - V1.1.0, 2026.08.26, cyWu, 切模式闪灯改到 function_play_mode_apply 统一
|
||||
* 触发(自动互切/手动切自动都会闪);手动点动
|
||||
* 启动也补上闪灯;DP2 停转(cmd=3)改边沿触发,
|
||||
* 避免残留的 03 一直把刚启动的自动模式打断;
|
||||
* 自动模式新增单次最长运行时长,到点回待机
|
||||
******************************************************************************/
|
||||
|
||||
#include "app_function.h"
|
||||
@@ -15,6 +20,7 @@
|
||||
#include "app_led.h"
|
||||
#include "app_time_util.h"
|
||||
#include "bsp_hw.h"
|
||||
#include "board_pin.h"
|
||||
#include "jb_product.h"
|
||||
#include "system/includes.h"
|
||||
|
||||
@@ -27,8 +33,9 @@
|
||||
typedef struct {
|
||||
uint8_t app_power; /**< DP0:0=软关,电机/玩法停,BLE 仍在 */
|
||||
uint8_t play_mode; /**< 当前自动模式 0~6 */
|
||||
uint32_t play_ms; /**< 当前自动模式已运行时长,超时回待机 */
|
||||
uint8_t hand_busy; /**< 手动点动进行中 */
|
||||
uint8_t hand_cmd_last; /**< 边沿:同一 1/2 不重复启动 */
|
||||
uint8_t hand_cmd_last; /**< 边沿:同一 1/2/3 不重复触发 */
|
||||
uint16_t hand_ms;
|
||||
volatile uint8_t hand_write_pending; /**< DP2 刚写入(03 停转要靠事件) */
|
||||
volatile uint8_t hand_write_cmd;
|
||||
@@ -39,8 +46,9 @@ 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_play_mode_timeout(uint16_t dt);
|
||||
static void function_hand_poll(void);
|
||||
static uint8_t function_on_click(void);
|
||||
static void function_on_click(void);
|
||||
|
||||
/**
|
||||
* @brief 初始化功能模块状态
|
||||
@@ -68,7 +76,7 @@ void app_function_dp_hand_write(uint8_t cmd)
|
||||
*/
|
||||
void app_function_poll(void)
|
||||
{
|
||||
/* DP0:APP 下发开关 */
|
||||
/* DP0:APP 下发开关。充电中 / 正在关机时不跟,避免和 USB 软关抢状态 */
|
||||
if (!app_power_usb_online() && !app_power_poweroff_pending()) {
|
||||
if (gDevData.power && !s_func.app_power) {
|
||||
s_func.app_power = 1;
|
||||
@@ -79,6 +87,7 @@ void app_function_poll(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* 先跟自动模式,再处理手动点动:同一拍里 APP 发自动模式时,能先把手动停掉 */
|
||||
function_play_mode_poll();
|
||||
function_hand_poll();
|
||||
}
|
||||
@@ -91,10 +100,11 @@ void app_function_poll(void)
|
||||
void app_function_tick_1ms(uint16_t dt)
|
||||
{
|
||||
if (s_func.hand_busy) {
|
||||
bsp_motor_switch_tick();
|
||||
bsp_motor_switch_tick(); /* 手动点动期间也要走换向死区 */
|
||||
s_func.hand_ms = app_add_ms(s_func.hand_ms, dt);
|
||||
} else {
|
||||
app_mode_run();
|
||||
function_play_mode_timeout(dt); /* 自动模式跑满 10 分钟回待机 */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +126,7 @@ static void function_hand_stop(void)
|
||||
void app_function_stop_all(void)
|
||||
{
|
||||
s_func.play_mode = 0;
|
||||
s_func.play_ms = 0;
|
||||
gDevData.play_mode = 0;
|
||||
app_mode_stop();
|
||||
function_hand_stop();
|
||||
@@ -130,6 +141,7 @@ void app_function_soft_off(void)
|
||||
s_func.app_power = 0;
|
||||
gDevData.power = 0;
|
||||
s_func.play_mode = 0;
|
||||
s_func.play_ms = 0;
|
||||
gDevData.play_mode = 0;
|
||||
app_mode_stop();
|
||||
function_hand_stop();
|
||||
@@ -174,30 +186,47 @@ uint8_t app_function_play_mode(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 切换自动模式 0~6
|
||||
* @brief 切换自动模式 0~6,并统一在这里判定要不要闪灯提示
|
||||
* @note 闪灯规则:只要 play_mode 真的变了就闪两下。手动点动期间 play_mode
|
||||
* 恒为 0(见 function_hand_poll),所以“手动切到自动”落到这里时
|
||||
* old_mode 必是 0、新 mode 非 0,天然满足“变了”,不用额外判断;
|
||||
* 由充电/软关/配对等条件被动摁回 0 且本来就是 0 的情况才不闪,
|
||||
* 避免刷屏式误闪
|
||||
* @param mode 0=停转,1~6=对应玩法
|
||||
* @return 无
|
||||
*/
|
||||
static void function_play_mode_apply(uint8_t mode)
|
||||
{
|
||||
uint8_t old_mode = s_func.play_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()) {
|
||||
|| app_pair_is_active()) {
|
||||
mode = 0;
|
||||
}
|
||||
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) {
|
||||
app_mode_stop();
|
||||
} else {
|
||||
app_mode_start(mode);
|
||||
}
|
||||
if (mode != old_mode) {
|
||||
app_led_request_mode_blink(); /* 模式真变了才闪两下 */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 跟随 APP 下发的 DP1 自动模式
|
||||
* @note 手动点动进行中也允许响应,因为选中一个自动模式本身就代表要从
|
||||
* 手动切回自动(function_play_mode_apply 内部会先停手动)
|
||||
* @return 无
|
||||
*/
|
||||
static void function_play_mode_poll(void)
|
||||
@@ -205,7 +234,7 @@ 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()) {
|
||||
|| app_pair_is_active()) {
|
||||
return;
|
||||
}
|
||||
mode = gDevData.play_mode;
|
||||
@@ -214,12 +243,36 @@ static void function_play_mode_poll(void)
|
||||
gDevData.play_mode = 0;
|
||||
}
|
||||
if (mode == s_func.play_mode) {
|
||||
return;
|
||||
return; /* 没变化,不重复 start */
|
||||
}
|
||||
function_play_mode_apply(mode);
|
||||
log_info("APP play_mode=%d\n", mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 自动模式单次最长运行 BOARD_AUTO_MODE_MAX_MS,到点自动回待机
|
||||
* @note 不走 function_play_mode_apply 的闪灯提示:这是保护性超时,不是
|
||||
* 用户主动切模式,闪灯提示反而会让用户误以为是自己按到了什么
|
||||
* @param dt 距上次调用的毫秒数
|
||||
* @return 无
|
||||
*/
|
||||
static void function_play_mode_timeout(uint16_t dt)
|
||||
{
|
||||
if (s_func.play_mode == 0) {
|
||||
return;
|
||||
}
|
||||
s_func.play_ms += dt;
|
||||
if (s_func.play_ms < BOARD_AUTO_MODE_MAX_MS) {
|
||||
return;
|
||||
}
|
||||
log_info("auto mode %d run %ums, timeout to standby\n",
|
||||
s_func.play_mode, (unsigned)s_func.play_ms);
|
||||
s_func.play_mode = 0;
|
||||
s_func.play_ms = 0;
|
||||
gDevData.play_mode = 0;
|
||||
app_mode_stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief DP2 手动:只看第 1 字节。0=空闲,1=顺时针,2=逆时针,3=停
|
||||
* @note 00 00 是 DP 默认值,不当停止。百分比/力度、DP3、DP4 预留不接。
|
||||
@@ -230,6 +283,7 @@ static void function_hand_poll(void)
|
||||
uint8_t cmd;
|
||||
uint8_t got_write = s_func.hand_write_pending;
|
||||
|
||||
/* 优先用 DP 写入钩子带来的边沿;没有新写入才读协议层当前值 */
|
||||
if (got_write) {
|
||||
s_func.hand_write_pending = 0;
|
||||
cmd = s_func.hand_write_cmd;
|
||||
@@ -247,12 +301,17 @@ static void function_hand_poll(void)
|
||||
}
|
||||
|
||||
if (cmd == 0) {
|
||||
s_func.hand_cmd_last = 0;
|
||||
s_func.hand_cmd_last = 0; /* 00 是 DP 默认值,不当停止命令 */
|
||||
return;
|
||||
}
|
||||
|
||||
/* cmd==3 改边沿触发:只在“刚收到停止”这一拍才可能真的停一次,避免
|
||||
* DP2 停留在 03(APP 没有再写别的值)时,后面随便什么原因一进自动/
|
||||
* 手动就被这个陈旧的 03 立刻打断——这正是之前“自动运行中,APP 发
|
||||
* 停止,再发运行自动模式却启动不了”的根因 */
|
||||
if (cmd == 3) {
|
||||
if (got_write || s_func.hand_busy || app_mode_get_active()) {
|
||||
if ((got_write || (cmd != s_func.hand_cmd_last))
|
||||
&& (s_func.hand_busy || app_mode_get_active())) {
|
||||
app_function_stop_all();
|
||||
log_info("hand stop cmd=3\n");
|
||||
}
|
||||
@@ -262,30 +321,31 @@ static void function_hand_poll(void)
|
||||
|
||||
if ((cmd == 1 || cmd == 2) && (got_write || (cmd != s_func.hand_cmd_last))) {
|
||||
s_func.play_mode = 0;
|
||||
s_func.play_ms = 0;
|
||||
gDevData.play_mode = 0;
|
||||
app_mode_stop();
|
||||
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);
|
||||
app_led_request_mode_blink(); /* 自动/空闲切到手动,闪两下提示 */
|
||||
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=手动点动中被忽略
|
||||
* @brief 短按:自动模式 0→1→…→6→0(闪灯提示交给 function_play_mode_apply)
|
||||
* @return 无
|
||||
*/
|
||||
static uint8_t function_on_click(void)
|
||||
static void function_on_click(void)
|
||||
{
|
||||
if (s_func.hand_busy) {
|
||||
log_info("key click ignored, hand busy\n");
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -306,15 +366,14 @@ void usr_on_key_short(void)
|
||||
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();
|
||||
}
|
||||
function_on_click();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user