feat: 完成弹力魔盒初版固件
This commit is contained in:
+180
-62
@@ -18,10 +18,12 @@
|
||||
#include "app_pair.h"
|
||||
#include "app_mode.h"
|
||||
#include "app_led.h"
|
||||
#include "app_shake_wake.h"
|
||||
#include "app_time_util.h"
|
||||
#include "bsp_hw.h"
|
||||
#include "board_pin.h"
|
||||
#include "jb_product.h"
|
||||
#include "usr_le_api.h"
|
||||
#include "system/includes.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
@@ -32,13 +34,19 @@
|
||||
/** 功能模块总状态,替代原来一堆散落的 static 变量 */
|
||||
typedef struct {
|
||||
uint8_t app_power; /**< DP0:0=软关,电机/玩法停,BLE 仍在 */
|
||||
uint8_t play_mode; /**< 当前自动模式 0~6 */
|
||||
uint8_t play_mode; /**< 当前自动模式 0~5 */
|
||||
uint32_t play_ms; /**< 当前自动模式已运行时长,超时回待机 */
|
||||
uint8_t hand_busy; /**< 手动点动进行中 */
|
||||
uint8_t hand_cmd_last; /**< 边沿:同一 1/2/3 不重复触发 */
|
||||
uint8_t hand_cmd_last; /**< 最近一次手动命令 */
|
||||
uint16_t hand_ms;
|
||||
volatile uint8_t hand_write_pending; /**< DP2 刚写入(03 停转要靠事件) */
|
||||
volatile uint8_t hand_write_cmd;
|
||||
volatile uint8_t power_write_pending;
|
||||
volatile uint8_t power_write_value;
|
||||
volatile uint8_t play_write_pending;
|
||||
volatile uint8_t play_write_mode;
|
||||
uint8_t shake_wake;
|
||||
uint8_t no_disturb;
|
||||
} AppFunctionCtx_t;
|
||||
|
||||
static AppFunctionCtx_t s_func;
|
||||
@@ -49,6 +57,9 @@ static void function_play_mode_poll(void);
|
||||
static void function_play_mode_timeout(uint16_t dt);
|
||||
static void function_hand_poll(void);
|
||||
static void function_on_click(void);
|
||||
static uint8_t function_power_on(uint8_t show_hint);
|
||||
static uint8_t function_shake_wake_allowed(void);
|
||||
static void function_shake_wake_trigger(void);
|
||||
|
||||
/**
|
||||
* @brief 初始化功能模块状态
|
||||
@@ -57,11 +68,28 @@ static void function_on_click(void);
|
||||
void app_function_init(void)
|
||||
{
|
||||
memset(&s_func, 0, sizeof(s_func));
|
||||
app_shake_wake_init(function_shake_wake_allowed,
|
||||
function_shake_wake_trigger);
|
||||
s_func.shake_wake = !!gDevData.shake_wake;
|
||||
s_func.no_disturb = !!gDevData.no_disturb_enable[0];
|
||||
app_shake_wake_set_enable(s_func.shake_wake);
|
||||
}
|
||||
|
||||
void app_function_dp_power_write(uint8_t enable)
|
||||
{
|
||||
s_func.power_write_value = !!enable;
|
||||
s_func.power_write_pending = 1;
|
||||
}
|
||||
|
||||
void app_function_dp_play_write(uint8_t mode)
|
||||
{
|
||||
s_func.play_write_mode = mode;
|
||||
s_func.play_write_pending = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief DP2 写入钩子:仅记录第 1 字节,真正执行在 app_function_poll
|
||||
* @param cmd 0=空闲,1=顺时针,2=逆时针,3=停
|
||||
* @param cmd 0=忽略,1=原地右转,2=原地左转
|
||||
* @return 无
|
||||
*/
|
||||
void app_function_dp_hand_write(uint8_t cmd)
|
||||
@@ -70,24 +98,48 @@ void app_function_dp_hand_write(uint8_t cmd)
|
||||
s_func.hand_write_pending = 1;
|
||||
}
|
||||
|
||||
void app_function_set_shake_wake(uint8_t enable)
|
||||
{
|
||||
s_func.shake_wake = !!enable;
|
||||
gDevData.shake_wake = s_func.shake_wake;
|
||||
app_shake_wake_set_enable(s_func.shake_wake);
|
||||
}
|
||||
|
||||
void app_function_set_no_disturb(uint8_t enable)
|
||||
{
|
||||
s_func.no_disturb = !!enable;
|
||||
app_shake_wake_reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 跟随 APP 下发的 DP0/DP1,并处理 DP2 手动点动
|
||||
* @return 无
|
||||
*/
|
||||
void app_function_poll(void)
|
||||
{
|
||||
/* DP0:APP 下发开关。充电中 / 正在关机时不跟,避免和 USB 软关抢状态 */
|
||||
if (!app_power_usb_online() && !app_power_poweroff_pending()) {
|
||||
if (gDevData.power && !s_func.app_power) {
|
||||
s_func.app_power = 1;
|
||||
app_led_hint_power_on();
|
||||
log_info("APP power on\n");
|
||||
} else if (!gDevData.power && s_func.app_power) {
|
||||
if (s_func.shake_wake != !!gDevData.shake_wake) {
|
||||
app_function_set_shake_wake(gDevData.shake_wake);
|
||||
}
|
||||
if (s_func.no_disturb != !!gDevData.no_disturb_enable[0]) {
|
||||
app_function_set_no_disturb(gDevData.no_disturb_enable[0]);
|
||||
}
|
||||
|
||||
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)) {
|
||||
log_info("APP power on\n");
|
||||
} else {
|
||||
gDevData.power = s_func.app_power;
|
||||
}
|
||||
} else if (!enable && s_func.app_power) {
|
||||
app_function_soft_off();
|
||||
} else {
|
||||
gDevData.power = s_func.app_power;
|
||||
}
|
||||
}
|
||||
|
||||
/* 先跟自动模式,再处理手动点动:同一拍里 APP 发自动模式时,能先把手动停掉 */
|
||||
function_play_mode_poll();
|
||||
function_hand_poll();
|
||||
}
|
||||
@@ -99,9 +151,18 @@ void app_function_poll(void)
|
||||
*/
|
||||
void app_function_tick_1ms(uint16_t dt)
|
||||
{
|
||||
/* 与业务节拍共用一次唤醒,避免震动检测另开 10ms sys_timer。 */
|
||||
app_shake_wake_poll();
|
||||
|
||||
if (s_func.hand_busy) {
|
||||
bsp_motor_switch_tick(); /* 手动点动期间也要走换向死区 */
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
app_mode_run();
|
||||
function_play_mode_timeout(dt); /* 自动模式跑满 10 分钟回待机 */
|
||||
@@ -116,7 +177,8 @@ static void function_hand_stop(void)
|
||||
{
|
||||
s_func.hand_busy = 0;
|
||||
s_func.hand_ms = 0;
|
||||
bsp_motor_set(BSP_MOTOR_STOP);
|
||||
gDevData.hand_mode = HAND_MODE_0;
|
||||
bsp_drive_stop();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,6 +190,7 @@ void app_function_stop_all(void)
|
||||
s_func.play_mode = 0;
|
||||
s_func.play_ms = 0;
|
||||
gDevData.play_mode = 0;
|
||||
gDevData.hand_mode = HAND_MODE_0;
|
||||
app_mode_stop();
|
||||
function_hand_stop();
|
||||
}
|
||||
@@ -145,6 +208,10 @@ void app_function_soft_off(void)
|
||||
gDevData.play_mode = 0;
|
||||
app_mode_stop();
|
||||
function_hand_stop();
|
||||
/* 不依赖系统下一次轻睡回调,软关生效后立刻关四路 MCPWM。 */
|
||||
bsp_hw_sleep_pwm_off();
|
||||
app_shake_wake_reset();
|
||||
usr_ble_set_standby(1);
|
||||
log_info("soft off, wait APP power on\n");
|
||||
}
|
||||
|
||||
@@ -154,8 +221,7 @@ void app_function_soft_off(void)
|
||||
*/
|
||||
void app_function_power_on_silent(void)
|
||||
{
|
||||
s_func.app_power = 1;
|
||||
gDevData.power = 1;
|
||||
(void)function_power_on(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,7 +244,7 @@ uint8_t app_function_is_hand_busy(void)
|
||||
|
||||
/**
|
||||
* @brief 查询当前自动玩法编号
|
||||
* @return 0=停转,1~6=对应玩法
|
||||
* @return 0=停转,1~5=对应玩法
|
||||
*/
|
||||
uint8_t app_function_play_mode(void)
|
||||
{
|
||||
@@ -186,25 +252,25 @@ uint8_t app_function_play_mode(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 切换自动模式 0~6,并统一在这里判定要不要闪灯提示
|
||||
* @brief 切换自动模式 0~5,并统一在这里判定要不要闪灯提示
|
||||
* @note 闪灯规则:只要 play_mode 真的变了就闪两下。手动点动期间 play_mode
|
||||
* 恒为 0(见 function_hand_poll),所以“手动切到自动”落到这里时
|
||||
* old_mode 必是 0、新 mode 非 0,天然满足“变了”,不用额外判断;
|
||||
* 由充电/软关/配对等条件被动摁回 0 且本来就是 0 的情况才不闪,
|
||||
* 避免刷屏式误闪
|
||||
* @param mode 0=停转,1~6=对应玩法
|
||||
* @param mode 0=停转,1~5=对应玩法
|
||||
* @return 无
|
||||
*/
|
||||
static void function_play_mode_apply(uint8_t mode)
|
||||
{
|
||||
uint8_t old_mode = s_func.play_mode;
|
||||
|
||||
if (mode > 6) {
|
||||
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_pair_is_active()) {
|
||||
|| app_led_is_ota()) {
|
||||
mode = 0;
|
||||
}
|
||||
if (mode != 0 && s_func.hand_busy) {
|
||||
@@ -233,17 +299,21 @@ static void function_play_mode_poll(void)
|
||||
{
|
||||
uint8_t mode;
|
||||
|
||||
if (app_power_usb_online() || !s_func.app_power || app_power_poweroff_pending()
|
||||
|| app_pair_is_active()) {
|
||||
if (!s_func.play_write_pending) {
|
||||
return;
|
||||
}
|
||||
mode = gDevData.play_mode;
|
||||
if (mode > 6) {
|
||||
mode = 0;
|
||||
gDevData.play_mode = 0;
|
||||
s_func.play_write_pending = 0;
|
||||
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;
|
||||
}
|
||||
if (mode == s_func.play_mode) {
|
||||
return; /* 没变化,不重复 start */
|
||||
mode = s_func.play_write_mode;
|
||||
if (mode > PLAY_MODE_5) {
|
||||
mode = 0;
|
||||
}
|
||||
if (mode != PLAY_MODE_0 && mode == s_func.play_mode) {
|
||||
mode = PLAY_MODE_0;
|
||||
}
|
||||
function_play_mode_apply(mode);
|
||||
log_info("APP play_mode=%d\n", mode);
|
||||
@@ -267,15 +337,11 @@ static void function_play_mode_timeout(uint16_t dt)
|
||||
}
|
||||
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();
|
||||
app_function_soft_off();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief DP2 手动:只看第 1 字节。0=空闲,1=顺时针,2=逆时针,3=停
|
||||
* @note 00 00 是 DP 默认值,不当停止。百分比/力度、DP3、DP4 预留不接。
|
||||
* @return 无
|
||||
*/
|
||||
static void function_hand_poll(void)
|
||||
@@ -288,54 +354,63 @@ static void function_hand_poll(void)
|
||||
s_func.hand_write_pending = 0;
|
||||
cmd = s_func.hand_write_cmd;
|
||||
} else {
|
||||
cmd = gDevData.hand_mode[0];
|
||||
cmd = gDevData.hand_mode;
|
||||
}
|
||||
|
||||
if (app_power_usb_online() || !s_func.app_power || app_power_poweroff_pending()
|
||||
|| app_pair_is_active()) {
|
||||
if (s_func.hand_busy || (got_write && (cmd == 3))) {
|
||||
|| app_led_is_ota()) {
|
||||
if (s_func.hand_busy) {
|
||||
app_function_stop_all();
|
||||
}
|
||||
s_func.hand_cmd_last = cmd;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd == 0) {
|
||||
s_func.hand_cmd_last = 0; /* 00 是 DP 默认值,不当停止命令 */
|
||||
if (!got_write) {
|
||||
s_func.hand_cmd_last = cmd;
|
||||
return;
|
||||
}
|
||||
|
||||
/* cmd==3 改边沿触发:只在“刚收到停止”这一拍才可能真的停一次,避免
|
||||
* DP2 停留在 03(APP 没有再写别的值)时,后面随便什么原因一进自动/
|
||||
* 手动就被这个陈旧的 03 立刻打断——这正是之前“自动运行中,APP 发
|
||||
* 停止,再发运行自动模式却启动不了”的根因 */
|
||||
if (cmd == 3) {
|
||||
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");
|
||||
if (cmd == HAND_MODE_0) {
|
||||
if (s_func.hand_busy) {
|
||||
function_hand_stop();
|
||||
app_led_request_mode_blink();
|
||||
}
|
||||
s_func.hand_cmd_last = 3;
|
||||
s_func.hand_cmd_last = cmd;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((cmd == 1 || cmd == 2) && (got_write || (cmd != s_func.hand_cmd_last))) {
|
||||
if (cmd == HAND_MODE_1 || cmd == HAND_MODE_2) {
|
||||
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;
|
||||
/* 1=顺时针收绳,2=逆时针放绳(BOARD_MOTOR_IN_INA_HIGH=1) */
|
||||
bsp_motor_set((cmd == 1) ? BSP_MOTOR_IN : BSP_MOTOR_OUT);
|
||||
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(); /* 自动/空闲切到手动,闪两下提示 */
|
||||
log_info("hand cmd=%d %s until 03\n", cmd, (cmd == 1) ? "cw" : "ccw");
|
||||
log_info("hand cmd=%d %s max=%ums\n", cmd,
|
||||
(cmd == HAND_MODE_1) ? "upper" : "lower",
|
||||
(unsigned)BOARD_HAND_PULSE_MS);
|
||||
} else {
|
||||
gDevData.hand_mode = HAND_MODE_0;
|
||||
if (s_func.hand_busy) {
|
||||
function_hand_stop();
|
||||
}
|
||||
log_info("ignore unsupported hand cmd=%d\n", cmd);
|
||||
}
|
||||
s_func.hand_cmd_last = cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 短按:自动模式 0→1→…→6→0(闪灯提示交给 function_play_mode_apply)
|
||||
* @brief 短按:自动模式 0→1→…→5→0(闪灯提示交给 function_play_mode_apply)
|
||||
* @return 无
|
||||
*/
|
||||
static void function_on_click(void)
|
||||
@@ -344,7 +419,8 @@ static void function_on_click(void)
|
||||
log_info("key click ignored, hand busy\n");
|
||||
return;
|
||||
}
|
||||
function_play_mode_apply((uint8_t)((s_func.play_mode + 1) % 7));
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -361,16 +437,15 @@ void usr_on_key_short(void)
|
||||
log_info("key click ignored, charging\n");
|
||||
return;
|
||||
}
|
||||
if (app_pair_is_active()) {
|
||||
log_info("key click ignored, pairing\n");
|
||||
if (app_led_is_ota()) {
|
||||
log_info("key click ignored, ota\n");
|
||||
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");
|
||||
if (function_power_on(1)) {
|
||||
log_info("key click cancel soft off\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
function_on_click();
|
||||
@@ -389,10 +464,53 @@ void usr_on_key_long(void)
|
||||
log_info("key hold 3s ignored, charging\n");
|
||||
return;
|
||||
}
|
||||
if (app_pair_is_active()) {
|
||||
log_info("key hold 3s ignored, pairing\n");
|
||||
if (app_led_is_ota()) {
|
||||
log_info("key hold 3s, exit ota and poweroff\n");
|
||||
usr_app_ota_exit();
|
||||
app_led_exit_ota();
|
||||
app_power_request_poweroff();
|
||||
return;
|
||||
}
|
||||
log_info("key hold 3s, poweroff\n");
|
||||
app_power_request_poweroff();
|
||||
}
|
||||
|
||||
/** Shared standby-to-on transition for APP, key and vibration wake. */
|
||||
static uint8_t function_power_on(uint8_t show_hint)
|
||||
{
|
||||
if (app_power_usb_online() || app_power_poweroff_pending() ||
|
||||
app_led_is_ota()) {
|
||||
return 0;
|
||||
}
|
||||
if (s_func.app_power) {
|
||||
gDevData.power = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
app_function_stop_all();
|
||||
s_func.app_power = 1;
|
||||
gDevData.power = 1;
|
||||
app_shake_wake_reset();
|
||||
usr_ble_set_standby(0);
|
||||
if (show_hint) {
|
||||
app_led_hint_power_on();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint8_t function_shake_wake_allowed(void)
|
||||
{
|
||||
return s_func.shake_wake &&
|
||||
!s_func.no_disturb &&
|
||||
!s_func.app_power &&
|
||||
!app_power_usb_online() &&
|
||||
!app_power_poweroff_pending() &&
|
||||
!app_led_is_ota();
|
||||
}
|
||||
|
||||
static void function_shake_wake_trigger(void)
|
||||
{
|
||||
if (function_power_on(1)) {
|
||||
log_info("shake wake power on\n");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user