diff --git a/.gitignore b/.gitignore
index 9955a91..64c6183 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,7 +29,8 @@ cpu/*/tools/sdk.map
cpu/*/tools/rom.lst
cpu/*/tools/app.bin
cpu/*/tools/data_code.bin
-cpu/*/tools/p11_code.bin
+# P11 固件是下载工具输入,不是本工程生成物,必须随工程保留。
+!cpu/*/tools/p11_code.bin
cpu/*/tools/*.bc
# 打包下载目录中的固件产物(保留脚本 .bat / 密钥 .key)
@@ -94,3 +95,9 @@ Release_Project_*/
# - cpu/br28/tools/*.exe 等 下载/打包工具
# - include_lib/ / apps/ 头文件与应用源码
# -----------------------------------------------------------------------------
+
+# Programming key: upload only after company approval
+*.key
+*.pem
+*.pfx
+/objs/
\ No newline at end of file
diff --git a/AC701N.cbp b/AC701N.cbp
index 7e8bf4f..284cf08 100644
--- a/AC701N.cbp
+++ b/AC701N.cbp
@@ -281,7 +281,8 @@
-
+
+
@@ -832,6 +833,10 @@
+
+
+
+
diff --git a/Makefile b/Makefile
index e685d24..7d1c07c 100644
--- a/Makefile
+++ b/Makefile
@@ -411,6 +411,7 @@ c_SRC_FILES := \
apps/usr_app/app_pair.c \
apps/usr_app/app_boot.c \
apps/usr_app/app_function.c \
+ apps/usr_app/app_shake_wake.c \
apps/usr_le_code/ble_proto.c \
apps/usr_le_code/bleproto.c \
apps/usr_le_code/bleproto_packer.c \
diff --git a/apps/common/third_party_profile/jieli/JL_rcsp/bt_trans_data/le_rcsp_adv_module.c b/apps/common/third_party_profile/jieli/JL_rcsp/bt_trans_data/le_rcsp_adv_module.c
index 897a5b6..d64a668 100644
--- a/apps/common/third_party_profile/jieli/JL_rcsp/bt_trans_data/le_rcsp_adv_module.c
+++ b/apps/common/third_party_profile/jieli/JL_rcsp/bt_trans_data/le_rcsp_adv_module.c
@@ -50,6 +50,7 @@
#include "classic/tws_api.h"
#include "rcsp_adv_user_update.h"
#include "bt_tws.h"
+#include "usr_le_api.h"
#if (TCFG_BLE_DEMO_SELECT == DEF_BLE_DEMO_ADV_RCSP)
@@ -106,6 +107,14 @@ static const struct conn_update_param_t connection_param_table[] = {
/* {12, 28, 4, 600},//3.7 */
/* {12, 24, 30, 600},//3.05 */
};
+/* DP0=0 keeps BLE connected, but can trade a little command latency for much
+ * fewer idle RF events. The preferred setting has a worst-case idle response
+ * interval of about 640 ms; later entries are gateway compatibility fallbacks. */
+static const struct conn_update_param_t connection_param_table_standby[] = {
+ {24, 32, 15, 600},
+ {20, 32, 15, 600},
+ {16, 24, 10, 600},
+};
static const struct conn_update_param_t connection_param_table_ota[] = {
{16, 24, 0, 600},
{12, 28, 0, 600},//11
@@ -129,6 +138,8 @@ static u8 scan_rsp_data[ADV_RSP_PACKET_MAX];//max is 31
/* #define scan_rsp_data &att_ram_buffer[32] */
static u32 ble_timer_handle = 0;
+static u16 connection_update_timer;
+static uint8_t connection_standby_mode;
static char *gap_device_name = "br22_ble_test";
static u8 gap_device_name_len = 0;
volatile static u8 ble_work_state = 0;
@@ -165,7 +176,10 @@ static int get_buffer_vaild_len(void *priv);
//------------------------------------------------------
static void send_request_connect_parameter(u8 table_index)
{
- struct conn_update_param_t *param = (void *)&connection_param_table[table_index];//static ram
+ const struct conn_update_param_t *table = connection_standby_mode ?
+ connection_param_table_standby :
+ connection_param_table;
+ struct conn_update_param_t *param = (void *)&table[table_index];//static ram
log_info("update_request:-%d-%d-%d-%d-\n", param->interval_min, param->interval_max, param->latency, param->timeout);
if (con_handle) {
@@ -182,6 +196,35 @@ static void check_connetion_updata_deal(void)
}
}
+static void connection_param_update_apply(void *priv)
+{
+ (void)priv;
+ connection_update_timer = 0;
+ connection_update_cnt = 0;
+ check_connetion_updata_deal();
+}
+
+void usr_ble_set_standby(uint8_t standby)
+{
+ standby = !!standby;
+ if (connection_standby_mode == standby) {
+ return;
+ }
+
+ connection_standby_mode = standby;
+ connection_update_cnt = 0;
+ if (!con_handle) {
+ return;
+ }
+ if (connection_update_timer) {
+ sys_timeout_del(connection_update_timer);
+ connection_update_timer = 0;
+ }
+ /* DP writes are consumed from a usr_timer callback. Defer the controller
+ * command to system-timer context. */
+ connection_update_timer = sys_timeout_add(NULL, connection_param_update_apply, 100);
+}
+
static void send_request_connect_parameter_ota(u8 table_index)
{
diff --git a/apps/earphone/board/br28/board_jl701n_demo.c b/apps/earphone/board/br28/board_jl701n_demo.c
index ffc2626..885cfd4 100644
--- a/apps/earphone/board/br28/board_jl701n_demo.c
+++ b/apps/earphone/board/br28/board_jl701n_demo.c
@@ -734,6 +734,17 @@ struct port_wakeup port0 = {
.iomap = TCFG_IOKEY_POWER_ONE_PORT, //唤醒口选择
};
+#if BOARD_CHG_ENABLE
+/* R18 is not fitted on CBC3, so VBUS never reaches VPWR in softoff. Wake on
+ * the fitted ME4054 CHG_ON signal instead; it falls when charging starts. */
+struct port_wakeup board_chg_wakeup_port = {
+ .pullup_down_enable = ENABLE,
+ .edge = FALLING_EDGE,
+ .filter = PORT_FLT_8ms,
+ .iomap = BOARD_CHG_PIN,
+};
+#endif
+
#if (TCFG_TEST_BOX_ENABLE || TCFG_CHARGESTORE_ENABLE || TCFG_ANC_BOX_ENABLE || TCFG_UMIDIGI_BOX_ENABLE)
struct port_wakeup port1 = {
.pullup_down_enable = DISABLE, //配置I/O 内部上下拉是否使能
@@ -779,6 +790,9 @@ const struct wakeup_param wk_param = {
.aport[1] = &vbat_port,
.aport[2] = &ldoin_port,
#endif
+#if BOARD_CHG_ENABLE
+ .port[BOARD_CHG_WAKEUP_INDEX] = &board_chg_wakeup_port,
+#endif
};
void gSensor_wkupup_disable(void)
@@ -986,6 +1000,10 @@ void board_set_soft_poweroff(void)
#if TCFG_IOKEY_ENABLE
soff_gpio_protect(TCFG_IOKEY_POWER_ONE_PORT);
#endif
+#if BOARD_CHG_ENABLE
+ /* Preserve PG8 input state and its external pull-up for softoff wake. */
+ soff_gpio_protect(BOARD_CHG_PIN);
+#endif
#if (!(TCFG_LP_TOUCH_KEY_ENABLE && TCFG_LP_TOUCH_KEY1_EN))
//默认唤醒io
@@ -1015,8 +1033,6 @@ void board_set_soft_poweroff(void)
void sleep_exit_callback(u32 usec)
{
sleep_exit_callback_common(NULL);
-
- putchar('>');
}
void sleep_enter_callback(u8 step)
@@ -1024,7 +1040,6 @@ void sleep_enter_callback(u8 step)
/* 此函数禁止添加打印 */
if (step == 1) {
bsp_hw_sleep_pwm_off();
- putchar('<');
} else {
gpio_set_pull_up(TCFG_CHARGESTORE_PORT, 0);
gpio_set_pull_down(TCFG_CHARGESTORE_PORT, 0);
diff --git a/apps/usr_app/app_boot.c b/apps/usr_app/app_boot.c
index b6d8cf4..a66e864 100644
--- a/apps/usr_app/app_boot.c
+++ b/apps/usr_app/app_boot.c
@@ -24,6 +24,7 @@
#include "app_main.h"
#include "usr_le_api.h"
#include "led_manager.h"
+#include "asm/charge.h"
#include "asm/power/power_api.h"
#include "asm/power/power_reset.h"
@@ -34,6 +35,7 @@
extern void clr_wdt(void);
extern u32 timer_get_ms(void);
+extern u8 is_ldo5v_wakeup(void);
/**
* @brief 忙等若干毫秒,并喂狗
@@ -116,9 +118,17 @@ static uint8_t boot_skip_key_wait(void)
{
uint8_t i;
+ /* LDO5V wake is latched by P33 and remains reliable while the live
+ * voltage/CHG detectors are still settling after a soft-poweroff reset. */
+ if (is_ldo5v_wakeup()) {
+ log_info("boot ldo5v wake, skip key wait\n");
+ return 1;
+ }
+
/* USB 在位(充电口有电):说明是插着电源,不是用户凭空按键开机 */
for (i = 0; i < 10; i++) {
- if (bsp_chg_is_low() || bsp_vpwr_is_online()) {
+ if (bsp_chg_is_low() || bsp_vpwr_is_online()
+ || get_ldo5v_online_hw()) {
log_info("boot usb, skip key wait\n");
return 1;
}
@@ -141,10 +151,7 @@ static uint8_t boot_skip_key_wait(void)
/**
* @brief 等待并判定开机按键
- * @note 配对(5s)不再一到点就立即提交:需要继续按住观察是否会按到 12s
- * 触发 OTA,真正的配对提交放在松手那一刻做。按满 5s 绿灯先切到配对
- * 快闪提示,行为上仍是“按满 5s 再松手即进配对”,只是多给了继续按到
- * 12s 改道 OTA 的机会。
+ * @note 按满 5s 立即进入配对;继续按住到 12s 时直接转入 OTA。
* 3s 常亮 / 5s 快闪直接复用 led_manager 的 LED_EVENT_STANDBY /
* LED_EVENT_PAIRING 事件(和正常运行时配对灯效完全一致的节奏),
* 不再手写一份闪烁定时。LED 事件库由调用方(usr_jb_init)在本函数
@@ -155,7 +162,7 @@ void app_boot_wait_key(void)
{
uint32_t t0;
uint32_t elapsed;
- uint8_t pair_armed = 0;
+ uint8_t pair_started = 0;
usr_var.usr_goto_pair = 0;
@@ -190,15 +197,17 @@ void app_boot_wait_key(void)
return;
}
- /* 5s 只武装配对并切到快闪,真正提交放到松手那一刻,给继续按到 12s 改道 OTA 的机会 */
- if (!pair_armed && (elapsed >= BOARD_KEY_PAIR_MS)) {
- pair_armed = 1;
+ /* 5s 立即进入配对;继续按住时仍检测 12s OTA。 */
+ if (!pair_started && (elapsed >= BOARD_KEY_PAIR_MS)) {
+ pair_started = 1;
+ usr_var.usr_goto_pair = 1;
+ usr_clear_pair_info_noreset();
+ app_pair_start();
LED_EventDelete(LED_EVENT_STANDBY);
LED_EventAdd(LED_EVENT_PAIRING);
- log_info("boot hold %ums, pair hint blink (release now for pair, "
- "keep holding to %ums for ota)\n",
+ log_info("boot hold %ums, power on + pair (keep holding to %ums for ota)\n",
(unsigned)elapsed, (unsigned)BOARD_KEY_OTA_MS);
- } else if (!pair_armed && !LED_IsEventExist(LED_EVENT_STANDBY)
+ } else if (!pair_started && !LED_IsEventExist(LED_EVENT_STANDBY)
&& (elapsed >= BOARD_KEY_POWER_MS)) {
LED_EventAdd(LED_EVENT_STANDBY);
log_info("boot hold 3s, will power on\n");
@@ -213,13 +222,8 @@ void app_boot_wait_key(void)
boot_sleep();
}
- /* 松手时已过 5s(且没按到 12s):提交配对;否则只是正常开机 */
- if (pair_armed) {
- usr_var.usr_goto_pair = 1;
- usr_clear_pair_info_noreset();
- app_pair_start();
- log_info("boot hold %ums, power on + pair\n", (unsigned)elapsed);
- } else {
+ /* 配对已在 5s 阈值提交;松手这里只记录普通开机。 */
+ if (!pair_started) {
log_info("boot hold %ums, power on\n", (unsigned)elapsed);
}
}
diff --git a/apps/usr_app/app_function.c b/apps/usr_app/app_function.c
index 71e07db..3434f69 100644
--- a/apps/usr_app/app_function.c
+++ b/apps/usr_app/app_function.c
@@ -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");
+ }
+}
diff --git a/apps/usr_app/app_function.h b/apps/usr_app/app_function.h
index cf86469..b2532ff 100644
--- a/apps/usr_app/app_function.h
+++ b/apps/usr_app/app_function.h
@@ -7,8 +7,8 @@
* @history
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现 DP0 开关、自动玩法、手动点动与按键动作
*
- * @note 本模块决定“电机现在该不该转、转哪种”,会读 app_power/app_pair 的
- * 状态作为阻塞条件(充电中/深睡中/配对中都不能转),也会触发
+ * @note 本模块决定“电机现在该不该转、转哪种”,会读电源与 OTA 状态作为
+ * 阻塞条件;配对是开机状态上的附加会话,不阻塞运动或按键。也会触发
* app_led 的一次性提示灯(切模式闪、开机绿灯),但不会被它们反向依赖。
******************************************************************************/
@@ -23,13 +23,25 @@
*/
void app_function_init(void);
+/** CBC3 DP0 写入:排队到业务节拍统一执行。 */
+void app_function_dp_power_write(uint8_t enable);
+
+/** CBC3 DP1 写入:支持重复下发当前模式时停止。 */
+void app_function_dp_play_write(uint8_t mode);
+
/**
* @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);
+/** CBC3 DP6 震动开机开关。 */
+void app_function_set_shake_wake(uint8_t enable);
+
+/** 勿扰时段是否正在生效;生效时屏蔽震动开机。 */
+void app_function_set_no_disturb(uint8_t enable);
+
/**
* @brief 跟随 APP 下发的 DP0/DP1,并处理 DP2 手动点动,每个业务 tick 调用一次
* @return 无
@@ -75,7 +87,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);
diff --git a/apps/usr_app/app_led.c b/apps/usr_app/app_led.c
index 353f23d..7c52c9b 100644
--- a/apps/usr_app/app_led.c
+++ b/apps/usr_app/app_led.c
@@ -124,6 +124,16 @@ void app_led_request_ota(void)
LED_EventAdd(LED_EVENT_OTA);
}
+void app_led_exit_ota(void)
+{
+ if (!s_initialized || !s_ota) {
+ return;
+ }
+ s_ota = 0;
+ LED_EventDelete(LED_EVENT_OTA);
+ bsp_led_all_off();
+}
+
/**
* @brief 查询是否处于 OTA 灯效
* @return 1=OTA 灯效中,0=否
@@ -230,7 +240,8 @@ void app_led_run(uint16_t dt, uint8_t app_power)
charge_led = app_power_charge_led();
low_bat = app_power_low_bat();
func_on = app_power ? 1 : 0;
- pairing = (uint8_t)(app_pair_is_active() && func_on);
+ pairing = (uint8_t)(app_pair_is_active() && func_on
+ && !app_power_usb_online());
/* 充电红 / 充满绿:和功能开关无关,关着设备也可能在充电 */
if (charge_led == 1) {
diff --git a/apps/usr_app/app_led.h b/apps/usr_app/app_led.h
index 5d7bb99..51dcc25 100644
--- a/apps/usr_app/app_led.h
+++ b/apps/usr_app/app_led.h
@@ -57,6 +57,9 @@ void app_led_hint_power_on(void);
*/
void app_led_request_ota(void);
+/** Leave the OTA indication and return LED control to the normal state. */
+void app_led_exit_ota(void);
+
/**
* @brief 查询是否处于 OTA 灯效
* @return 1=OTA 灯效中,0=否
diff --git a/apps/usr_app/app_mode.c b/apps/usr_app/app_mode.c
index c9bdade..1d81698 100644
--- a/apps/usr_app/app_mode.c
+++ b/apps/usr_app/app_mode.c
@@ -1,15 +1,3 @@
-/******************************************************************************
- * @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"
@@ -19,200 +7,341 @@
#define LOG_INFO_ENABLE
#include "debug.h"
-typedef struct {
- BspMotorDir_t dir;
- uint16_t ms;
- uint16_t duty; /**< 0~10000,STOP 忽略 */
-} AppModeStep_t;
+#define MAGIC_BOX_HIGH_PERCENT 100
+#define MAGIC_BOX_MEDIUM_LOW_PERCENT 45
+#define MAGIC_BOX_MODE1_FORWARD_MS 585
+#define MAGIC_BOX_MODE1_BACKWARD_MS 600
+#define MAGIC_BOX_MODE2_FORWARD_MS 260
+#define MAGIC_BOX_MODE2_BACKWARD_MS 200
+#define MAGIC_BOX_MODE3_FORWARD_MS 585
+#define MAGIC_BOX_MODE3_BACKWARD_MS 600
+#define MAGIC_BOX_MODE3_STOP_MS 2000
+#define MAGIC_BOX_MODE3_LOWER_SEGMENTS 10
+#define MAGIC_BOX_MODE4_FORWARD_MS 390
+#define MAGIC_BOX_MODE4_BACKWARD_MS 300
+#define MAGIC_BOX_MODE4_STOP_MS 3000
+#define MAGIC_BOX_MODE5_BACKWARD_MS 600
+#define MAGIC_BOX_MODE5_UPPER_STOP_MS 2000
+#define MAGIC_BOX_MODE5_CYCLE_MS 20000
-#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
-};
-
-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; /**< 当前节拍起始时间戳,用于判断是否该跳下一步 */
+ uint8_t active;
+ uint8_t output_valid;
+ uint8_t upper_phase;
+ uint8_t lower_pattern;
+ uint8_t lower_segment;
+ int8_t upper_direction;
+ int8_t lower_direction;
+ uint8_t upper_speed;
+ uint16_t upper_duration_ms;
+ uint16_t lower_duration_ms;
+ uint32_t state_started_ms;
+ uint32_t upper_started_ms;
+ uint32_t lower_started_ms;
+ int8_t output_upper_direction;
+ int8_t output_lower_direction;
+ uint8_t output_upper_speed;
+ uint8_t output_lower_speed;
} 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)
+static void app_mode_outputs_set(int8_t upper_direction, uint8_t upper_speed,
+ int8_t lower_direction, uint8_t lower_speed)
{
- const AppModeStep_t *tbl = s_tables[s_mode.active];
-
- if (!tbl) {
- bsp_motor_set(BSP_MOTOR_STOP);
+ if (s_mode.output_valid &&
+ upper_direction == s_mode.output_upper_direction &&
+ upper_speed == s_mode.output_upper_speed &&
+ lower_direction == s_mode.output_lower_direction &&
+ lower_speed == s_mode.output_lower_speed) {
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);
+
+ bsp_drive_set((BspMotorDir_t)upper_direction, (uint16_t)upper_speed * 100,
+ (BspMotorDir_t)lower_direction, (uint16_t)lower_speed * 100);
+ s_mode.output_upper_direction = upper_direction;
+ s_mode.output_upper_speed = upper_speed;
+ s_mode.output_lower_direction = lower_direction;
+ s_mode.output_lower_speed = lower_speed;
+ s_mode.output_valid = 1;
+}
+
+static uint16_t app_mode4_random_step_ms(void)
+{
+ static const uint16_t step_ms[] = {80, 100, 150, 200};
+
+ return step_ms[rand32() % ARRAY_SIZE(step_ms)];
+}
+
+static uint16_t app_mode5_random_forward_ms(void)
+{
+ static const uint16_t forward_ms[] = {555, 780, 1140};
+
+ return forward_ms[rand32() % ARRAY_SIZE(forward_ms)];
+}
+
+static void app_mode5_select_lower_pattern(uint32_t now_ms)
+{
+ s_mode.lower_pattern = rand32() % 4;
+ s_mode.lower_segment = 0;
+ s_mode.lower_started_ms = now_ms;
+
+ switch (s_mode.lower_pattern) {
+ case 0:
+ s_mode.lower_direction = BSP_MOTOR_FORWARD;
+ s_mode.lower_duration_ms = 2000;
+ break;
+ case 1:
+ s_mode.lower_direction = BSP_MOTOR_BACKWARD;
+ s_mode.lower_duration_ms = 2000;
+ break;
+ case 2:
+ s_mode.lower_direction = (rand32() & 1) ?
+ BSP_MOTOR_FORWARD : BSP_MOTOR_BACKWARD;
+ s_mode.lower_duration_ms = 100;
+ break;
+ default:
+ s_mode.lower_direction = (rand32() & 1) ?
+ BSP_MOTOR_FORWARD : BSP_MOTOR_BACKWARD;
+ s_mode.lower_duration_ms = 200;
+ break;
}
}
-/**
- * @brief 玩法初始化,电机停
- * @return APP_MODE_OK=成功
- */
+static void app_mode5_start_cycle(uint32_t now_ms)
+{
+ s_mode.state_started_ms = now_ms;
+ s_mode.upper_phase = 0;
+ s_mode.upper_started_ms = now_ms;
+ s_mode.upper_duration_ms = app_mode5_random_forward_ms();
+ s_mode.upper_direction = BSP_MOTOR_FORWARD;
+ s_mode.upper_speed = MAGIC_BOX_HIGH_PERCENT;
+ app_mode5_select_lower_pattern(now_ms);
+}
+
+static void app_mode_start_current(uint32_t now_ms)
+{
+ bsp_drive_stop();
+ s_mode.output_valid = 0;
+ s_mode.state_started_ms = now_ms;
+ s_mode.upper_phase = 0;
+ s_mode.upper_started_ms = now_ms;
+ s_mode.lower_started_ms = now_ms;
+
+ if (s_mode.active == 4) {
+ s_mode.lower_direction = (rand32() & 1) ?
+ BSP_MOTOR_FORWARD : BSP_MOTOR_BACKWARD;
+ s_mode.lower_duration_ms = app_mode4_random_step_ms();
+ } else if (s_mode.active == 5) {
+ app_mode5_start_cycle(now_ms);
+ }
+}
+
+static void app_mode1_process(uint32_t now_ms)
+{
+ const uint16_t pair_ms = MAGIC_BOX_MODE1_FORWARD_MS +
+ MAGIC_BOX_MODE1_BACKWARD_MS;
+ uint16_t elapsed_ms = (uint32_t)(now_ms - s_mode.state_started_ms) % pair_ms;
+
+ if (elapsed_ms < MAGIC_BOX_MODE1_FORWARD_MS) {
+ app_mode_outputs_set(BSP_MOTOR_FORWARD, MAGIC_BOX_HIGH_PERCENT,
+ BSP_MOTOR_STOP, 0);
+ } else {
+ app_mode_outputs_set(BSP_MOTOR_BACKWARD, MAGIC_BOX_HIGH_PERCENT,
+ BSP_MOTOR_STOP, 0);
+ }
+}
+
+static void app_mode2_process(uint32_t now_ms)
+{
+ const uint16_t pair_ms = MAGIC_BOX_MODE2_FORWARD_MS +
+ MAGIC_BOX_MODE2_BACKWARD_MS;
+ uint16_t elapsed_ms = (uint32_t)(now_ms - s_mode.state_started_ms) % pair_ms;
+
+ if (elapsed_ms < MAGIC_BOX_MODE2_FORWARD_MS) {
+ app_mode_outputs_set(BSP_MOTOR_FORWARD, MAGIC_BOX_HIGH_PERCENT,
+ BSP_MOTOR_STOP, 0);
+ } else {
+ app_mode_outputs_set(BSP_MOTOR_BACKWARD, MAGIC_BOX_HIGH_PERCENT,
+ BSP_MOTOR_STOP, 0);
+ }
+}
+
+static void app_mode3_process(uint32_t now_ms)
+{
+ const uint16_t upper_move_ms = MAGIC_BOX_MODE3_FORWARD_MS +
+ MAGIC_BOX_MODE3_BACKWARD_MS;
+ const uint16_t cycle_ms = upper_move_ms + MAGIC_BOX_MODE3_STOP_MS;
+ uint16_t elapsed_ms = (uint32_t)(now_ms - s_mode.state_started_ms) % cycle_ms;
+ uint8_t lower_segment = ((uint32_t)elapsed_ms *
+ MAGIC_BOX_MODE3_LOWER_SEGMENTS) / cycle_ms;
+ int8_t upper_direction;
+ uint8_t upper_speed;
+ int8_t lower_direction;
+
+ if (elapsed_ms < MAGIC_BOX_MODE3_FORWARD_MS) {
+ upper_direction = BSP_MOTOR_FORWARD;
+ upper_speed = MAGIC_BOX_HIGH_PERCENT;
+ } else if (elapsed_ms < upper_move_ms) {
+ upper_direction = BSP_MOTOR_BACKWARD;
+ upper_speed = MAGIC_BOX_HIGH_PERCENT;
+ } else {
+ upper_direction = BSP_MOTOR_STOP;
+ upper_speed = 0;
+ }
+
+ lower_direction = (lower_segment & 1) ?
+ BSP_MOTOR_BACKWARD : BSP_MOTOR_FORWARD;
+ app_mode_outputs_set(upper_direction, upper_speed,
+ lower_direction, MAGIC_BOX_HIGH_PERCENT);
+}
+
+static void app_mode4_process(uint32_t now_ms)
+{
+ const uint16_t upper_move_ms = MAGIC_BOX_MODE4_FORWARD_MS +
+ MAGIC_BOX_MODE4_BACKWARD_MS;
+ const uint16_t cycle_ms = upper_move_ms + MAGIC_BOX_MODE4_STOP_MS;
+ uint16_t elapsed_ms = (uint32_t)(now_ms - s_mode.state_started_ms);
+ int8_t upper_direction;
+ uint8_t upper_speed;
+
+ if (elapsed_ms >= cycle_ms) {
+ s_mode.state_started_ms = now_ms;
+ elapsed_ms = 0;
+ s_mode.lower_started_ms = now_ms;
+ s_mode.lower_direction = -s_mode.lower_direction;
+ s_mode.lower_duration_ms = app_mode4_random_step_ms();
+ }
+ if ((uint32_t)(now_ms - s_mode.lower_started_ms) >=
+ s_mode.lower_duration_ms) {
+ s_mode.lower_started_ms = now_ms;
+ s_mode.lower_direction = -s_mode.lower_direction;
+ s_mode.lower_duration_ms = app_mode4_random_step_ms();
+ }
+
+ if (elapsed_ms < MAGIC_BOX_MODE4_FORWARD_MS) {
+ upper_direction = BSP_MOTOR_FORWARD;
+ upper_speed = MAGIC_BOX_HIGH_PERCENT;
+ } else if (elapsed_ms < upper_move_ms) {
+ upper_direction = BSP_MOTOR_BACKWARD;
+ upper_speed = MAGIC_BOX_MEDIUM_LOW_PERCENT;
+ } else {
+ upper_direction = BSP_MOTOR_STOP;
+ upper_speed = 0;
+ }
+ app_mode_outputs_set(upper_direction, upper_speed,
+ s_mode.lower_direction, MAGIC_BOX_HIGH_PERCENT);
+}
+
+static void app_mode5_process(uint32_t now_ms)
+{
+ if ((uint32_t)(now_ms - s_mode.state_started_ms) >=
+ MAGIC_BOX_MODE5_CYCLE_MS) {
+ app_mode5_start_cycle(now_ms);
+ }
+
+ if ((uint32_t)(now_ms - s_mode.upper_started_ms) >=
+ s_mode.upper_duration_ms) {
+ s_mode.upper_started_ms = now_ms;
+ s_mode.upper_phase++;
+ if (s_mode.upper_phase == 1) {
+ s_mode.upper_direction = BSP_MOTOR_BACKWARD;
+ s_mode.upper_speed = MAGIC_BOX_HIGH_PERCENT;
+ s_mode.upper_duration_ms = MAGIC_BOX_MODE5_BACKWARD_MS;
+ } else if (s_mode.upper_phase == 2) {
+ s_mode.upper_direction = BSP_MOTOR_STOP;
+ s_mode.upper_speed = 0;
+ s_mode.upper_duration_ms = MAGIC_BOX_MODE5_UPPER_STOP_MS;
+ } else {
+ s_mode.upper_phase = 0;
+ s_mode.upper_direction = BSP_MOTOR_FORWARD;
+ s_mode.upper_speed = MAGIC_BOX_HIGH_PERCENT;
+ s_mode.upper_duration_ms = app_mode5_random_forward_ms();
+ }
+ }
+
+ if ((uint32_t)(now_ms - s_mode.lower_started_ms) >=
+ s_mode.lower_duration_ms) {
+ if (s_mode.lower_pattern >= 2 && ++s_mode.lower_segment < 8) {
+ s_mode.lower_started_ms = now_ms;
+ s_mode.lower_direction = -s_mode.lower_direction;
+ } else {
+ app_mode5_select_lower_pattern(now_ms);
+ }
+ }
+ app_mode_outputs_set(s_mode.upper_direction, s_mode.upper_speed,
+ s_mode.lower_direction, MAGIC_BOX_HIGH_PERCENT);
+}
+
AppMode_Ret_t app_mode_init(void)
{
- s_mode.active = 0;
- s_mode.step = 0;
- s_mode.step_t0 = timer_get_ms();
+ memset(&s_mode, 0, sizeof(s_mode));
s_mode.initialized = 1;
- bsp_motor_set(BSP_MOTOR_STOP);
+ bsp_drive_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) {
+ if (mode < 1 || mode > 5) {
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);
+ app_mode_start_current(timer_get_ms());
+ log_info("magic box 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);
+ s_mode.output_valid = 0;
+ bsp_drive_stop();
}
-/**
- * @brief 推进当前节拍(按 timer_get_ms 真实时间)
- * @return 无
- */
void app_mode_run(void)
{
- const AppModeStep_t *tbl;
+ uint32_t now_ms;
- bsp_motor_switch_tick(); /* 换向死区:H 桥松开后再切方向 */
- if (!s_mode.initialized || s_mode.active == 0) {
+ bsp_motor_switch_tick();
+ if (!s_mode.initialized || !s_mode.active) {
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();
+ now_ms = timer_get_ms();
+ switch (s_mode.active) {
+ case 1:
+ app_mode1_process(now_ms);
+ break;
+ case 2:
+ app_mode2_process(now_ms);
+ break;
+ case 3:
+ app_mode3_process(now_ms);
+ break;
+ case 4:
+ app_mode4_process(now_ms);
+ break;
+ case 5:
+ app_mode5_process(now_ms);
+ break;
+ default:
+ app_mode_stop();
+ break;
}
}
diff --git a/apps/usr_app/app_mode.h b/apps/usr_app/app_mode.h
index f67548a..1fa136c 100644
--- a/apps/usr_app/app_mode.h
+++ b/apps/usr_app/app_mode.h
@@ -1,6 +1,6 @@
/******************************************************************************
* @file app_mode.h
- * @brief 6 种自动玩法:节拍含方向与 PWM 占空比(0=停,1~6=波形)
+ * @brief 弹力魔盒 5 种双电机自动玩法(0=停,1~5=玩法)
* @author cyWu <1917507415@qq.com>
* @date 2026.08.24
* @version V1.2.0
@@ -28,8 +28,8 @@ typedef enum {
AppMode_Ret_t app_mode_init(void);
/**
- * @brief 启动自动模式 1~6,循环播放节拍
- * @param mode 玩法编号,范围 1~6
+ * @brief 启动自动模式 1~5,循环播放节拍
+ * @param mode 玩法编号,范围 1~5
* @return APP_MODE_OK=成功,APP_MODE_ERR_NOT_INIT=未初始化,APP_MODE_ERR_PARAM=参数非法
*/
AppMode_Ret_t app_mode_start(uint8_t mode);
@@ -48,7 +48,7 @@ void app_mode_run(void);
/**
* @brief 读取当前自动模式
- * @return 0=停转,1~6=对应玩法
+ * @return 0=停转,1~5=对应玩法
*/
uint8_t app_mode_get_active(void);
diff --git a/apps/usr_app/app_pair.c b/apps/usr_app/app_pair.c
index 8730f6f..417a83b 100644
--- a/apps/usr_app/app_pair.c
+++ b/apps/usr_app/app_pair.c
@@ -36,6 +36,12 @@ void app_pair_start(void)
s_pair.elapsed_ms = 0;
}
+void app_pair_stop(void)
+{
+ s_pair.active = 0;
+ s_pair.elapsed_ms = 0;
+}
+
/**
* @brief 推进配对计时并检查结果,每个业务 tick 调用一次
* @param dt 距上次调用的毫秒数
diff --git a/apps/usr_app/app_pair.h b/apps/usr_app/app_pair.h
index 8957568..bbc83c2 100644
--- a/apps/usr_app/app_pair.h
+++ b/apps/usr_app/app_pair.h
@@ -29,6 +29,9 @@ typedef enum {
*/
void app_pair_start(void);
+/** End the current pairing window without changing saved binding data. */
+void app_pair_stop(void);
+
/**
* @brief 推进配对计时并检查结果,每个业务 tick 调用一次
* @param dt 距上次调用的毫秒数
diff --git a/apps/usr_app/app_power.c b/apps/usr_app/app_power.c
index 94768f5..5aaa07c 100644
--- a/apps/usr_app/app_power.c
+++ b/apps/usr_app/app_power.c
@@ -35,6 +35,7 @@
#define BAT_UPDATE_MS (BAT_SAMPLE_MS * BAT_AVG_N) /**< 凑够一次新均值的真实耗时,喂给 app_battery */
#define BAT_BOOT_MS 3000 /* 上电 3s 内不因低电深睡 */
#define BAT_LOG_MS 10000
+#define BAT_PERIODIC_LOG_ENABLE 0 /* Production standby must not wake UART periodically. */
#define BAT_LOW_WIN 3 /* 连续约 2.4s 才进低电告警(仅闪灯,不关机) */
/** 充电检测:CHG/VPWR 消抖后的结果 */
@@ -401,6 +402,7 @@ static void power_battery_check_low(uint16_t mv)
*/
static void power_battery_periodic_log(uint16_t dt)
{
+#if BAT_PERIODIC_LOG_ENABLE
AppBatterySample_t *s = &s_power.sample;
s->log_ms = app_add_ms(s->log_ms, dt);
@@ -408,6 +410,9 @@ static void power_battery_periodic_log(uint16_t dt)
s->log_ms = 0;
log_info("bat %d%% %dmV alm=%d\n", s->pct, s->mv, s_power.alarm.low_bat);
}
+#else
+ (void)dt;
+#endif
}
/**
diff --git a/apps/usr_app/app_shake_wake.c b/apps/usr_app/app_shake_wake.c
new file mode 100644
index 0000000..58165c7
--- /dev/null
+++ b/apps/usr_app/app_shake_wake.c
@@ -0,0 +1,112 @@
+/******************************************************************************
+ * @file app_shake_wake.c
+ * @brief PA3 vibration-switch wake detector.
+ *
+ * The fitted switch is normally closed to ground. A vibration opens the
+ * contact, so PA3 goes high, then returns low when the contact closes again.
+ * A wake is accepted only after the high level has been stable for the
+ * re-arm interval and is generated on the following high-to-low transition.
+ ******************************************************************************/
+
+#include "app_shake_wake.h"
+#include "board_pin.h"
+#include "asm/gpio.h"
+
+typedef struct {
+ AppShakeWakeAllowedFn allowed;
+ AppShakeWakeTriggerFn trigger;
+ uint8_t enable;
+ uint8_t armed;
+ uint8_t trigger_pending;
+ uint8_t high_ticks;
+} AppShakeWakeCtx_t;
+
+static AppShakeWakeCtx_t s_shake;
+
+static void shake_reset_detection(void)
+{
+ s_shake.armed = 0;
+ s_shake.trigger_pending = 0;
+ s_shake.high_ticks = 0;
+}
+
+static void shake_pin_init(void)
+{
+#if BOARD_SHAKE_ENABLE
+ gpio_set_die(BOARD_SHAKE_PIN, 1);
+ gpio_direction_input(BOARD_SHAKE_PIN);
+ gpio_set_pull_up(BOARD_SHAKE_PIN, 1);
+ gpio_set_pull_down(BOARD_SHAKE_PIN, 0);
+#endif
+}
+
+void app_shake_wake_init(AppShakeWakeAllowedFn allowed,
+ AppShakeWakeTriggerFn trigger)
+{
+ s_shake.allowed = allowed;
+ s_shake.trigger = trigger;
+ s_shake.enable = 0;
+ shake_reset_detection();
+ shake_pin_init();
+}
+
+void app_shake_wake_set_enable(uint8_t enable)
+{
+ s_shake.enable = !!enable;
+ shake_reset_detection();
+}
+
+void app_shake_wake_reset(void)
+{
+ shake_reset_detection();
+}
+
+void app_shake_wake_poll(void)
+{
+ uint8_t pin_high;
+ uint8_t rearm_ticks;
+
+ if (!s_shake.enable) {
+ shake_reset_detection();
+ return;
+ }
+
+#if BOARD_SHAKE_ENABLE
+ pin_high = (uint8_t)(gpio_read(BOARD_SHAKE_PIN) == BOARD_SHAKE_ACTIVE_LEVEL);
+#else
+ pin_high = 0;
+#endif
+ rearm_ticks = (uint8_t)(BOARD_SHAKE_REARM_MS / BOARD_SHAKE_POLL_MS);
+ if (!rearm_ticks) {
+ rearm_ticks = 1;
+ }
+
+ if (pin_high) {
+ if (s_shake.high_ticks < rearm_ticks) {
+ s_shake.high_ticks++;
+ }
+ if (s_shake.high_ticks >= rearm_ticks) {
+ s_shake.armed = 1;
+ }
+ return;
+ }
+
+ /* The contact has closed again: this is the verified wake edge. */
+ s_shake.high_ticks = 0;
+ if (s_shake.armed) {
+ s_shake.armed = 0;
+ s_shake.trigger_pending = 1;
+ }
+
+ if (!s_shake.trigger_pending) {
+ return;
+ }
+ s_shake.trigger_pending = 0;
+ if (!s_shake.allowed || !s_shake.allowed()) {
+ shake_reset_detection();
+ return;
+ }
+ if (s_shake.trigger) {
+ s_shake.trigger();
+ }
+}
diff --git a/apps/usr_app/app_shake_wake.h b/apps/usr_app/app_shake_wake.h
new file mode 100644
index 0000000..ab1f155
--- /dev/null
+++ b/apps/usr_app/app_shake_wake.h
@@ -0,0 +1,22 @@
+/******************************************************************************
+ * @file app_shake_wake.h
+ * @brief PA3 vibration-switch wake detector.
+ ******************************************************************************/
+
+#ifndef __APP_SHAKE_WAKE_H__
+#define __APP_SHAKE_WAKE_H__
+
+#include
+
+typedef uint8_t (*AppShakeWakeAllowedFn)(void);
+typedef void (*AppShakeWakeTriggerFn)(void);
+
+/* The detector owns only PA3 sampling state. Product policy stays in callbacks.
+ * app_shake_wake_poll() is driven by the shared BOARD_SHAKE_POLL_MS business tick. */
+void app_shake_wake_init(AppShakeWakeAllowedFn allowed,
+ AppShakeWakeTriggerFn trigger);
+void app_shake_wake_set_enable(uint8_t enable);
+void app_shake_wake_reset(void);
+void app_shake_wake_poll(void);
+
+#endif /* __APP_SHAKE_WAKE_H__ */
diff --git a/apps/usr_jb_proto/Protocol/jb_product.c b/apps/usr_jb_proto/Protocol/jb_product.c
index 1cc9739..be1fd69 100644
--- a/apps/usr_jb_proto/Protocol/jb_product.c
+++ b/apps/usr_jb_proto/Protocol/jb_product.c
@@ -1,6 +1,6 @@
/* ================================================================
* jb_product.c — 用户层实现(请填补空白处)
- * 设备: 门夹弹力绳 | 生成时间: 2026-08-21 18:01:47
+ * 设备: 猫猫捕猎罩 | 生成时间: 2026-08-18 16:24:47
*
* ══ 开发流程 ══
* 1. 实现硬件抽象: jbGetTimerCount(定时器由 SOC SDK 提供)
@@ -13,6 +13,8 @@
*/
#include "jb_product.h"
+#include "app_function.h"
+#include "usr_rtc.h"
/* ════════════════════════════════════════════════════════════════
* 全局变量
@@ -21,6 +23,79 @@
jb_data_point_t gDevData;
static volatile uint32_t timerMs;
+#define JB_DND_LEN 13
+#define JB_DND_ENABLE_OFFSET 0
+#define JB_DND_WEEK_OFFSET 2
+#define JB_DND_START_HOUR_OFFSET 9
+#define JB_DND_START_MIN_OFFSET 10
+#define JB_DND_END_HOUR_OFFSET 11
+#define JB_DND_END_MIN_OFFSET 12
+static uint8_t jbLastNoDisturbActive;
+
+/* Return weekday with Monday=0, matching DP7 bytes 2..8. */
+static uint8_t jbWeekdayMonday0(const struct sys_time *time)
+{
+ static const uint8_t monthOffset[12] = {
+ 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4
+ };
+ uint16_t year = time->year;
+ uint8_t sunday0;
+
+ if (time->month < 3) {
+ year--;
+ }
+ sunday0 = (uint8_t)((year + year / 4U - year / 100U + year / 400U +
+ monthOffset[time->month - 1U] + time->day) % 7U);
+ return (uint8_t)((sunday0 + 6U) % 7U);
+}
+
+static void jbNoDisturbNormalize(uint8_t *cfg)
+{
+ uint8_t i;
+
+ cfg[JB_DND_ENABLE_OFFSET] = !!cfg[JB_DND_ENABLE_OFFSET];
+ for (i = 0; i < 7; i++) {
+ cfg[JB_DND_WEEK_OFFSET + i] = !!cfg[JB_DND_WEEK_OFFSET + i];
+ }
+}
+
+static uint8_t jbNoDisturbScheduleValid(const uint8_t *cfg)
+{
+ uint8_t i;
+
+ if (cfg[JB_DND_START_HOUR_OFFSET] >= 24 ||
+ cfg[JB_DND_END_HOUR_OFFSET] >= 24 ||
+ cfg[JB_DND_START_MIN_OFFSET] >= 60 ||
+ cfg[JB_DND_END_MIN_OFFSET] >= 60) {
+ return 0;
+ }
+ for (i = 0; i < 7; i++) {
+ if (cfg[JB_DND_WEEK_OFFSET + i]) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static uint8_t jbNoDisturbActive(void)
+{
+ /* Match the verified product behavior: DP7 byte 0 is the global
+ * no-disturb switch. Schedule bytes are retained and reported, but do
+ * not gate the local vibration wake path. */
+ return !!gDevData.no_disturb_enable[JB_DND_ENABLE_OFFSET];
+}
+
+static void jbNoDisturbUpdate(void)
+{
+ uint8_t active = jbNoDisturbActive();
+
+ if (active == jbLastNoDisturbActive) {
+ return;
+ }
+ jbLastNoDisturbActive = active;
+ app_function_set_no_disturb(active);
+}
+
/* ════════════════════════════════════════════════════════════════
* userInit — DP 默认值
* ════════════════════════════════════════════════════════════════ */
@@ -30,16 +105,16 @@ void userInit(void)
gDevData.power = 0; /* DP0 开关 0=关 1=开 */
gDevData.play_mode = 0; /* PLAY_MODE_0 (停止复位) */
- memset(gDevData.hand_mode, 0, 2); /* DP2 手动模式 */
- gDevData.calibration = 0; /* DP3 绳索校准 0=关 1=开 */
- gDevData.numberturns = 0; /* DP4 绳索圈数 */
- gDevData.alm_low_battery = 0; /* DP5 低电量报警 0=关 1=开 */
- gDevData.battery = 0; /* DP6 电量上报 */
- gDevData.charge = 1; /* CHARGE_1 (充电中) */
- memset(gDevData.no_disturb_enable, 0, 13); /* DP8 勿扰模式开关 */
- gDevData.reserva_dp1 = 0; /* DP9 预留DP1 */
- gDevData.reserva_dp2 = 0; /* DP10 预留DP2 */
- gDevData.reserva_dp3 = 0; /* DP11 预留DP3 */
+ gDevData.hand_mode = 0; /* HAND_MODE_0 (忽略) */
+ gDevData.alm_low_battery = 0; /* DP3 低电量报警 0=关 1=开 */
+ gDevData.beep_trig = 0; /* DP4 声音开关 0=关 1=开 */
+ gDevData.battery = 0; /* DP5 电量上报 */
+ gDevData.shake_wake = 0; /* DP6 震动触发开机 0=关 1=开 */
+ memset(gDevData.no_disturb_enable, 0, 13); /* DP7 勿扰模式开关 */
+ gDevData.charge = 0; /* CHARGE_0 (不支持充电) */
+ gDevData.reserva_dp3 = 0; /* DP10 预留DP3 */
+ jbLastNoDisturbActive = 0xff;
+ jbNoDisturbUpdate();
}
/* ════════════════════════════════════════════════════════════════
@@ -48,16 +123,18 @@ void userInit(void)
* ════════════════════════════════════════════════════════════════ */
void userHandle(void)
{
+ jbNoDisturbUpdate();
+
/* ── 可上报 DP (RO / RW): 用户数据采集 ── */
// gDevData.power = read_sensor(); /* DP0 开关 */
// gDevData.play_mode = read_sensor(); /* DP1 自动模式 */
- // gDevData.alm_low_battery = read_sensor(); /* DP5 低电量报警 */
- // gDevData.battery = read_sensor(); /* DP6 电量上报 */
- // gDevData.charge = read_sensor(); /* DP7 充电状态 */
- // memcpy(gDevData.no_disturb_enable, sensor_read(), 13); /* DP8 勿扰模式开关 */
- // gDevData.reserva_dp1 = read_sensor(); /* DP9 预留DP1 */
- // gDevData.reserva_dp2 = read_sensor(); /* DP10 预留DP2 */
- // gDevData.reserva_dp3 = read_sensor(); /* DP11 预留DP3 */
+ // gDevData.alm_low_battery = read_sensor(); /* DP3 低电量报警 */
+ // gDevData.beep_trig = read_sensor(); /* DP4 声音开关 */
+ // gDevData.battery = read_sensor(); /* DP5 电量上报 */
+ // gDevData.shake_wake = read_sensor(); /* DP6 震动触发开机 */
+ // memcpy(gDevData.no_disturb_enable, sensor_read(), 13); /* DP7 勿扰模式开关 */
+ // gDevData.charge = read_sensor(); /* DP8 充电状态 */
+ // gDevData.reserva_dp3 = read_sensor(); /* DP10 预留DP3 */
}
/* ════════════════════════════════════════════════════════════════
@@ -69,6 +146,8 @@ void userHandle(void)
* ════════════════════════════════════════════════════════════════ */
int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
{
+ int8_t result = JB_OK;
+
if (!info || !ctrl)
{
return -1;
@@ -81,98 +160,47 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
/* ── DP0 开关 (bool) ── */
case DP_ID_POWER:
- if (ctrl->power)
- {
- gDevData.power = 1;
- // TODO: 开 — 执行动作
- }
- else
- {
- gDevData.power = 0;
- // TODO: 关 — 执行动作
- }
+ app_function_dp_power_write(ctrl->power);
break;
/* ── DP1 自动模式 (enum) ── */
case DP_ID_PLAY_MODE:
- gDevData.play_mode = ctrl->play_mode;
- switch (ctrl->play_mode)
- {
- case PLAY_MODE_0: /* 停止复位 */
- // TODO: 处理 停止复位
- break;
- case PLAY_MODE_1: /* 弹力伪装 */
- // TODO: 处理 弹力伪装
- break;
- case PLAY_MODE_2: /* 飞鸟俯冲 */
- // TODO: 处理 飞鸟俯冲
- break;
- case PLAY_MODE_3: /* 云阶小陀螺 */
- // TODO: 处理 云阶小陀螺
- break;
- case PLAY_MODE_4: /* 起伏攻防战 */
- // TODO: 处理 起伏攻防战
- break;
- case PLAY_MODE_5: /* 微风拂动 */
- // TODO: 处理 微风拂动
- break;
- case PLAY_MODE_6: /* 闪电快闪 */
- // TODO: 处理 闪电快闪
- break;
- default:
- break;
- }
+ app_function_dp_play_write(ctrl->play_mode);
break;
- /* ── DP2 手动模式 (raw) ── */
+ /* ── DP2 手动模式 (enum) ── */
case DP_ID_HAND_MODE:
- memcpy(gDevData.hand_mode, ctrl->hand_mode, 2);
- /* 薄适配:通知业务层;03=停,00=空闲 */
- {
- extern void usr_dp_hand_write(uint8_t cmd);
- usr_dp_hand_write(ctrl->hand_mode[0]);
- }
+ app_function_dp_hand_write(ctrl->hand_mode);
break;
- /* ── DP3 绳索校准 (bool) ── */
- case DP_ID_CALIBRATION:
- if (ctrl->calibration)
+ /* ── DP4 声音开关 (bool) ── */
+ case DP_ID_BEEP_TRIG:
+ if (ctrl->beep_trig)
{
- gDevData.calibration = 1;
+ gDevData.beep_trig = 1;
// TODO: 开 — 执行动作
}
else
{
- gDevData.calibration = 0;
+ gDevData.beep_trig = 0;
// TODO: 关 — 执行动作
}
break;
- /* ── DP4 绳索圈数 (uint8) ── */
- case DP_ID_NUMBERTURNS:
- gDevData.numberturns = ctrl->numberturns;
- // TODO: 根据 ctrl->numberturns 执行动作
+ /* ── DP6 震动触发开机 (bool) ── */
+ case DP_ID_SHAKE_WAKE:
+ app_function_set_shake_wake(ctrl->shake_wake);
break;
- /* ── DP8 勿扰模式开关 (raw) ── */
+ /* ── DP7 勿扰模式开关 (raw) ── */
case DP_ID_NO_DISTURB_ENABLE:
- memcpy(gDevData.no_disturb_enable, ctrl->no_disturb_enable, 13);
- // TODO: 根据 gDevData.no_disturb_enable 执行动作 (长度 13 字节)
+ memcpy(gDevData.no_disturb_enable, ctrl->no_disturb_enable,
+ JB_DND_LEN);
+ jbNoDisturbNormalize(gDevData.no_disturb_enable);
+ jbNoDisturbUpdate();
break;
- /* ── DP9 预留DP1 (uint32) ── */
- case DP_ID_RESERVA_DP1:
- gDevData.reserva_dp1 = ctrl->reserva_dp1;
- // TODO: 根据 ctrl->reserva_dp1 执行动作
- break;
-
- /* ── DP10 预留DP2 (uint32) ── */
- case DP_ID_RESERVA_DP2:
- gDevData.reserva_dp2 = ctrl->reserva_dp2;
- // TODO: 根据 ctrl->reserva_dp2 执行动作
- break;
-
- /* ── DP11 预留DP3 (uint32) ── */
+ /* ── DP10 预留DP3 (uint32) ── */
case DP_ID_RESERVA_DP3:
gDevData.reserva_dp3 = ctrl->reserva_dp3;
// TODO: 根据 ctrl->reserva_dp3 执行动作
@@ -182,21 +210,16 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
break;
}
}
- return 0;
+ return result;
}
-/* 0x30 OTA 升级请求
- * 返回 0=接受升级(协议层应答 JB_OK + MaxDataLen)
- * 非 0=拒绝,返回值即错误码(JB_OTA_*)
- *
- * ⚠ SOC 侧 OTA 尚未实现:当前仅应答,不执行任何升级动作。 */
-int8_t jbOnOtaNotify(uint32_t fwVersion, uint32_t fwLength, uint32_t fwCrc32)
+/* 0x30 OTA 升级请求 */
+void jbOnOtaNotify(uint32_t fwVersion, uint32_t fwLength, uint32_t fwCrc32)
{
(void)fwVersion; (void)fwLength; (void)fwCrc32;
// TODO: 检查 Flash 空间与版本,决定是否接收升级
- // 允许: 返回 JB_OK(协议层应答 JB_OK + MaxDataLen)
- // 拒绝: 返回对应 JB_OTA_* 错误码(如 JB_OTA_VER_LOW / JB_OTA_SPACE_ERR)
- return JB_OK;
+ // 允许: 直接返回(协议层默认应答 JB_OK + MaxDataLen=128)
+ // 拒绝: 在 jbHandleOtaNotify 中改为 jbSendFrame(CMD_OTA_NOTIFY_ACK, sn, 错误码, 3)
}
/* 0x32 OTA 数据(含分片序号)*/
@@ -219,8 +242,7 @@ void jbOnOtaStop(void)
- 可在本回调中设置"XX 完成/失败"标志,供主循环判断"确认成功才继续" */
void jbOnAck(uint8_t cmd, uint8_t result)
{
- (void)cmd;
- (void)result;
+ (void)cmd; (void)result;
// TODO: switch(cmd){ case CMD_OTA_COMPLETE_ACK: gOtaDone = (result==JB_OK); break; case CMD_OTA_STOP_MCU_ACK: ... }
}
@@ -232,7 +254,7 @@ void jbOnAck(uint8_t cmd, uint8_t result)
void jbTimerIrq(void)
{
timerMs++;
-} /* 在 1ms 中断中调用本函数 */
+} /* 在 1ms 中断中调用本函数 */
uint32_t jbGetTimerCount(void)
{
diff --git a/apps/usr_jb_proto/Protocol/jb_product.h b/apps/usr_jb_proto/Protocol/jb_product.h
index f6b3777..5b05536 100644
--- a/apps/usr_jb_proto/Protocol/jb_product.h
+++ b/apps/usr_jb_proto/Protocol/jb_product.h
@@ -1,6 +1,6 @@
/* ================================================================
* jb_product.h — 用户层头文件(自动生成)
- * 设备: 门夹弹力绳 | 生成时间: 2026-08-21 18:01:47
+ * 设备: 猫猫捕猎罩 | 生成时间: 2026-08-18 16:24:47
*
* 声明用户需实现的函数。数据结构定义见 jb_protocol.h
* ================================================================
@@ -43,7 +43,7 @@ void jbTimerIrq(void); /* Call from 1ms timer ISR */
/* =========== 协议回调(声明见 jb_protocol.h)================== */
/* int8_t jbEventProcess(...); // 0x03 控制事件 */
-/* int8_t jbOnOtaNotify(...); // 0x30 OTA 请求 */
+/* void jbOnOtaNotify(...); // 0x30 OTA 请求 */
/* void jbOnOtaData(...); // 0x32 OTA 数据 */
/* void jbOnOtaStop(); // 0x36/0x38 停止 OTA */
/* void jbOnAck(...); // 任意请求 ACK(结果) */
diff --git a/apps/usr_jb_proto/Protocol/jb_protocol.c b/apps/usr_jb_proto/Protocol/jb_protocol.c
index 0ff8d68..22c875e 100644
--- a/apps/usr_jb_proto/Protocol/jb_protocol.c
+++ b/apps/usr_jb_proto/Protocol/jb_protocol.c
@@ -1,14 +1,13 @@
/* ================================================================
* jb_protocol.c — 见宝 IOT 协议实现(自动生成)
- * 设备: 门夹弹力绳 | 生成时间: 2026-08-21 18:01:47
+ * 设备: 猫猫捕猎罩 | 生成时间: 2026-08-18 16:24:47
* ================================================================
*/
#include "jb_protocol.h"
-#include "jb_common.h"
#include "jb_product.h"
#include "jb_ringbuffer.h"
-#include "stdio.h"
+#include "jb_common.h"
/* ════════════════════════════════════════════════════════════════
* 全局实例
@@ -168,46 +167,44 @@ static uint16_t jbPackDps(const jb_data_point_t *dp,
bitmap[DP_ID_PLAY_MODE / 8] |= (1 << (DP_ID_PLAY_MODE % 8));
out[valIdx++] = (uint8_t)dp->play_mode;
}
- /* DP5 低电量报警 (bool, 1B, RO) */
+ /* DP3 低电量报警 (bool, 1B, RO) */
if (mask[DP_ID_ALM_LOW_BATTERY / 8] & (1 << (DP_ID_ALM_LOW_BATTERY % 8)))
{
bitmap[DP_ID_ALM_LOW_BATTERY / 8] |= (1 << (DP_ID_ALM_LOW_BATTERY % 8));
out[valIdx++] = dp->alm_low_battery ? 1 : 0;
}
- /* DP6 电量上报 (uint8, 1B, RO) */
+ /* DP4 声音开关 (bool, 1B, RW) */
+ if (mask[DP_ID_BEEP_TRIG / 8] & (1 << (DP_ID_BEEP_TRIG % 8)))
+ {
+ bitmap[DP_ID_BEEP_TRIG / 8] |= (1 << (DP_ID_BEEP_TRIG % 8));
+ out[valIdx++] = dp->beep_trig ? 1 : 0;
+ }
+ /* DP5 电量上报 (uint8, 1B, RO) */
if (mask[DP_ID_BATTERY / 8] & (1 << (DP_ID_BATTERY % 8)))
{
bitmap[DP_ID_BATTERY / 8] |= (1 << (DP_ID_BATTERY % 8));
out[valIdx++] = (uint8_t)dp->battery;
}
- /* DP7 充电状态 (enum, 1B, RO) */
- if (mask[DP_ID_CHARGE / 8] & (1 << (DP_ID_CHARGE % 8)))
+ /* DP6 震动触发开机 (bool, 1B, RW) */
+ if (mask[DP_ID_SHAKE_WAKE / 8] & (1 << (DP_ID_SHAKE_WAKE % 8)))
{
- bitmap[DP_ID_CHARGE / 8] |= (1 << (DP_ID_CHARGE % 8));
- out[valIdx++] = (uint8_t)dp->charge;
+ bitmap[DP_ID_SHAKE_WAKE / 8] |= (1 << (DP_ID_SHAKE_WAKE % 8));
+ out[valIdx++] = dp->shake_wake ? 1 : 0;
}
- /* DP8 勿扰模式开关 (raw, 13B, RW) */
+ /* DP7 勿扰模式开关 (raw, 13B, RW) */
if (mask[DP_ID_NO_DISTURB_ENABLE / 8] & (1 << (DP_ID_NO_DISTURB_ENABLE % 8)))
{
bitmap[DP_ID_NO_DISTURB_ENABLE / 8] |= (1 << (DP_ID_NO_DISTURB_ENABLE % 8));
memcpy(&out[valIdx], dp->no_disturb_enable, 13);
valIdx += 13;
}
- /* DP9 预留DP1 (uint32, 4B, RW) */
- if (mask[DP_ID_RESERVA_DP1 / 8] & (1 << (DP_ID_RESERVA_DP1 % 8)))
+ /* DP8 充电状态 (enum, 1B, RO) */
+ if (mask[DP_ID_CHARGE / 8] & (1 << (DP_ID_CHARGE % 8)))
{
- bitmap[DP_ID_RESERVA_DP1 / 8] |= (1 << (DP_ID_RESERVA_DP1 % 8));
- jbWriteU32BE(&out[valIdx], (uint32_t)dp->reserva_dp1);
- valIdx += 4;
+ bitmap[DP_ID_CHARGE / 8] |= (1 << (DP_ID_CHARGE % 8));
+ out[valIdx++] = (uint8_t)dp->charge;
}
- /* DP10 预留DP2 (uint32, 4B, RW) */
- if (mask[DP_ID_RESERVA_DP2 / 8] & (1 << (DP_ID_RESERVA_DP2 % 8)))
- {
- bitmap[DP_ID_RESERVA_DP2 / 8] |= (1 << (DP_ID_RESERVA_DP2 % 8));
- jbWriteU32BE(&out[valIdx], (uint32_t)dp->reserva_dp2);
- valIdx += 4;
- }
- /* DP11 预留DP3 (uint32, 4B, RW) */
+ /* DP10 预留DP3 (uint32, 4B, RW) */
if (mask[DP_ID_RESERVA_DP3 / 8] & (1 << (DP_ID_RESERVA_DP3 % 8)))
{
bitmap[DP_ID_RESERVA_DP3 / 8] |= (1 << (DP_ID_RESERVA_DP3 % 8));
@@ -256,44 +253,43 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
info->dp_ids[info->count++] = DP_ID_PLAY_MODE;
}
}
- /* DP2 手动模式 (raw, 2B, WO) */
+ /* DP2 手动模式 (enum, 1B, WO) */
if (bitmap[DP_ID_HAND_MODE / 8] & (1 << (DP_ID_HAND_MODE % 8)))
{
- if (valIdx + 2 <= valLen)
+ if (valIdx < valLen)
{
- memcpy(dp->hand_mode, &values[valIdx], 2);
- valIdx += 2;
+ dp->hand_mode = values[valIdx++];
}
if (info && info->count < DP_ID_MAX)
{
info->dp_ids[info->count++] = DP_ID_HAND_MODE;
}
}
- /* DP3 绳索校准 (bool, 1B, WO) */
- if (bitmap[DP_ID_CALIBRATION / 8] & (1 << (DP_ID_CALIBRATION % 8)))
+ /* DP4 声音开关 (bool, 1B, RW) */
+ if (bitmap[DP_ID_BEEP_TRIG / 8] & (1 << (DP_ID_BEEP_TRIG % 8)))
{
if (valIdx < valLen)
{
- dp->calibration = (values[valIdx++] != 0);
+ dp->beep_trig = (values[valIdx++] != 0);
}
if (info && info->count < DP_ID_MAX)
{
- info->dp_ids[info->count++] = DP_ID_CALIBRATION;
+ info->dp_ids[info->count++] = DP_ID_BEEP_TRIG;
}
}
- /* DP4 绳索圈数 (uint8, 1B, WO) */
- if (bitmap[DP_ID_NUMBERTURNS / 8] & (1 << (DP_ID_NUMBERTURNS % 8)))
+ /* DP6 震动触发开机 (bool, 1B, RW) */
+ if (bitmap[DP_ID_SHAKE_WAKE / 8] & (1 << (DP_ID_SHAKE_WAKE % 8)))
{
if (valIdx < valLen)
{
- dp->numberturns = values[valIdx++];
+ dp->shake_wake = (values[valIdx++] != 0);
}
if (info && info->count < DP_ID_MAX)
{
- info->dp_ids[info->count++] = DP_ID_NUMBERTURNS;
+ info->dp_ids[info->count++] = DP_ID_SHAKE_WAKE;
}
}
- /* DP8 勿扰模式开关 (raw, 13B, RW) */
+ /* DP7 勿扰模式开关 (raw, 13B, RW) */
if (bitmap[DP_ID_NO_DISTURB_ENABLE / 8] & (1 << (DP_ID_NO_DISTURB_ENABLE % 8)))
{
if (valIdx + 13 <= valLen)
@@ -306,33 +302,7 @@ static void jbUnpackDps(const uint8_t *bitmap, const uint8_t *values,
info->dp_ids[info->count++] = DP_ID_NO_DISTURB_ENABLE;
}
}
- /* DP9 预留DP1 (uint32, 4B, RW) */
- if (bitmap[DP_ID_RESERVA_DP1 / 8] & (1 << (DP_ID_RESERVA_DP1 % 8)))
- {
- if (valIdx + 4 <= valLen)
- {
- dp->reserva_dp1 = (uint32_t)jbReadU32BE(&values[valIdx]);
- valIdx += 4;
- }
- if (info && info->count < DP_ID_MAX)
- {
- info->dp_ids[info->count++] = DP_ID_RESERVA_DP1;
- }
- }
- /* DP10 预留DP2 (uint32, 4B, RW) */
- if (bitmap[DP_ID_RESERVA_DP2 / 8] & (1 << (DP_ID_RESERVA_DP2 % 8)))
- {
- if (valIdx + 4 <= valLen)
- {
- dp->reserva_dp2 = (uint32_t)jbReadU32BE(&values[valIdx]);
- valIdx += 4;
- }
- if (info && info->count < DP_ID_MAX)
- {
- info->dp_ids[info->count++] = DP_ID_RESERVA_DP2;
- }
- }
- /* DP11 预留DP3 (uint32, 4B, RW) */
+ /* DP10 预留DP3 (uint32, 4B, RW) */
if (bitmap[DP_ID_RESERVA_DP3 / 8] & (1 << (DP_ID_RESERVA_DP3 % 8)))
{
if (valIdx + 4 <= valLen)
@@ -370,43 +340,43 @@ static uint8_t jbBuildDiffMask(const jb_data_point_t *last,
mask[DP_ID_PLAY_MODE / 8] |= (1 << (DP_ID_PLAY_MODE % 8));
hasChange = 1;
}
- /* DP5 低电量报警 */
+ /* DP3 低电量报警 */
if (last->alm_low_battery != cur->alm_low_battery)
{
mask[DP_ID_ALM_LOW_BATTERY / 8] |= (1 << (DP_ID_ALM_LOW_BATTERY % 8));
hasChange = 1;
}
- /* DP6 电量上报 */
+ /* DP4 声音开关 */
+ if (last->beep_trig != cur->beep_trig)
+ {
+ mask[DP_ID_BEEP_TRIG / 8] |= (1 << (DP_ID_BEEP_TRIG % 8));
+ hasChange = 1;
+ }
+ /* DP5 电量上报 */
if (last->battery != cur->battery)
{
mask[DP_ID_BATTERY / 8] |= (1 << (DP_ID_BATTERY % 8));
hasChange = 1;
}
- /* DP7 充电状态 */
- if (last->charge != cur->charge)
+ /* DP6 震动触发开机 */
+ if (last->shake_wake != cur->shake_wake)
{
- mask[DP_ID_CHARGE / 8] |= (1 << (DP_ID_CHARGE % 8));
+ mask[DP_ID_SHAKE_WAKE / 8] |= (1 << (DP_ID_SHAKE_WAKE % 8));
hasChange = 1;
}
- /* DP8 勿扰模式开关 */
+ /* DP7 勿扰模式开关 */
if (memcmp(last->no_disturb_enable, cur->no_disturb_enable, 13))
{
mask[DP_ID_NO_DISTURB_ENABLE / 8] |= (1 << (DP_ID_NO_DISTURB_ENABLE % 8));
hasChange = 1;
}
- /* DP9 预留DP1 */
- if (last->reserva_dp1 != cur->reserva_dp1)
+ /* DP8 充电状态 */
+ if (last->charge != cur->charge)
{
- mask[DP_ID_RESERVA_DP1 / 8] |= (1 << (DP_ID_RESERVA_DP1 % 8));
+ mask[DP_ID_CHARGE / 8] |= (1 << (DP_ID_CHARGE % 8));
hasChange = 1;
}
- /* DP10 预留DP2 */
- if (last->reserva_dp2 != cur->reserva_dp2)
- {
- mask[DP_ID_RESERVA_DP2 / 8] |= (1 << (DP_ID_RESERVA_DP2 % 8));
- hasChange = 1;
- }
- /* DP11 预留DP3 */
+ /* DP10 预留DP3 */
if (last->reserva_dp3 != cur->reserva_dp3)
{
mask[DP_ID_RESERVA_DP3 / 8] |= (1 << (DP_ID_RESERVA_DP3 % 8));
@@ -419,11 +389,9 @@ static uint8_t jbBuildDiffMask(const jb_data_point_t *last,
/* 置全量掩码(所有可上报 DP)*/
static void jbSetFullMask(uint8_t *mask)
{
- memset(mask, 0xFF, DP_BITMAP_SIZE);
- if (DP_COUNT % 8)
- {
- mask[DP_BITMAP_SIZE - 1] &= (1 << (DP_COUNT % 8)) - 1;
- }
+ /* DP IDs are sparse; list only the fields that can be reported/read. */
+ mask[0] = 0xFB; /* 0, 1, 3, 4, 5, 6, 7 */
+ mask[1] = 0x05; /* 8, 10 */
}
/* ════════════════════════════════════════════════════════════════
@@ -582,14 +550,25 @@ static void jbHandleControl(uint8_t *frame, uint16_t len)
uint8_t *values = &frame[6 + DP_BITMAP_SIZE];
uint16_t valLen = len - 7 - DP_BITMAP_SIZE;
+ /* Only writable fields are legal in a control frame. */
+ if ((bitmap[0] & (uint8_t)~0xD7U) ||
+ (bitmap[1] & (uint8_t)~0x04U))
+ {
+ jbSendAck(CMD_CONTROL_ACK, sn, JB_NOT_SUPPORT);
+ return;
+ }
+
jb_event_info_t info;
jb_data_point_t ctrlData;
memset(&ctrlData, 0, sizeof(ctrlData));
jbUnpackDps(bitmap, values, valLen, &ctrlData, &info);
- /* 分发给用户层;用户在回调中更新 gDevData */
- jbEventProcess(&info, &ctrlData);
- jbSendAck(CMD_CONTROL_ACK, sn, JB_OK);
+ /* 分发给用户层;用户在回调中更新 gDevData。 */
+ {
+ int8_t result = jbEventProcess(&info, &ctrlData);
+ jbSendAck(CMD_CONTROL_ACK, sn,
+ (result < 0) ? JB_PARAM_ERR : (uint8_t)result);
+ }
}
/* ── 0x05 APP 读取请求 ── */
@@ -632,10 +611,12 @@ static void jbHandleOtaNotify(uint8_t *frame, uint16_t len)
uint32_t fwLength = jbReadU32BE(&frame[9]);
uint32_t fwCrc32 = jbReadU32BE(&frame[13]);
- /* 应答: 结果码 + 最大单包长度(2B)(协议原格式,3 字节) */
- int8_t otaRet = jbOnOtaNotify(fwVersion, fwLength, fwCrc32);
+ jbOnOtaNotify(fwVersion, fwLength, fwCrc32);
+
+ /* 默认应答: 结果码 + 最大单包长度(2B) */
+ /* maxDataLen 由 MCU 自身能力决定,填入 0x31 应答,BLE 据此分片 */
uint8_t ack[3];
- ack[0] = (otaRet == 0) ? JB_OK : (uint8_t)otaRet;
+ ack[0] = JB_OK;
ack[1] = (uint8_t)(JB_OTA_MAX_PAYLOAD >> 8);
ack[2] = (uint8_t)(JB_OTA_MAX_PAYLOAD & 0xFF);
jbSendFrame(CMD_OTA_NOTIFY_ACK, sn, ack, 3);
@@ -649,9 +630,8 @@ static void jbHandleOtaData(uint8_t *frame, uint16_t len)
uint16_t totalShards = jbReadU16BE(&frame[8]);
const uint8_t *pData = &frame[10];
uint16_t dataLen = (len > 11) ? (len - 11) : 0;
- /* 先发本包 0x33 应答,再调用户回调满足「先回复、再重启」 */
- jbSendAck(CMD_OTA_DATA_ACK, sn, JB_OK);
jbOnOtaData(pData, dataLen, shardIdx, totalShards);
+ jbSendAck(CMD_OTA_DATA_ACK, sn, JB_OK);
}
/* ── 0x36 BLE 通知停止 OTA ── */
diff --git a/apps/usr_jb_proto/Protocol/jb_protocol.h b/apps/usr_jb_proto/Protocol/jb_protocol.h
index c3111d0..adad1f8 100644
--- a/apps/usr_jb_proto/Protocol/jb_protocol.h
+++ b/apps/usr_jb_proto/Protocol/jb_protocol.h
@@ -1,7 +1,7 @@
/* ================================================================
* jb_protocol.h — 见宝 IOT 协议层(自动生成)
- * 设备: 门夹弹力绳 | DP 数: 12 | Bitmap: 2B
- * 协议版本: V1.0 | 生成时间: 2026-08-21 18:01:47
+ * 设备: 猫猫捕猎罩 | DP 数: 10 | Bitmap: 2B
+ * 协议版本: V1.0 | 生成时间: 2026-08-18 16:24:47
*
* 帧格式: [55 AA] [LEN_H LEN_L] [CMD] [SN] [payload...] [CKSUM]
* LEN = CMD(1) + SN(1) + payload + CKSUM(1) = payload + 3
@@ -26,7 +26,7 @@
#define MAX_PACKAGE_LEN 128
#define JB_OTA_MAX_PAYLOAD 96 /* OTA 单包 Payload 上限(Byte); 须 <= MAX_PACKAGE_LEN - 11(0x32 帧开销) */
-#define DP_COUNT 12
+#define DP_COUNT 10
#define DP_BITMAP_SIZE 2
#define SEND_MAX_TIME 2500 /* ACK 重传超时(ms);BLE Indication往返常需数百ms~1s,过短会误重传导致双应答 */
@@ -34,14 +34,13 @@
#define REPORT_DEBOUNCE 2000 /* 变化上报防抖时间(ms) */
#define REPORT_PERIOD 600000 /* 定时全量上报周期(ms) */
-/* Device identity: 取自 Excel 设备信息表的 ProdtCode / productCode。
+/* Device identity: change these values for the target product.
* JB_CATEGORY_CODE: 必须 4 字符 ASCII,供 BLE 广播 prodcode 使用
* JB_PRODUCT_CODE : 必须 6 字符 ASCII,供 BLE 广播 pcode 使用
- * 模板未填时回退占位 XXXX / XXXXXX(仍需用户修改)。
*/
#define JB_PROTOCOL_VERSION 0x0100 /* V1.0 */
-#define JB_CATEGORY_CODE "CBC2"
-#define JB_PRODUCT_CODE "PICBC2"
+#define JB_CATEGORY_CODE "CBC4"
+#define JB_PRODUCT_CODE "PICBC4"
#define JB_DEVICE_SN 0x00
#define JB_DEVICE_CAPABILITY 0ULL
@@ -93,14 +92,14 @@ typedef enum
/* ════════════════════════════════════════════════════════════════
* BLE 工作状态位 (0x14)
* ════════════════════════════════════════════════════════════════ */
-#define WS_CONNECTING (1 << 0) /* 配网中 */
+#define WS_PAIRING (1 << 0) /* 配网中 */
#define WS_CONFIGURED (1 << 1) /* 已配网 */
#define WS_RECONNECTING (1 << 2) /* 断线重连中 */
#define WS_CLOUD_CONNECTED (1 << 3) /* 已连接到云 */
-#define WS_APP_ONLINE (1 << 4) /* App 在线 */
-#define WS_APP_DIRECT (1 << 5) /* App 直连 */
+#define WS_APP_ONLINE (1 << 4) /* APP 在线 */
+#define WS_APP_DIRECT (1 << 5) /* APP 直连 */
#define WS_OTA_UPGRADING (1 << 6) /* 模组 OTA 升级中 */
-#define WS_RESERVED (1 << 7) /* 保留 */
+#define WS_RESERVED (1 << 7) /* Reserved */
/* ════════════════════════════════════════════════════════════════
* 设备能力位图 (0x02 Capability Bitmap, 8B 大端)
@@ -120,31 +119,27 @@ typedef enum
{
DP_ID_POWER = 0, /* 开关 (bool, RW) */
DP_ID_PLAY_MODE = 1, /* 自动模式 (enum, RW) */
- DP_ID_HAND_MODE = 2, /* 手动模式 (raw, WO) */
- DP_ID_CALIBRATION = 3, /* 绳索校准 (bool, WO) */
- DP_ID_NUMBERTURNS = 4, /* 绳索圈数 (uint8, WO) */
- DP_ID_ALM_LOW_BATTERY = 5, /* 低电量报警 (bool, RO) */
- DP_ID_BATTERY = 6, /* 电量上报 (uint8, RO) */
- DP_ID_CHARGE = 7, /* 充电状态 (enum, RO) */
- DP_ID_NO_DISTURB_ENABLE = 8, /* 勿扰模式开关 (raw, RW) */
- DP_ID_RESERVA_DP1 = 9, /* 预留DP1 (uint32, RW) */
- DP_ID_RESERVA_DP2 = 10, /* 预留DP2 (uint32, RW) */
- DP_ID_RESERVA_DP3 = 11, /* 预留DP3 (uint32, RW) */
+ DP_ID_HAND_MODE = 2, /* 手动模式 (enum, WO) */
+ DP_ID_ALM_LOW_BATTERY = 3, /* 低电量报警 (bool, RO) */
+ DP_ID_BEEP_TRIG = 4, /* 声音开关 (bool, RW) */
+ DP_ID_BATTERY = 5, /* 电量上报 (uint8, RO) */
+ DP_ID_SHAKE_WAKE = 6, /* 震动触发开机 (bool, RW) */
+ DP_ID_NO_DISTURB_ENABLE = 7, /* 勿扰模式开关 (raw, RW) */
+ DP_ID_CHARGE = 8, /* 充电状态 (enum, RO) */
+ DP_ID_RESERVA_DP3 = 10, /* 预留DP3 (uint32, RW) */
DP_ID_MAX
} dp_id_t;
/* DP 数据长度(字节) */
#define DP_LEN_POWER 1
#define DP_LEN_PLAY_MODE 1
-#define DP_LEN_HAND_MODE 2
-#define DP_LEN_CALIBRATION 1
-#define DP_LEN_NUMBERTURNS 1
+#define DP_LEN_HAND_MODE 1
#define DP_LEN_ALM_LOW_BATTERY 1
+#define DP_LEN_BEEP_TRIG 1
#define DP_LEN_BATTERY 1
-#define DP_LEN_CHARGE 1
+#define DP_LEN_SHAKE_WAKE 1
#define DP_LEN_NO_DISTURB_ENABLE 13
-#define DP_LEN_RESERVA_DP1 4
-#define DP_LEN_RESERVA_DP2 4
+#define DP_LEN_CHARGE 1
#define DP_LEN_RESERVA_DP3 4
/* ════════════════════════════════════════════════════════════════
@@ -154,17 +149,25 @@ typedef enum
typedef enum
{
PLAY_MODE_0 = 0, /* 停止复位 */
- PLAY_MODE_1 = 1, /* 弹力伪装 */
- PLAY_MODE_2 = 2, /* 飞鸟俯冲 */
- PLAY_MODE_3 = 3, /* 云阶小陀螺 */
- PLAY_MODE_4 = 4, /* 起伏攻防战 */
- PLAY_MODE_5 = 5, /* 微风拂动 */
- PLAY_MODE_6 = 6, /* 闪电快闪 */
+ PLAY_MODE_1 = 1, /* 上部慢速往复 */
+ PLAY_MODE_2 = 2, /* 上部快速往复 */
+ PLAY_MODE_3 = 3, /* 上部间歇、下部均匀摇摆 */
+ PLAY_MODE_4 = 4, /* 上部慢探、下部随机摇摆 */
+ PLAY_MODE_5 = 5, /* 上下电机随机组合 */
} PLAY_MODE_t;
+/* 手动模式 */
+typedef enum
+{
+ HAND_MODE_0 = 0, /* 停止 */
+ HAND_MODE_1 = 1, /* 上部电机探出 */
+ HAND_MODE_2 = 2, /* 下部电机探出 */
+} HAND_MODE_t;
+
/* 充电状态 */
typedef enum
{
+ CHARGE_0 = 0, /* 不支持充电 */
CHARGE_1 = 1, /* 充电中 */
CHARGE_2 = 2, /* 未充电 */
} CHARGE_t;
@@ -178,16 +181,14 @@ typedef struct
{
uint8_t power; /* DP0 开关 */
uint8_t play_mode; /* DP1 自动模式 */
- uint8_t hand_mode[2]; /* DP2 手动模式 */
- uint8_t calibration; /* DP3 绳索校准 */
- uint8_t numberturns; /* DP4 绳索圈数 */
- uint8_t alm_low_battery;/* DP5 低电量报警 */
- uint8_t battery; /* DP6 电量上报 */
- uint8_t charge; /* DP7 充电状态 */
- uint8_t no_disturb_enable[13];/* DP8 勿扰模式开关 */
- uint32_t reserva_dp1; /* DP9 预留DP1 */
- uint32_t reserva_dp2; /* DP10 预留DP2 */
- uint32_t reserva_dp3; /* DP11 预留DP3 */
+ uint8_t hand_mode; /* DP2 手动模式 */
+ uint8_t alm_low_battery;/* DP3 低电量报警 */
+ uint8_t beep_trig; /* DP4 声音开关 */
+ uint8_t battery; /* DP5 电量上报 */
+ uint8_t shake_wake; /* DP6 震动触发开机 */
+ uint8_t no_disturb_enable[13];/* DP7 勿扰模式开关 */
+ uint8_t charge; /* DP8 充电状态 */
+ uint32_t reserva_dp3; /* DP10 预留DP3 */
} jb_data_point_t;
/* 控制事件信息 */
@@ -260,7 +261,7 @@ extern void ble_send_data(uint8_t *data, uint16_t len);
int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *data);
/* BLE → MCU 事件回调 */
-int8_t jbOnOtaNotify(uint32_t fwVersion, uint32_t fwLength, uint32_t fwCrc32); /* 0x30, 返回0=接受,非0=拒绝码 */
+void jbOnOtaNotify(uint32_t fwVersion, uint32_t fwLength, uint32_t fwCrc32); /* 0x30 */
void jbOnOtaData(const uint8_t *data, uint16_t len,
uint16_t shardIdx, uint16_t totalShards); /* 0x32 */
void jbOnOtaStop(void); /* 0x36 / 0x38 停止 */
diff --git a/apps/usr_jb_proto/usr_jb_main.c b/apps/usr_jb_proto/usr_jb_main.c
index b15c459..01667d4 100644
--- a/apps/usr_jb_proto/usr_jb_main.c
+++ b/apps/usr_jb_proto/usr_jb_main.c
@@ -1,9 +1,9 @@
/******************************************************************************
* @file usr_jb_main.c
- * @brief CBC2 业务组合根:只做“先后调用谁、谁通知谁”,不含具体业务算法
+ * @brief CBC1 猫猫捕猎罩业务组合根:只做调用编排,不含具体业务算法
* @author cyWu <1917507415@qq.com>
* @date 2026.08.27
- * @version V2.2.0
+ * @version V2.3.0
* @history
* - V1.0.0, 2026.08.21, cyWu, 首次发布
* - V1.1.0, 2026.08.24, cyWu, 收口未接线模块;DP2 仅第 1 字节点动 1s
@@ -16,11 +16,13 @@
* - V2.1.0, 2026.08.26, cyWu, 电机运转期间长期占用 1ms 硬件定时器中断,
* 配网连接后与 BLE 协议栈抢时序触发看门狗复位;
* 换向死区判定改用阈值检查而非精确计时,
- * 取消专门的 1ms 高频档,全系统统一 20ms 节拍,
+ * 取消专门的 1ms 高频档,全系统统一 10ms 节拍,
* 只保留"能不能休眠"这一个维度
* - V2.2.0, 2026.08.27, cyWu, app_led_init() 提前到 app_boot_wait_key() 之前,
* 开机 3s/5s/拒绝开机低电闪直接用灯效事件表,
* 不用再各自摸裸 GPIO 手搓一份闪烁定时
+ * - V2.3.0, 2026.08.31, cyWu, DP0=0 待机允许系统轻睡;震动检测并入现有
+ * 10ms 业务节拍,避免重复定时器唤醒
*
* @note 协议生成文件 jb_product.c 不要填业务。APP 下行写 gDevData。
* usr_timer_loop 由 BLE 回调,勿再注册第二份。
@@ -45,6 +47,7 @@
#include "usr_le_product.h"
#include "usr_le_api.h"
#include "bsp_hw.h"
+#include "board_pin.h"
#include "app_power.h"
#include "app_pair.h"
#include "app_boot.h"
@@ -69,7 +72,7 @@ const uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN] = {
JB_PRODUCT_CODE[3], JB_PRODUCT_CODE[4], JB_PRODUCT_CODE[5]
};
-#define USR_TIMER_IDLE_MS 20 /**< 全局统一节拍:按键消抖、换向死区判定都按它对齐 */
+#define USR_TIMER_IDLE_MS BOARD_SHAKE_POLL_MS /**< 按键、换向和震动检测共用同一节拍 */
/**
* @brief 业务节拍档位
@@ -78,7 +81,7 @@ const uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN] = {
* 单独开过 1ms 硬件定时器中断,配网连接后跟 BLE 协议栈自己的收发/ACK
* 处理抢时序,长时间叠加导致看门狗复位。
* 换向死区的判定是"距上次换向是否已过 switch_ms"这种阈值检查,不是
- * 精确计时:用 20ms 节拍去查,查到时实际已经过了不少于 20ms,天然
+ * 精确计时:用 10ms 节拍去查,查到时实际已经过了不少于 10ms,天然
* 满足"至少 1ms"的死区要求,只会让保护更充分,不会失效。所以死区
* 不需要专门的 1ms 精度,可以和其它业务共用同一个节拍。
*/
@@ -108,11 +111,20 @@ static void usr_on_power_evt(AppPowerEvt_t evt)
{
switch (evt) {
case APP_POWER_EVT_USB_IN:
- app_function_stop_all();
- log_info("usb in, motor stop\n");
+ if (!app_led_is_ota()) {
+ app_pair_stop();
+ usr_var.usr_goto_pair = 0;
+ app_function_soft_off();
+ log_info("usb in, charge standby\n");
+ } else {
+ app_function_stop_all();
+ log_info("usb in, keep ota\n");
+ }
break;
case APP_POWER_EVT_USB_OUT:
- app_function_soft_off();
+ if (!app_led_is_ota()) {
+ app_function_soft_off();
+ }
break;
default:
break;
@@ -161,6 +173,8 @@ static void usr_poweroff_finalize_hook(void)
*/
static UsrTickMode_t usr_timer_pick_mode(void)
{
+ /* DP0=0 仍保留低优先级业务节拍、BLE 和外部唤醒源,不需要禁止轻睡。
+ * 定时器到期会正常唤醒 CPU,按键/震动/USB 检测功能不会因此停掉。 */
if (bsp_key_is_pressed() || app_power_poweroff_pending()
|| app_mode_get_active() || bsp_motor_is_busy()
|| app_function_is_hand_busy()) {
@@ -194,7 +208,7 @@ static void usr_timer_rearm(UsrTickMode_t mode)
* 只有这一种,没有"任务线程 + 禁睡"的组合可选。
* 代价是这里执行的每一行代码都会占用中断时间,跟 BLE 协议栈自己的
* 收发中断抢时序;这次的看门狗复位就是因为这条路径长期以 1kHz
- * 触发。现在虽然降到了 20ms 一拍,但仍要守住底线:
+ * 触发。现在虽然降到了 10ms 一拍,但仍要守住底线:
* - 禁止阻塞等待(信号量/循环等 ACK 之类)
* - 禁止耗时操作(flash 擦写、大块 memcpy、长循环)
* 按键短按/长按走到的 usr_on_key_short/long、jbSendReqWithAck
@@ -291,6 +305,7 @@ void usr_jb_init(void)
user_key_init();
user_key_lock_if_held();
app_function_power_on_silent();
+ usr_on_power_evt(app_power_tick_1ms(0));
usr_timer_rearm(USR_TICK_IDLE);
log_info("timer idle %ums, sleep ok\n", (unsigned)USR_TIMER_IDLE_MS);
diff --git a/apps/usr_le_code/usr_le_adv.c b/apps/usr_le_code/usr_le_adv.c
index 6ea703f..00f2d33 100644
--- a/apps/usr_le_code/usr_le_adv.c
+++ b/apps/usr_le_code/usr_le_adv.c
@@ -66,6 +66,7 @@ void usr_ble_adv_init(void);
void usr_ble_adv_updata(void);
static int le_timer_handle = 0;
+static u8 ota_process_cnt;
/**
* @brief BLE 是否已连接
@@ -301,29 +302,27 @@ void usr_ble_adv_updata_OTA(void)
void usr_ota_process(void)
{
- static u8 cnt = 0;
-
- if (cnt == 0)
+ if (ota_process_cnt == 0)
{
ble_multi_trans_disconnect();
}
if (!usr_ble_is_connected())
{
- cnt++;
- if (cnt == 2)
+ ota_process_cnt++;
+ if (ota_process_cnt == 2)
{
usr_ble_adv_updata_OTA();
}
- if (cnt >= 60)
+ if (ota_process_cnt >= 60)
{
- cnt = 0;
+ ota_process_cnt = 0;
cpu_reset();
}
}
else
{
- cnt = 1;
+ ota_process_cnt = 1;
}
}
@@ -333,8 +332,23 @@ void usr_ota_process(void)
void usr_app_ota_init(void)
{
usr_var.usr_goto_updata = 1;
+ ota_process_cnt = 0;
if (usr_var.ota_timer == 0)
{
usr_var.ota_timer = sys_timer_add(NULL, usr_ota_process, 1000);
}
}
+
+void usr_app_ota_exit(void)
+{
+ if (usr_var.ota_timer) {
+ sys_timer_del((u16)usr_var.ota_timer);
+ usr_var.ota_timer = 0;
+ }
+ ota_process_cnt = 0;
+ if (usr_get_ota_status() == 0) {
+ usr_ota_exit();
+ }
+ usr_var.usr_goto_updata = 0;
+ usr_ble_disconnect_and_stop_adv();
+}
diff --git a/apps/usr_le_code/usr_le_api.h b/apps/usr_le_code/usr_le_api.h
index 5e7834a..5d2d583 100644
--- a/apps/usr_le_code/usr_le_api.h
+++ b/apps/usr_le_code/usr_le_api.h
@@ -54,6 +54,12 @@ uint8_t usr_get_pair_flag(void);
*/
void usr_app_ota_init(void);
+/** Stop the application OTA timer/advertising state without resetting. */
+void usr_app_ota_exit(void);
+
+/** Select normal or DP0 standby BLE connection parameters. */
+void usr_ble_set_standby(uint8_t standby);
+
#ifdef __cplusplus
}
#endif
diff --git a/apps/usr_periph/board_pin.h b/apps/usr_periph/board_pin.h
index 9ebb3b2..78e76b1 100644
--- a/apps/usr_periph/board_pin.h
+++ b/apps/usr_periph/board_pin.h
@@ -1,6 +1,6 @@
/******************************************************************************
* @file board_pin.h
- * @brief CBC2 门夹弹力绳板级引脚、电平与时序(换板只改本文件)
+ * @brief CBC1 猫猫捕猎罩板级引脚、电平与时序(换板只改本文件)
* @author cyWu <1917507415@qq.com>
* @date 2026.08.27
* @version V1.5.0
@@ -27,29 +27,28 @@
#define BOARD_LED_ENABLE 1 /**< 1=红绿 LED */
#define BOARD_KEY_ENABLE 1 /**< 1=按键 PB1 */
#define BOARD_CHG_ENABLE 1 /**< 1=充电检测 PG8 + VPWR */
-#define BOARD_MOTOR_ENABLE 1 /**< 1=电机 PA6/PC2 */
+#define BOARD_MOTOR_ENABLE 1 /**< 1=双电机 U13 + U18 */
+#define BOARD_SHAKE_ENABLE 1 /**< 1=震动输入 PA3 */
/*============================================================================*/
/* GPIO */
/*============================================================================*/
-#define BOARD_MOTOR_INA_PIN IO_PORTA_06 /**< 电机 INA */
-#define BOARD_MOTOR_INB_PIN IO_PORTC_02 /**< 电机 INB */
+#define BOARD_LEFT_FORWARD_PIN IO_PORTA_06 /**< U13-INA,左轮前进 */
+#define BOARD_LEFT_BACKWARD_PIN IO_PORTC_02 /**< U13-INB,左轮后退 */
+#define BOARD_RIGHT_BACKWARD_PIN IO_PORTC_03 /**< U18-INA,右轮后退 */
+#define BOARD_RIGHT_FORWARD_PIN IO_PORTC_04 /**< U18-INB,右轮前进 */
+#define BOARD_SHAKE_PIN IO_PORTA_03 /**< 震动开机,常闭开关断开时高脉冲有效 */
#define BOARD_LED_RED_PIN IO_PORTA_08 /**< 红灯 LED_PWR,低电平亮 */
#define BOARD_LED_GREEN_PIN IO_PORTA_07 /**< 绿灯 LED_CHG,低电平亮 */
#define BOARD_KEY_PIN IO_PORTB_01 /**< 按键,低电平按下 */
#define BOARD_CHG_PIN IO_PORTG_08 /**< ME4054 CHG,外部 2.2k 上拉 */
-/* PA3 限位未贴件,空脚不初始化 */
#define BOARD_LED_ON_LEVEL 0 /**< LED 点亮电平:0=低亮 */
#define BOARD_KEY_ACTIVE_LEVEL 0 /**< 按键有效电平:0=按下为低 */
#define BOARD_CHG_CHARGING_LEVEL 0 /**< CHG 充电中为低 */
-/**
- * 收绳方向:1 = INA 高 / INB 低 为收绳(顺时针)
- * 若实测方向反了,把本宏改为 0
- */
-#define BOARD_MOTOR_IN_INA_HIGH 1
+#define BOARD_SHAKE_ACTIVE_LEVEL 1 /**< 常闭震动开关:断开时高脉冲有效 */
/*============================================================================*/
/* 电量 / 充电 */
@@ -105,8 +104,8 @@
/*============================================================================*/
#define BOARD_MOTOR_REVERSE_MS 1 /**< 换向前先松开,防 H 桥直通 */
-#define BOARD_HAND_PULSE_MS 1000 /**< 预留;手动 1/2 一直转到写 03 停止 */
-#define BOARD_MOTOR_PWM_HZ 20000 /**< MCPWM 频率(Hz),20k 避开可听啸叫 */
+#define BOARD_HAND_PULSE_MS 5000 /**< 手动右转/左转单次最长 5s */
+#define BOARD_MOTOR_PWM_HZ 200 /**< 猫猫捕猎罩原项目验证过的 MCPWM 频率(Hz) */
#define BOARD_MOTOR_PWM_DUTY 8000 /**< 占空比 0~10000=0%~100%,80% */
#define BOARD_MOTOR_PWM_IDLE_CLOSE 1 /**< 1=空闲/轻睡关 MCPWM;0=保持开着,方便对比待机电流 */
@@ -114,6 +113,19 @@
/* 自动玩法 */
/*============================================================================*/
-#define BOARD_AUTO_MODE_MAX_MS (10u * 60u * 1000u) /**< 自动模式 1~6 单次最长运行时长,到点自动回待机 */
+#define BOARD_AUTO_MODE_MAX_MS (10u * 60u * 1000u) /**< 自动模式 1~5 单次最长运行时长,到点自动回待机 */
+
+/*============================================================================*/
+/* 震动开机 */
+/*============================================================================*/
+
+#define BOARD_SHAKE_REARM_MS 50u /**< 高电平稳定后重新武装 */
+#define BOARD_SHAKE_TRIGGER_EDGE_COUNT 1u /**< 验证阶段:单个高脉冲立即触发 */
+#define BOARD_SHAKE_TRIGGER_WINDOW_MS 250u /**< 保留窗口参数,便于恢复多脉冲策略 */
+#define BOARD_SHAKE_POLL_MS 10u /**< 与业务调度器共用的采样节拍 */
+
+/* PG8/CHG_ON is the only fitted USB-related signal that changes while the
+ * product is in softoff; VPWR is disconnected on boards with R18 not fitted. */
+#define BOARD_CHG_WAKEUP_INDEX 3u
#endif /* __BOARD_PIN_H__ */
diff --git a/apps/usr_periph/bsp_hw.c b/apps/usr_periph/bsp_hw.c
index 4762d46..d3e6639 100644
--- a/apps/usr_periph/bsp_hw.c
+++ b/apps/usr_periph/bsp_hw.c
@@ -1,6 +1,6 @@
/******************************************************************************
* @file bsp_hw.c
- * @brief CBC2 板级 GPIO / ADC:电机 H 桥、LED、按键、充电检测
+ * @brief CBC1 板级 GPIO / ADC:单电机 H 桥、震动、LED、按键、充电检测
* @author cyWu <1917507415@qq.com>
* @date 2026.08.24
* @version V1.5.0
@@ -28,21 +28,16 @@
#include "debug.h"
static uint8_t s_initialized;
-
-/** 电机状态,替代原来散落的 7 个 static */
-typedef struct {
- BspMotorDir_t dir; /**< 当前实际方向 */
- BspMotorDir_t pending; /**< 换向死区结束后要切到的方向 */
- uint16_t duty; /**< 当前实际占空比 */
- uint16_t pending_duty; /**< 换向死区结束后要用的占空比 */
- uint16_t switch_ms; /**< 换向死区剩余时长,0=空闲/不在换向中 */
- u32 switch_t0; /**< 换向死区起始时间戳 */
- uint8_t pwm_clk_on; /**< 1=MCPWM 时钟已打开 */
-} BspMotorCtx_t;
-
-static BspMotorCtx_t s_motor;
-
-extern u32 timer_get_ms(void);
+static uint8_t s_motor_active;
+static uint8_t s_pwm_clk_on;
+static uint8_t s_motor_switch_pending;
+static BspMotorDir_t s_left_direction;
+static BspMotorDir_t s_right_direction;
+static BspMotorDir_t s_pending_left_direction;
+static BspMotorDir_t s_pending_right_direction;
+static uint16_t s_pending_left_duty;
+static uint16_t s_pending_right_duty;
+static uint32_t s_motor_switch_started_ms;
#if (BOARD_LED_ENABLE || BOARD_MOTOR_ENABLE)
/**
@@ -60,7 +55,7 @@ static void bsp_pin_output(u32 pin, int value)
}
#endif
-#if (BOARD_KEY_ENABLE || BOARD_CHG_ENABLE)
+#if (BOARD_KEY_ENABLE || BOARD_CHG_ENABLE || BOARD_SHAKE_ENABLE)
/**
* @brief 配置 GPIO 为数字输入(可内部上拉)
* @param pin GPIO 编号
@@ -78,40 +73,50 @@ static void bsp_pin_input(u32 pin, int pull_up)
#endif
#if BOARD_MOTOR_ENABLE
+static void bsp_motor_pwm_channel_init(pwm_ch_num_type channel, u8 pin)
+{
+ struct pwm_platform_data pwm;
+
+ memset(&pwm, 0, sizeof(pwm));
+ pwm.pwm_aligned_mode = pwm_edge_aligned;
+ pwm.pwm_ch_num = channel;
+ pwm.frequency = BOARD_MOTOR_PWM_HZ;
+ pwm.duty = 0;
+ pwm.h_pin = pin;
+ pwm.l_pin = (u8) - 1;
+ pwm.complementary_en = 0;
+ mcpwm_init(&pwm);
+}
+
/**
* @brief 初始化 INA/INB 两路 MCPWM,初始占空比 0(停转)
* @return 无
*/
static void bsp_motor_pwm_init(void)
{
- struct pwm_platform_data pwm;
+ const u32 pins[] = {
+ BOARD_LEFT_FORWARD_PIN, BOARD_LEFT_BACKWARD_PIN,
+ BOARD_RIGHT_BACKWARD_PIN, BOARD_RIGHT_FORWARD_PIN
+ };
+ uint8_t i;
- gpio_set_pull_up(BOARD_MOTOR_INA_PIN, 0);
- gpio_set_pull_down(BOARD_MOTOR_INA_PIN, 0);
- gpio_set_die(BOARD_MOTOR_INA_PIN, 1);
- gpio_set_pull_up(BOARD_MOTOR_INB_PIN, 0);
- gpio_set_pull_down(BOARD_MOTOR_INB_PIN, 0);
- gpio_set_die(BOARD_MOTOR_INB_PIN, 1);
+ for (i = 0; i < ARRAY_SIZE(pins); i++) {
+ gpio_set_pull_up(pins[i], 0);
+ gpio_set_pull_down(pins[i], 0);
+ gpio_set_die(pins[i], 1);
+ }
- pwm.pwm_aligned_mode = pwm_edge_aligned;
- pwm.frequency = BOARD_MOTOR_PWM_HZ;
- pwm.duty = 0;
- pwm.l_pin = (u8) - 1;
- pwm.complementary_en = 0;
-
- pwm.pwm_ch_num = pwm_ch0;
- pwm.h_pin = BOARD_MOTOR_INA_PIN;
- mcpwm_init(&pwm);
-
- pwm.pwm_ch_num = pwm_ch1;
- pwm.h_pin = BOARD_MOTOR_INB_PIN;
- mcpwm_init(&pwm);
+ bsp_motor_pwm_channel_init(pwm_ch0, BOARD_LEFT_FORWARD_PIN);
+ bsp_motor_pwm_channel_init(pwm_ch1, BOARD_LEFT_BACKWARD_PIN);
+ bsp_motor_pwm_channel_init(pwm_ch2, BOARD_RIGHT_BACKWARD_PIN);
+ bsp_motor_pwm_channel_init(pwm_ch3, BOARD_RIGHT_FORWARD_PIN);
mcpwm_set_duty(pwm_ch0, 0);
mcpwm_set_duty(pwm_ch1, 0);
- s_motor.pwm_clk_on = 1;
- log_info("motor pwm %uHz duty=%u/10000\n",
- (unsigned)BOARD_MOTOR_PWM_HZ, (unsigned)BOARD_MOTOR_PWM_DUTY);
+ mcpwm_set_duty(pwm_ch2, 0);
+ mcpwm_set_duty(pwm_ch3, 0);
+ s_pwm_clk_on = 1;
+ log_info("dual motor pwm %uHz\n", (unsigned)BOARD_MOTOR_PWM_HZ);
}
/**
@@ -120,16 +125,22 @@ static void bsp_motor_pwm_init(void)
*/
static void bsp_motor_pwm_clock_on(void)
{
- if (s_motor.pwm_clk_on) {
+ if (s_pwm_clk_on) {
return;
}
mcpwm_open(pwm_ch0);
mcpwm_open(pwm_ch1);
- gpio_och_sel_output_signal(BOARD_MOTOR_INA_PIN, OUTPUT_CH_SIGNAL_MC_PWM0_H);
- gpio_och_sel_output_signal(BOARD_MOTOR_INB_PIN, OUTPUT_CH_SIGNAL_MC_PWM1_H);
- gpio_set_direction(BOARD_MOTOR_INA_PIN, 0);
- gpio_set_direction(BOARD_MOTOR_INB_PIN, 0);
- s_motor.pwm_clk_on = 1;
+ mcpwm_open(pwm_ch2);
+ mcpwm_open(pwm_ch3);
+ gpio_och_sel_output_signal(BOARD_LEFT_FORWARD_PIN, OUTPUT_CH_SIGNAL_MC_PWM0_H);
+ gpio_och_sel_output_signal(BOARD_LEFT_BACKWARD_PIN, OUTPUT_CH_SIGNAL_MC_PWM1_H);
+ gpio_och_sel_output_signal(BOARD_RIGHT_BACKWARD_PIN, OUTPUT_CH_SIGNAL_MC_PWM2_H);
+ gpio_och_sel_output_signal(BOARD_RIGHT_FORWARD_PIN, OUTPUT_CH_SIGNAL_MC_PWM3_H);
+ gpio_set_direction(BOARD_LEFT_FORWARD_PIN, 0);
+ gpio_set_direction(BOARD_LEFT_BACKWARD_PIN, 0);
+ gpio_set_direction(BOARD_RIGHT_BACKWARD_PIN, 0);
+ gpio_set_direction(BOARD_RIGHT_FORWARD_PIN, 0);
+ s_pwm_clk_on = 1;
}
/**
@@ -140,61 +151,63 @@ static void bsp_motor_pwm_clock_off(void)
{
mcpwm_set_duty(pwm_ch0, 0);
mcpwm_set_duty(pwm_ch1, 0);
- if (s_motor.pwm_clk_on) {
+ mcpwm_set_duty(pwm_ch2, 0);
+ mcpwm_set_duty(pwm_ch3, 0);
+ if (s_pwm_clk_on) {
mcpwm_close(pwm_ch0);
mcpwm_close(pwm_ch1);
- gpio_och_disable_output_signal(BOARD_MOTOR_INA_PIN, OUTPUT_CH_SIGNAL_MC_PWM0_H);
- gpio_och_disable_output_signal(BOARD_MOTOR_INB_PIN, OUTPUT_CH_SIGNAL_MC_PWM1_H);
+ mcpwm_close(pwm_ch2);
+ mcpwm_close(pwm_ch3);
+ gpio_och_disable_output_signal(BOARD_LEFT_FORWARD_PIN, OUTPUT_CH_SIGNAL_MC_PWM0_H);
+ gpio_och_disable_output_signal(BOARD_LEFT_BACKWARD_PIN, OUTPUT_CH_SIGNAL_MC_PWM1_H);
+ gpio_och_disable_output_signal(BOARD_RIGHT_BACKWARD_PIN, OUTPUT_CH_SIGNAL_MC_PWM2_H);
+ gpio_och_disable_output_signal(BOARD_RIGHT_FORWARD_PIN, OUTPUT_CH_SIGNAL_MC_PWM3_H);
JL_MCPWM->MCPWM_CON0 = 0;
- s_motor.pwm_clk_on = 0;
+ s_pwm_clk_on = 0;
}
- gpio_direction_output(BOARD_MOTOR_INA_PIN, 0);
- gpio_direction_output(BOARD_MOTOR_INB_PIN, 0);
+ gpio_direction_output(BOARD_LEFT_FORWARD_PIN, 0);
+ gpio_direction_output(BOARD_LEFT_BACKWARD_PIN, 0);
+ gpio_direction_output(BOARD_RIGHT_BACKWARD_PIN, 0);
+ gpio_direction_output(BOARD_RIGHT_FORWARD_PIN, 0);
}
-/**
- * @brief 立刻改 INA/INB PWM。运转脚输出 duty,另一脚 0%;STOP=两路 0%
- * @param dir 目标方向
- * @param duty 占空比 0~10000
- * @return 无
- */
-static void bsp_motor_gpio(BspMotorDir_t dir, uint16_t duty)
+static void bsp_drive_gpio(BspMotorDir_t left_dir, uint16_t left_duty,
+ BspMotorDir_t right_dir, uint16_t right_duty)
{
- u16 ina_duty = 0;
- u16 inb_duty = 0;
-
- if (duty > 10000) {
- duty = 10000;
+ if (left_duty > 10000) {
+ left_duty = 10000;
+ }
+ if (right_duty > 10000) {
+ right_duty = 10000;
+ }
+ if (left_dir == BSP_MOTOR_STOP) {
+ left_duty = 0;
+ }
+ if (right_dir == BSP_MOTOR_STOP) {
+ right_duty = 0;
}
- if (dir == BSP_MOTOR_IN) {
- if (BOARD_MOTOR_IN_INA_HIGH) {
- ina_duty = duty;
- } else {
- inb_duty = duty;
- }
- } else if (dir == BSP_MOTOR_OUT) {
- if (BOARD_MOTOR_IN_INA_HIGH) {
- inb_duty = duty;
- } else {
- ina_duty = duty;
- }
- }
+ /* Crazy Car's proven driver clears all four channels at every action
+ * boundary, then applies the two wheel outputs as one atomic command. */
+ mcpwm_set_duty(pwm_ch0, 0);
+ mcpwm_set_duty(pwm_ch1, 0);
+ mcpwm_set_duty(pwm_ch2, 0);
+ mcpwm_set_duty(pwm_ch3, 0);
- if ((ina_duty == 0) && (inb_duty == 0)) {
- /* 换向死区(BOARD_MOTOR_REVERSE_MS)、刹车瞬间也会短暂占空比为 0,
- * 但马上(1~30ms 内)还要再驱动,不能当成“真空闲”去关时钟:
- * 频繁 close/open MCPWM 外设比单纯调占空比激进得多,玩法换向越快
- * 关得越勤,有卡死风险。这里只清占空比(两脚拉低,电气效果一样),
- * 真正的空闲关时钟统一交给 bsp_hw_sleep_pwm_off()——它在系统真的
- * 要进轻睡前才调用,并且会先查 bsp_motor_is_busy()。 */
- mcpwm_set_duty(pwm_ch0, 0);
- mcpwm_set_duty(pwm_ch1, 0);
- return;
+ s_motor_active = (uint8_t)(left_duty || right_duty);
+ if (s_motor_active) {
+ bsp_motor_pwm_clock_on();
+ }
+ if (left_dir == BSP_MOTOR_FORWARD) {
+ mcpwm_set_duty(pwm_ch0, left_duty);
+ } else if (left_dir == BSP_MOTOR_BACKWARD) {
+ mcpwm_set_duty(pwm_ch1, left_duty);
+ }
+ if (right_dir == BSP_MOTOR_FORWARD) {
+ mcpwm_set_duty(pwm_ch3, right_duty);
+ } else if (right_dir == BSP_MOTOR_BACKWARD) {
+ mcpwm_set_duty(pwm_ch2, right_duty);
}
- bsp_motor_pwm_clock_on();
- mcpwm_set_duty(pwm_ch0, ina_duty);
- mcpwm_set_duty(pwm_ch1, inb_duty);
}
#endif
@@ -231,20 +244,19 @@ BspHw_Ret_t bsp_hw_init(void)
#if BOARD_MOTOR_ENABLE
bsp_motor_pwm_init();
- s_motor.dir = BSP_MOTOR_STOP;
- s_motor.pending = BSP_MOTOR_STOP;
- s_motor.duty = 0;
- s_motor.pending_duty = 0;
- s_motor.switch_ms = 0;
+ s_motor_active = 0;
+ s_motor_switch_pending = 0;
+ s_left_direction = BSP_MOTOR_STOP;
+ s_right_direction = BSP_MOTOR_STOP;
#if BOARD_MOTOR_PWM_IDLE_CLOSE
bsp_motor_pwm_clock_off();
#endif
#endif
s_initialized = 1;
- log_info("hw init led=%d key=%d chg=%d motor=%d\n",
+ log_info("hw init led=%d key=%d chg=%d motor=%d shake=%d\n",
BOARD_LED_ENABLE, BOARD_KEY_ENABLE, BOARD_CHG_ENABLE,
- BOARD_MOTOR_ENABLE);
+ BOARD_MOTOR_ENABLE, BOARD_SHAKE_ENABLE);
return BSP_HW_OK;
}
@@ -295,101 +307,93 @@ void bsp_led_all_off(void)
bsp_led_green_set(0);
}
-/**
- * @brief 设置电机方向。STOP 时直接切断驱动,自由滑行到停(不做反接刹车)
- * @param dir 收绳 / 放绳 / 停转
- * @return 无
- */
-void bsp_motor_set(BspMotorDir_t dir)
-{
- bsp_motor_set_pwm(dir, (dir == BSP_MOTOR_STOP) ? 0 : BOARD_MOTOR_PWM_DUTY);
-}
-
-/**
- * @brief 设置电机方向与 PWM 占空比
- * @param dir 收绳 / 放绳 / 停转
- * @param duty 占空比 0~10000;STOP 时按 0 处理
- * @return 无
- */
-void bsp_motor_set_pwm(BspMotorDir_t dir, uint16_t duty)
+void bsp_drive_set(BspMotorDir_t left_dir, uint16_t left_duty,
+ BspMotorDir_t right_dir, uint16_t right_duty)
{
#if !BOARD_MOTOR_ENABLE
- (void)dir;
- (void)duty;
- return;
+ (void)left_dir;
+ (void)left_duty;
+ (void)right_dir;
+ (void)right_duty;
#else
- if (duty > 10000) {
- duty = 10000;
+ uint8_t left_reversing;
+ uint8_t right_reversing;
+
+ if (left_dir < BSP_MOTOR_BACKWARD || left_dir > BSP_MOTOR_FORWARD ||
+ right_dir < BSP_MOTOR_BACKWARD || right_dir > BSP_MOTOR_FORWARD) {
+ return;
}
- if (dir == BSP_MOTOR_STOP) {
- duty = 0;
+ if (left_dir == BSP_MOTOR_STOP) {
+ left_duty = 0;
+ }
+ if (right_dir == BSP_MOTOR_STOP) {
+ right_duty = 0;
}
- /* 同向只改占空比:直接改 PWM,不走换向死区 */
- if (dir == s_motor.dir && s_motor.switch_ms == 0) {
- if (duty == s_motor.duty) {
- return;
- }
- s_motor.duty = duty;
- s_motor.pending = dir;
- s_motor.pending_duty = duty;
- bsp_motor_gpio(dir, duty);
+ if (left_duty == 0 && right_duty == 0) {
+ s_motor_switch_pending = 0;
+ /* Keep the last energized directions so an immediate opposite command
+ * still observes the H-bridge dead time. */
+ bsp_drive_gpio(BSP_MOTOR_STOP, 0, BSP_MOTOR_STOP, 0);
return;
}
- /* 停转:直接切断驱动,让电机自由滑行到停,不做反接刹车——反接刹车电流比
- * 正常驱动大(电池要同时顶住反向驱动电流和电机反电动势),实测会把供电轨
- * 拉低触发 LVD 复位。哪怕正在换向死区里,也直接取消死区改成停。 */
- if (dir == BSP_MOTOR_STOP) {
- s_motor.switch_ms = 0;
- s_motor.dir = BSP_MOTOR_STOP;
- s_motor.pending = BSP_MOTOR_STOP;
- s_motor.duty = 0;
- s_motor.pending_duty = 0;
- bsp_motor_gpio(BSP_MOTOR_STOP, 0);
+ if (s_motor_switch_pending) {
+ s_pending_left_direction = left_dir;
+ s_pending_left_duty = left_duty;
+ s_pending_right_direction = right_dir;
+ s_pending_right_duty = right_duty;
return;
}
- /* 换向:先松开再输出,避免 H 桥直通 */
- if (s_motor.dir != BSP_MOTOR_STOP && dir != s_motor.dir) {
- bsp_motor_gpio(BSP_MOTOR_STOP, 0);
- s_motor.pending = dir;
- s_motor.pending_duty = duty;
- s_motor.switch_ms = BOARD_MOTOR_REVERSE_MS;
- s_motor.switch_t0 = timer_get_ms();
- s_motor.dir = BSP_MOTOR_STOP;
- s_motor.duty = 0;
+ left_reversing = (uint8_t)(s_left_direction != BSP_MOTOR_STOP &&
+ left_dir != BSP_MOTOR_STOP &&
+ left_dir != s_left_direction);
+ right_reversing = (uint8_t)(s_right_direction != BSP_MOTOR_STOP &&
+ right_dir != BSP_MOTOR_STOP &&
+ right_dir != s_right_direction);
+ if (left_reversing || right_reversing) {
+ bsp_drive_gpio(BSP_MOTOR_STOP, 0, BSP_MOTOR_STOP, 0);
+ s_pending_left_direction = left_dir;
+ s_pending_left_duty = left_duty;
+ s_pending_right_direction = right_dir;
+ s_pending_right_duty = right_duty;
+ s_motor_switch_started_ms = timer_get_ms();
+ s_motor_switch_pending = 1;
return;
}
- s_motor.switch_ms = 0;
- s_motor.dir = dir;
- s_motor.pending = dir;
- s_motor.duty = duty;
- s_motor.pending_duty = duty;
- bsp_motor_gpio(dir, duty);
+ bsp_drive_gpio(left_dir, left_duty, right_dir, right_duty);
+ s_left_direction = left_duty ? left_dir : BSP_MOTOR_STOP;
+ s_right_direction = right_duty ? right_dir : BSP_MOTOR_STOP;
#endif
}
+void bsp_drive_stop(void)
+{
+ bsp_drive_set(BSP_MOTOR_STOP, 0, BSP_MOTOR_STOP, 0);
+}
+
/**
* @brief 推进换向死区计时,按真实毫秒计时;死区结束后切到目标方向/占空比
* @return 无
*/
void bsp_motor_switch_tick(void)
{
-#if !BOARD_MOTOR_ENABLE
- return;
-#else
- if (s_motor.switch_ms == 0) {
+#if BOARD_MOTOR_ENABLE
+ if (!s_motor_switch_pending ||
+ (uint32_t)(timer_get_ms() - s_motor_switch_started_ms) <
+ BOARD_MOTOR_REVERSE_MS) {
return;
}
- if ((timer_get_ms() - s_motor.switch_t0) < s_motor.switch_ms) {
- return;
- }
- s_motor.switch_ms = 0;
- s_motor.dir = s_motor.pending;
- s_motor.duty = s_motor.pending_duty;
- bsp_motor_gpio(s_motor.dir, s_motor.duty);
+
+ s_motor_switch_pending = 0;
+ bsp_drive_gpio(s_pending_left_direction, s_pending_left_duty,
+ s_pending_right_direction, s_pending_right_duty);
+ s_left_direction = s_pending_left_duty ?
+ s_pending_left_direction : BSP_MOTOR_STOP;
+ s_right_direction = s_pending_right_duty ?
+ s_pending_right_direction : BSP_MOTOR_STOP;
#endif
}
@@ -450,10 +454,15 @@ uint8_t bsp_vpwr_is_online(void)
if (mv >= BOARD_VPWR_ONLINE_MV) {
return 1;
}
+ /* LDO5V_DET is valid immediately after a charge wake, before the ADC has
+ * produced a stable sample. Unlike LVCMP it also drops promptly on pull. */
+ if (get_ldo5v_online_hw()) {
+ return 1;
+ }
if (mv <= BOARD_VPWR_OFF_MV) {
return 0;
}
- if (get_ldo5v_online_hw() || get_lvcmp_det()) {
+ if (get_lvcmp_det()) {
return 1;
}
return 0;
@@ -488,16 +497,7 @@ uint8_t bsp_motor_is_busy(void)
#if !BOARD_MOTOR_ENABLE
return 0;
#else
- if (s_motor.switch_ms) {
- return 1;
- }
- if (s_motor.dir != BSP_MOTOR_STOP) {
- return 1;
- }
- if (s_motor.pending != BSP_MOTOR_STOP) {
- return 1;
- }
- return 0;
+ return (uint8_t)(s_motor_active || s_motor_switch_pending);
#endif
}
@@ -507,13 +507,11 @@ uint8_t bsp_motor_is_busy(void)
*/
void bsp_hw_enter_sleep_io(void)
{
- bsp_motor_set(BSP_MOTOR_STOP);
+ bsp_drive_stop();
#if BOARD_MOTOR_ENABLE
- s_motor.switch_ms = 0;
- s_motor.dir = BSP_MOTOR_STOP;
- s_motor.pending = BSP_MOTOR_STOP;
- s_motor.duty = 0;
+ s_motor_active = 0;
bsp_motor_pwm_clock_off();
#endif
bsp_led_all_off();
+
}
diff --git a/apps/usr_periph/bsp_hw.h b/apps/usr_periph/bsp_hw.h
index d3b4eb1..d6cbe1b 100644
--- a/apps/usr_periph/bsp_hw.h
+++ b/apps/usr_periph/bsp_hw.h
@@ -1,6 +1,6 @@
/******************************************************************************
* @file bsp_hw.h
- * @brief CBC2 板级硬件:电机 / LED / 按键 / 充电 / 电量
+ * @brief CBC1 板级硬件:单电机 / 震动 / LED / 按键 / 充电 / 电量
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
* @version V1.6.0
@@ -27,9 +27,9 @@ typedef enum {
} BspHw_Ret_t;
typedef enum {
- BSP_MOTOR_STOP = 0, /**< 滑行停(INA/INB 都低) */
- BSP_MOTOR_IN, /**< 收绳(顺时针,见 BOARD_MOTOR_IN_INA_HIGH) */
- BSP_MOTOR_OUT /**< 放绳 */
+ BSP_MOTOR_BACKWARD = -1,
+ BSP_MOTOR_STOP = 0,
+ BSP_MOTOR_FORWARD = 1
} BspMotorDir_t;
/**
@@ -70,24 +70,20 @@ void bsp_led_all_off(void);
* @param dir 收绳 / 放绳 / 停转
* @return 无
*/
-void bsp_motor_set(BspMotorDir_t dir);
+void bsp_drive_set(BspMotorDir_t left_dir, uint16_t left_duty,
+ BspMotorDir_t right_dir, uint16_t right_duty);
+
+/** 停止左右轮,四路 PWM 全部清零。 */
+void bsp_drive_stop(void);
/**
- * @brief 设置电机方向与 PWM 占空比
- * @param dir 收绳 / 放绳 / 停转
- * @param duty 占空比 0~10000(0%~100%);STOP 时忽略
- * @return 无
- */
-void bsp_motor_set_pwm(BspMotorDir_t dir, uint16_t duty);
-
-/**
- * @brief 推进换向死区计时,按真实毫秒计时;死区结束后切到目标方向/占空比
+ * @brief 兼容旧调用;猫猫捕猎罩每个动作更新左轮对应的两路 PWM
* @return 无
*/
void bsp_motor_switch_tick(void);
/**
- * @brief 电机是否还在转或处于换向死区
+ * @brief 电机是否还在转
* @return 1=忙碌,0=已停且 PWM 已关
*/
uint8_t bsp_motor_is_busy(void);
diff --git a/cpu/br28/sdk.ld b/cpu/br28/sdk.ld
index e05c972..c3fe2e9 100644
--- a/cpu/br28/sdk.ld
+++ b/cpu/br28/sdk.ld
@@ -3,6 +3,7 @@
+
lp_signature_set = ABSOLUTE(0x1fd6c);
memmem = ABSOLUTE(0x1fd70);
memcpy = ABSOLUTE(0x1fd74);
@@ -240,6 +241,7 @@
_MASK_MEM_BEGIN = ABSOLUTE(0x19fc00);
_MASK_MEM_SIZE = ABSOLUTE(0x1a4);
+
EXTERN(
_start
@@ -269,6 +271,7 @@ pcm_decoder
mp3_decoder
+
);
UPDATA_SIZE = 0x80;
@@ -404,7 +407,8 @@ SECTIONS
battery_notify_begin = .;
*(.battery_notify)
battery_notify_end = .;
- . = ALIGN(4);
+
+ . = ALIGN(4);
__VERSION_BEGIN = .;
KEEP(*(.sys.version))
__VERSION_END = .;
@@ -494,6 +498,7 @@ SECTIONS
*(.audio_track_data)
*(.audio_adc_data)
+
. = ALIGN(4);
*(.data*)
@@ -725,6 +730,7 @@ SECTIONS
} > ram0
+
data_code_pc_limit_end = .;
__report_overlay_end = .;
@@ -789,6 +795,7 @@ SECTIONS
}
+
SECTIONS
{
.data : ALIGN(4)
@@ -820,6 +827,7 @@ SECTIONS
UPDATE_CODE_TOTAL_SIZE = update_code_end - update_code_start;
}
+
SECTIONS
{
.data : ALIGN(4)
@@ -922,6 +930,7 @@ BTSTACK_LE_HOST_MESH_RAM_TOTAL = BTSTACK_LE_HOST_MESH_DATA_SIZE + BTSTACK_LE_HOS
BTSTACK_LE_HOST_MESH_FLASH_TOTAL = BTSTACK_LE_HOST_MESH_CODE_SIZE;
BTSTACK_CODE_SIZE = (btstack_code_end - btstack_code_start) + (btstack_data_end - btstack_data_start);
+
SECTIONS
{
.data : ALIGN(4)
@@ -1215,6 +1224,7 @@ SECTIONS
*(.os_code)
} > ram0
}
+
SECTIONS
{
.data : ALIGN(4)
@@ -1424,6 +1434,7 @@ SECTIONS
BTCTLER_RAM_TOTAL = (btctler_data_end - btctler_data_start) + (btctler_bss_end - btctler_bss_start);
BTCTLER_CODE_TOTAL = (btctler_code_end - btctler_code_start);
}
+
SECTIONS
{
.data : ALIGN(4)
@@ -1541,7 +1552,8 @@ SECTIONS
*(.timer.text.cache.L1)
*(.gpio.text.cache.L1)
*(.iic_hw.text.cache.L1)
- driver_data_code_end = .;
+
+ driver_data_code_end = .;
. = ALIGN(4);
} > ram0
@@ -1551,6 +1563,7 @@ SECTIONS
DRIVER_DATA_CODE_TOTAL = (driver_data_code_end - driver_data_code_start);
}
+
SECTIONS
{
.data : ALIGN(4)
@@ -1990,6 +2003,7 @@ SECTIONS
} > ram0
}
+
text_begin = ADDR(.text);
text_size = SIZEOF(.text);
text_end = text_begin + text_size;
diff --git a/cpu/br28/sdk_used_list.used b/cpu/br28/sdk_used_list.used
index 79b91ea..7afd478 100644
--- a/cpu/br28/sdk_used_list.used
+++ b/cpu/br28/sdk_used_list.used
@@ -2,6 +2,7 @@
+
sdfile_vfs_ops
sbc_decoder
diff --git a/cpu/br28/tools/download.bat b/cpu/br28/tools/download.bat
index 64f3c15..57ccb0c 100644
--- a/cpu/br28/tools/download.bat
+++ b/cpu/br28/tools/download.bat
@@ -3,6 +3,7 @@
+
@echo off
Setlocal enabledelayedexpansion
@echo ********************************************************************************
diff --git a/cpu/br28/tools/download/earphone/download_app_ota.bat b/cpu/br28/tools/download/earphone/download_app_ota.bat
index 41fa64f..2efa9d0 100644
--- a/cpu/br28/tools/download/earphone/download_app_ota.bat
+++ b/cpu/br28/tools/download/earphone/download_app_ota.bat
@@ -10,57 +10,37 @@ copy ..\..\ota.bin .
copy ..\..\anc_coeff.bin .
copy ..\..\anc_gains.bin .
-set "ISD_CMD=..\..\isd_download.exe ..\..\isd_config.ini -dev br28 -boot 0x120000 -div8 -wait 300 -uboot ..\..\uboot.boot -app ..\..\app.bin -res ..\..\cfg_tool.bin tone.cfg p11_code.bin -uboot_compress"
+if not exist p11_code.bin (
+ echo [download] ERROR: ..\..\p11_code.bin is missing
+ exit /b 1
+)
-REM JL_BURN_KEY=1/key : USB flash keyed image; still generate nokey files
-REM default / 0 / nokey : USB flash nokey image; still generate keyed files
-set "NOKEY_FLASH="
-set "KEY_FLASH="
-if /i "%JL_BURN_KEY%"=="1" goto :usb_key
-if /i "%JL_BURN_KEY%"=="key" goto :usb_key
-if /i "%JL_BURN_KEY%"=="yes" goto :usb_key
-set "NOKEY_FLASH=-tonorflash"
-echo [download] USB burn: NO key
-goto :do_isd
+set "ISD_CMD=..\..\isd_download.exe ..\..\isd_config.ini -tonorflash -dev br28 -boot 0x120000 -div8 -wait 300 -uboot ..\..\uboot.boot -app ..\..\app.bin -res ..\..\cfg_tool.bin tone.cfg p11_code.bin -uboot_compress"
-:usb_key
-set "KEY_FLASH=-tonorflash"
-echo [download] USB burn: WITH key
+if not exist RS-5B88.key (
+ echo [download] ERROR: RS-5B88.key not found
+ exit /b 1
+)
-:do_isd
-REM ---- nokey image ----
-%ISD_CMD% %NOKEY_FLASH%
-if exist jl_isd.fw copy /y jl_isd.fw jl_isd_nokey.fw >nul
+echo [download] USB burn: WITH key RS-5B88.key
+if exist jl_isd.fw del jl_isd.fw
+if exist jl_isd.ufw del jl_isd.ufw
+%ISD_CMD% -key RS-5B88.key
+if not exist jl_isd.fw (
+ echo [download] ERROR: jl_isd.fw was not generated
+ exit /b 1
+)
+
+copy /y jl_isd.fw jl_isd_key.fw >nul
..\..\ufw_maker.exe -fw_to_ufw jl_isd.fw
-if exist jl_isd.ufw (
- copy /y jl_isd.ufw update.ufw >nul
- copy /y jl_isd.ufw update_nokey.ufw >nul
- del jl_isd.ufw
+if not exist jl_isd.ufw (
+ echo [download] ERROR: jl_isd.ufw was not generated
+ exit /b 1
)
-if exist db_update_data.bin copy /y db_update_data.bin db_update_data_nokey.bin >nul
-
-REM ---- keyed image ----
-if exist RS-5B88.key (
- %ISD_CMD% %KEY_FLASH% -key RS-5B88.key
- REM isd_download returns -11 when USB is offline; image is still generated
- if exist jl_isd.fw copy /y jl_isd.fw jl_isd_key.fw >nul
- ..\..\ufw_maker.exe -fw_to_ufw jl_isd.fw
- if exist jl_isd.ufw (
- copy /y jl_isd.ufw update_key.ufw >nul
- del jl_isd.ufw
- )
- if exist db_update_data.bin copy /y db_update_data.bin db_update_data_key.bin >nul
-) else (
- if /i not "%KEY_FLASH%"=="" (
- echo [download] ERROR: RS-5B88.key not found, cannot USB-burn keyed image
- exit /b 1
- )
-)
-
-REM keep default names as nokey for tools that still look for jl_isd.fw
-if exist jl_isd_nokey.fw copy /y jl_isd_nokey.fw jl_isd.fw >nul
-if exist update_nokey.ufw copy /y update_nokey.ufw update.ufw >nul
-if exist db_update_data_nokey.bin copy /y db_update_data_nokey.bin db_update_data.bin >nul
+copy /y jl_isd.ufw update.ufw >nul
+copy /y jl_isd.ufw update_key.ufw >nul
+del jl_isd.ufw
+if exist db_update_data.bin copy /y db_update_data.bin db_update_data_key.bin >nul
@rem delete temp files
if exist *.mp3 del *.mp3
diff --git a/cpu/br28/tools/p11_code.bin b/cpu/br28/tools/p11_code.bin
new file mode 100644
index 0000000..3ee174f
Binary files /dev/null and b/cpu/br28/tools/p11_code.bin differ
diff --git a/dp_definition.json b/dp_definition.json
index b0e2f57..30b369e 100644
--- a/dp_definition.json
+++ b/dp_definition.json
@@ -1,8 +1,8 @@
{
"product": {
- "name": "门夹弹力绳",
- "code": "PICBC2",
- "type": "CBC2",
+ "name": "弹力魔盒",
+ "code": "PICBC4",
+ "type": "CBC4",
"protocol": "V1.0.0"
},
"dps": [
@@ -30,15 +30,14 @@
"len": 1,
"scale": 1.0,
"dir": "rw",
- "description": "6种玩法,按键也可切换并上报",
+ "description": "5种玩法,按键也可切换并上报",
"values": {
"0": "停止复位",
- "1": "弹力伪装",
- "2": "飞鸟俯冲",
- "3": "云阶小陀螺",
- "4": "起伏攻防战",
- "5": "微风拂动",
- "6": "闪电快闪"
+ "1": "随机躲猫猫",
+ "2": "洞口接力跑",
+ "3": "快闪+快摇组合",
+ "4": "慢探+随机摇摆",
+ "5": "终极大乱斗"
}
},
{
@@ -46,12 +45,16 @@
"display_name": "手动模式",
"key": "hand_mode",
"kind": "normal",
- "type": "raw",
- "len": 2,
+ "type": "enum",
+ "len": 1,
"scale": 1.0,
"dir": "wo",
- "description": "预留力度和百分比看后面能否实现",
- "values": {}
+ "description": "上部或下部电机单次探出,最长5秒",
+ "values": {
+ "0": "停止",
+ "1": "上部探出",
+ "2": "下部探出"
+ }
},
{
"id": 3,
@@ -172,4 +175,4 @@
"values": {}
}
]
-}
\ No newline at end of file
+}
diff --git a/tools/pack_firmware.ps1 b/tools/pack_firmware.ps1
index 9800d07..8df9a97 100644
--- a/tools/pack_firmware.ps1
+++ b/tools/pack_firmware.ps1
@@ -9,7 +9,7 @@
[CmdletBinding()]
param(
- [string]$ProjectName = "PICBC2",
+ [string]$ProjectName = "PICBC4",
[string]$SdkRoot = ""
)