Compare commits
5
Commits
215e1be6bb
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6c2c6dd52 | ||
|
|
c7adda3711 | ||
|
|
c141c7e0aa | ||
|
|
5875d2a339 | ||
|
|
026d6d5da7 |
+34
-21
@@ -2,11 +2,19 @@
|
||||
* @file app_boot.c
|
||||
* @brief 深睡唤醒 / 上电按键判定实现
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.26
|
||||
* @version V1.1.0
|
||||
* @date 2026.08.27
|
||||
* @version V1.4.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现深睡唤醒按键判定(3s 开机 / 5s 配对)
|
||||
* - V1.1.0, 2026.08.26, cyWu, 新增 12s 长按进 OTA、开机前低电量拦截
|
||||
* - V1.2.0, 2026.08.27, cyWu, 按满 5s 绿灯即按配对节奏快闪,松手才真正提交配网
|
||||
* - V1.3.0, 2026.08.27, cyWu, 3s/5s 提示灯改直接复用 LED 事件库
|
||||
* (LED_EVENT_STANDBY/LED_EVENT_PAIRING),
|
||||
* 不再手写一份闪烁定时逻辑
|
||||
* - V1.4.0, 2026.08.27, cyWu, LED 事件库初始化提前到 usr_jb_init(调用本文件
|
||||
* 前完成),本文件不再自己调 user_led_init;
|
||||
* 拒绝开机低电闪也改用新增的 LED_EVENT_BOOT_REJECT
|
||||
* 事件,去掉手写的闪烁计次循环
|
||||
******************************************************************************/
|
||||
|
||||
#include "app_boot.h"
|
||||
@@ -15,6 +23,7 @@
|
||||
#include "board_pin.h"
|
||||
#include "app_main.h"
|
||||
#include "usr_le_api.h"
|
||||
#include "led_manager.h"
|
||||
#include "asm/power/power_api.h"
|
||||
#include "asm/power/power_reset.h"
|
||||
|
||||
@@ -62,18 +71,17 @@ static void boot_sleep(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 拒绝开机前的低电提示:红灯闪烁 BOARD_BOOT_LOWBAT_BLINK_N 下
|
||||
* @brief 拒绝开机前的低电提示:红灯闪 BOARD_BOOT_LOWBAT_BLINK_N 下
|
||||
* @note 直接挂 LED_EVENT_BOOT_REJECT,闪烁节奏/次数由 led_manager 的事件表
|
||||
* 统一管理,这里只管等它闪完(事件闪完会自己删掉)
|
||||
* @return 无
|
||||
*/
|
||||
static void boot_blink_low_battery(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < BOARD_BOOT_LOWBAT_BLINK_N; i++) {
|
||||
bsp_led_red_set(1);
|
||||
boot_delay_ms(BOARD_BOOT_LOWBAT_ON_MS);
|
||||
bsp_led_red_set(0);
|
||||
boot_delay_ms(BOARD_BOOT_LOWBAT_OFF_MS);
|
||||
LED_EventAdd(LED_EVENT_BOOT_REJECT);
|
||||
while (LED_IsEventExist(LED_EVENT_BOOT_REJECT)) {
|
||||
clr_wdt();
|
||||
user_led_handle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,15 +142,19 @@ static uint8_t boot_skip_key_wait(void)
|
||||
/**
|
||||
* @brief 等待并判定开机按键
|
||||
* @note 配对(5s)不再一到点就立即提交:需要继续按住观察是否会按到 12s
|
||||
* 触发 OTA,真正的配对提交放在松手那一刻做,行为上仍是“按满 5s
|
||||
* 再松手即进配对”,只是多给了继续按到 12s 改道 OTA 的机会。
|
||||
* 触发 OTA,真正的配对提交放在松手那一刻做。按满 5s 绿灯先切到配对
|
||||
* 快闪提示,行为上仍是“按满 5s 再松手即进配对”,只是多给了继续按到
|
||||
* 12s 改道 OTA 的机会。
|
||||
* 3s 常亮 / 5s 快闪直接复用 led_manager 的 LED_EVENT_STANDBY /
|
||||
* LED_EVENT_PAIRING 事件(和正常运行时配对灯效完全一致的节奏),
|
||||
* 不再手写一份闪烁定时。LED 事件库由调用方(usr_jb_init)在本函数
|
||||
* 之前完成初始化,这里直接假定可用,跟 bsp_hw_init() 一个道理。
|
||||
* @return 无(深睡分支不返回)
|
||||
*/
|
||||
void app_boot_wait_key(void)
|
||||
{
|
||||
uint32_t t0;
|
||||
uint32_t elapsed;
|
||||
uint8_t green_on = 0;
|
||||
uint8_t pair_armed = 0;
|
||||
|
||||
usr_var.usr_goto_pair = 0;
|
||||
@@ -165,7 +177,6 @@ void app_boot_wait_key(void)
|
||||
}
|
||||
|
||||
t0 = timer_get_ms();
|
||||
bsp_led_all_off();
|
||||
log_info("boot key hold, wait 3s on / 5s pair / 12s ota\n");
|
||||
|
||||
while (bsp_key_is_pressed()) {
|
||||
@@ -179,19 +190,21 @@ void app_boot_wait_key(void)
|
||||
return;
|
||||
}
|
||||
|
||||
/* 5s 只武装配对,真正提交放到松手那一刻,给继续按到 12s 改道 OTA 的机会 */
|
||||
/* 5s 只武装配对并切到快闪,真正提交放到松手那一刻,给继续按到 12s 改道 OTA 的机会 */
|
||||
if (!pair_armed && (elapsed >= BOARD_KEY_PAIR_MS)) {
|
||||
pair_armed = 1;
|
||||
log_info("boot hold %ums, pair armed (release now for pair, "
|
||||
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",
|
||||
(unsigned)elapsed, (unsigned)BOARD_KEY_OTA_MS);
|
||||
}
|
||||
|
||||
if (!green_on && (elapsed >= BOARD_KEY_POWER_MS)) {
|
||||
green_on = 1;
|
||||
bsp_led_green_set(1);
|
||||
} else if (!pair_armed && !LED_IsEventExist(LED_EVENT_STANDBY)
|
||||
&& (elapsed >= BOARD_KEY_POWER_MS)) {
|
||||
LED_EventAdd(LED_EVENT_STANDBY);
|
||||
log_info("boot hold 3s, will power on\n");
|
||||
}
|
||||
|
||||
user_led_handle(); /* 按事件表当前状态刷新一次灯,闪烁节奏由事件配置自己算 */
|
||||
}
|
||||
|
||||
elapsed = timer_get_ms() - t0;
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
* @brief 深睡唤醒 / 上电按键判定:3s 开机,5s 配对,12s OTA,不足 3s 松手再睡;
|
||||
* 开机前电量低于约 5% 且未充电时拒绝开机
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.26
|
||||
* @version V1.1.0
|
||||
* @date 2026.08.27
|
||||
* @version V1.2.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现深睡唤醒按键判定(3s 开机 / 5s 配对)
|
||||
* - V1.1.0, 2026.08.26, cyWu, 新增 12s 长按进 OTA;新增开机前低电量
|
||||
* (BOARD_VBAT_SHUTDOWN_MV)拦截,仅闪 5 下红灯
|
||||
* 提示后继续深睡,不接受非充电状态下的开机
|
||||
* - V1.2.0, 2026.08.27, cyWu, 按满 5s 绿灯即快闪提示,松手才真正提交配网
|
||||
*
|
||||
* @note 必须在 BLE 协议栈起来、1ms 定时器启动之前调用。杰理 jiffies 此时
|
||||
* 往往 10ms 才跳一次,所以内部一律用 timer_get_ms 直接量时间,
|
||||
@@ -21,10 +22,10 @@
|
||||
|
||||
/**
|
||||
* @brief 等待并判定开机按键:不足 3s 松手、或全程未按键,直接原地深睡(不返回);
|
||||
* 满 3s 视为正常开机;松手时已过 5s 视为开机并进入配对(内部会调用
|
||||
* app_pair_start);持续按满 12s 直接开机并进入 OTA(调用
|
||||
* usr_app_ota_init)。若未充电且电量低于约 5%,则闪 5 下红灯后
|
||||
* 继续深睡,拒绝开机。
|
||||
* 满 3s 视为正常开机(绿灯常亮);按满 5s 绿灯改快闪提示“松手即配网”,
|
||||
* 真正清绑定 / 开绑定广播仍等松手(内部调用 app_pair_start);持续按满
|
||||
* 12s 直接开机并进入 OTA(调用 usr_app_ota_init)。若未充电且电量低于
|
||||
* 约 5%,则闪 5 下红灯后继续深睡,拒绝开机。
|
||||
* @return 无(深睡分支通过 while(1) 喂狗不返回;其余情况正常返回)
|
||||
*/
|
||||
void app_boot_wait_key(void);
|
||||
|
||||
+65
-11
@@ -2,8 +2,8 @@
|
||||
* @file app_function.c
|
||||
* @brief 设备功能开关 + 自动玩法 + 手动点动 + 按键动作实现
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.26
|
||||
* @version V1.1.0
|
||||
* @date 2026.09.10
|
||||
* @version V1.3.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现 DP0 开关、自动玩法、手动点动与按键动作
|
||||
* - V1.1.0, 2026.08.26, cyWu, 切模式闪灯改到 function_play_mode_apply 统一
|
||||
@@ -11,6 +11,9 @@
|
||||
* 启动也补上闪灯;DP2 停转(cmd=3)改边沿触发,
|
||||
* 避免残留的 03 一直把刚启动的自动模式打断;
|
||||
* 自动模式新增单次最长运行时长,到点回待机
|
||||
* - V1.2.0, 2026.09.10, cyWu, 软关后 APP 下发其它控制指令也可唤醒,
|
||||
* 唤醒后本拍继续执行该指令(玩法/点动)
|
||||
* - V1.3.0, 2026.09.10, cyWu, 短按循环改为 0→4→2→6→1→3→5,App DP1 编号不变
|
||||
******************************************************************************/
|
||||
|
||||
#include "app_function.h"
|
||||
@@ -39,17 +42,41 @@ typedef struct {
|
||||
uint16_t hand_ms;
|
||||
volatile uint8_t hand_write_pending; /**< DP2 刚写入(03 停转要靠事件) */
|
||||
volatile uint8_t hand_write_cmd;
|
||||
volatile uint8_t app_cmd_wake; /**< 软关后收到其它 APP 控制 DP */
|
||||
} AppFunctionCtx_t;
|
||||
|
||||
static AppFunctionCtx_t s_func;
|
||||
|
||||
static void function_hand_stop(void);
|
||||
static void function_app_power_on(const char *reason);
|
||||
static void function_play_mode_apply(uint8_t mode);
|
||||
static void function_play_mode_poll(void);
|
||||
static void function_play_mode_timeout(uint16_t dt);
|
||||
static void function_hand_poll(void);
|
||||
static void function_on_click(void);
|
||||
|
||||
/* 短按循环顺序(协议玩法号):停 → s_mode4 → s_mode2 → s_mode6 → s_mode1 → s_mode3 → s_mode5 → 停 */
|
||||
static const uint8_t s_key_play_seq[7] = {
|
||||
0, 4, 2, 6, 1, 3, 5
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 按短按顺序取下一档玩法
|
||||
* @param cur 当前 DP1 玩法号 0~6
|
||||
* @return 下一档玩法号
|
||||
*/
|
||||
static uint8_t function_key_play_next(uint8_t cur)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < 7; i++) {
|
||||
if (s_key_play_seq[i] == cur) {
|
||||
return s_key_play_seq[(i + 1) % 7];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化功能模块状态
|
||||
* @return 无
|
||||
@@ -70,18 +97,45 @@ void app_function_dp_hand_write(uint8_t cmd)
|
||||
s_func.hand_write_pending = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief APP 下发了非关机控制 DP,软关时下一拍唤醒
|
||||
* @return 无
|
||||
*/
|
||||
void app_function_on_app_cmd(void)
|
||||
{
|
||||
s_func.app_cmd_wake = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief APP 侧开机:功能开、同步 DP0、挂开机绿灯
|
||||
* @param reason 日志原因(英文)
|
||||
* @return 无
|
||||
*/
|
||||
static void function_app_power_on(const char *reason)
|
||||
{
|
||||
s_func.app_power = 1;
|
||||
gDevData.power = 1;
|
||||
app_led_hint_power_on();
|
||||
log_info("%s\n", reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 跟随 APP 下发的 DP0/DP1,并处理 DP2 手动点动
|
||||
* @return 无
|
||||
*/
|
||||
void app_function_poll(void)
|
||||
{
|
||||
/* DP0:APP 下发开关。充电中 / 正在关机时不跟,避免和 USB 软关抢状态 */
|
||||
uint8_t cmd_wake = s_func.app_cmd_wake;
|
||||
|
||||
s_func.app_cmd_wake = 0;
|
||||
|
||||
/* DP0:APP 下发开关。充电中 / 正在关机时不跟,避免和 USB 软关抢状态。
|
||||
* 已软关时,后续其它控制 DP 也当开机,本拍接着跑玩法/点动。 */
|
||||
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");
|
||||
if (!s_func.app_power && cmd_wake) {
|
||||
function_app_power_on("APP cmd wake from soft off");
|
||||
} else if (gDevData.power && !s_func.app_power) {
|
||||
function_app_power_on("APP power on");
|
||||
} else if (!gDevData.power && s_func.app_power) {
|
||||
app_function_soft_off();
|
||||
}
|
||||
@@ -133,7 +187,7 @@ void app_function_stop_all(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 软关:电机停、DP0=0,等 APP/短按再开。不断 BLE
|
||||
* @brief 软关:电机停、DP0=0,等 APP 开机/其它指令/短按再开。不断 BLE
|
||||
* @return 无
|
||||
*/
|
||||
void app_function_soft_off(void)
|
||||
@@ -145,7 +199,7 @@ void app_function_soft_off(void)
|
||||
gDevData.play_mode = 0;
|
||||
app_mode_stop();
|
||||
function_hand_stop();
|
||||
log_info("soft off, wait APP power on\n");
|
||||
log_info("soft off, wait APP cmd or key\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -335,7 +389,7 @@ static void function_hand_poll(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 短按:自动模式 0→1→…→6→0(闪灯提示交给 function_play_mode_apply)
|
||||
* @brief 短按:按 s_key_play_seq 切自动模式(闪灯提示交给 function_play_mode_apply)
|
||||
* @return 无
|
||||
*/
|
||||
static void function_on_click(void)
|
||||
@@ -344,7 +398,7 @@ 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(function_key_play_next(s_func.play_mode));
|
||||
log_info("key click play_mode=%d\n", s_func.play_mode);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
* @file app_function.h
|
||||
* @brief 设备功能开关(DP0)+ 自动玩法(DP1)+ 手动点动(DP2)+ 按键动作
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.25
|
||||
* @version V1.0.0
|
||||
* @date 2026.09.10
|
||||
* @version V1.2.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现 DP0 开关、自动玩法、手动点动与按键动作
|
||||
* - V1.2.0, 2026.09.10, cyWu, 软关后 APP 下发其它控制 DP 也可唤醒功能
|
||||
*
|
||||
* @note 本模块决定“电机现在该不该转、转哪种”,会读 app_power/app_pair 的
|
||||
* 状态作为阻塞条件(充电中/深睡中/配对中都不能转),也会触发
|
||||
@@ -30,6 +31,13 @@ void app_function_init(void);
|
||||
*/
|
||||
void app_function_dp_hand_write(uint8_t cmd);
|
||||
|
||||
/**
|
||||
* @brief APP 下发了非关机控制 DP(玩法/点动等),软关时下一拍唤醒功能
|
||||
* @note 与关机 DP 同一帧到达时不要调用,避免关完立刻被同包其它 DP 拉开
|
||||
* @return 无
|
||||
*/
|
||||
void app_function_on_app_cmd(void);
|
||||
|
||||
/**
|
||||
* @brief 跟随 APP 下发的 DP0/DP1,并处理 DP2 手动点动,每个业务 tick 调用一次
|
||||
* @return 无
|
||||
@@ -50,7 +58,7 @@ void app_function_tick_1ms(uint16_t dt);
|
||||
void app_function_stop_all(void);
|
||||
|
||||
/**
|
||||
* @brief 软关:电机停、DP0=0,等 APP/短按再开(USB 拔出 / APP 关时用)
|
||||
* @brief 软关:电机停、DP0=0,等 APP 开机/其它指令/短按再开(USB 拔出 / APP 关时用)
|
||||
* @return 无
|
||||
*/
|
||||
void app_function_soft_off(void);
|
||||
|
||||
+23
-15
@@ -2,8 +2,8 @@
|
||||
* @file app_led.c
|
||||
* @brief CBC2 灯效适配:充电红与待机绿可同时亮,不再 if-else 互斥
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.25
|
||||
* @version V2.0.0
|
||||
* @date 2026.09.10
|
||||
* @version V2.5.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.21, cyWu, 首次发布
|
||||
* - V1.1.0, 2026.08.25, cyWu, 开机绿灯只提示几秒
|
||||
@@ -12,6 +12,7 @@
|
||||
* - V2.2.0, 2026.08.25, cyWu, 重启/清场逻辑各提一个小函数,把意图写进注释
|
||||
* - V2.3.0, 2026.08.27, cyWu, 新增 OTA 红绿交替灯效
|
||||
* - V2.4.0, 2026.08.27, cyWu, 新增空电关机红闪 5 下灯效
|
||||
* - V2.5.0, 2026.09.10, cyWu, 切模式时保留待机绿,闪完不再多一闪
|
||||
******************************************************************************/
|
||||
|
||||
#include "app_led.h"
|
||||
@@ -53,15 +54,6 @@ static uint8_t green_pattern_busy(void)
|
||||
|| LED_IsEventExist(LED_EVENT_PAIR_FAIL));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绿灯正被配对/切模式/结果占用时,不要再挂待机绿
|
||||
* @return 1=绿灯忙
|
||||
*/
|
||||
static uint8_t green_busy(void)
|
||||
{
|
||||
return (uint8_t)(LED_IsEventExist(LED_EVENT_PAIRING) || green_pattern_busy());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 灯效初始化
|
||||
* @return APP_LED_OK=成功
|
||||
@@ -157,6 +149,20 @@ uint8_t app_led_is_empty_off(void)
|
||||
return s_empty_off;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 短灯效还在播时不要进休眠
|
||||
* @return 1=应禁睡,0=可以睡
|
||||
*/
|
||||
uint8_t app_led_need_nosleep(void)
|
||||
{
|
||||
if (s_ota || s_empty_off) {
|
||||
return 1;
|
||||
}
|
||||
return (uint8_t)(LED_IsEventExist(LED_EVENT_PAIR_FAIL)
|
||||
|| LED_IsEventExist(LED_EVENT_PAIR_OK)
|
||||
|| LED_IsEventExist(LED_EVENT_MODE));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 播放一次性配对结果灯
|
||||
* @param ok 1=成功常绿,0=失败红绿交替
|
||||
@@ -263,11 +269,13 @@ void app_led_run(uint16_t dt, uint8_t app_power)
|
||||
led_clear_function_events();
|
||||
} else {
|
||||
#if BOARD_LED_STANDBY_ON
|
||||
/* 正在充电(红)或已充满(绿)时都不叠加待机绿,只有完全没接电才显示待机 */
|
||||
if ((charge_led == 0) && !pairing && !green_busy()) {
|
||||
LED_EventAdd(LED_EVENT_STANDBY);
|
||||
} else {
|
||||
/* 配对/切模式/配对失败用 return 1 拦住待机,这里不用删。
|
||||
* 充电红不占用绿灯:拦截只是不跑待机 handler,绿灯会停在上次的亮。
|
||||
* 所以充电时要删待机,把绿灯掐灭。 */
|
||||
if (charge_led == 1) {
|
||||
LED_EventDelete(LED_EVENT_STANDBY);
|
||||
} else {
|
||||
LED_EventAdd(LED_EVENT_STANDBY);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -75,6 +75,13 @@ void app_led_request_empty_off(void);
|
||||
*/
|
||||
uint8_t app_led_is_empty_off(void);
|
||||
|
||||
/**
|
||||
* @brief 短灯效还在播时不要进休眠(配对失败/成功、切模式闪)
|
||||
* @note 灯刷新跟业务定时器走;休眠后节拍被拉长,交替闪会又慢又少。
|
||||
* @return 1=应禁睡,0=可以睡
|
||||
*/
|
||||
uint8_t app_led_need_nosleep(void);
|
||||
|
||||
/**
|
||||
* @brief 清空全部灯事件并灭灯(关机前调用)
|
||||
* @return 无
|
||||
|
||||
@@ -86,6 +86,7 @@ static const AppModeStep_t s_mode6[] = { /* 闪电快闪:两下轻、两下重
|
||||
STEP_END
|
||||
};
|
||||
|
||||
/* 下标 = 协议玩法编号 DP1:1~6 对应 s_mode1~s_mode6。短按循环顺序在 app_function.c */
|
||||
static const AppModeStep_t *const s_tables[7] = {
|
||||
0, s_mode1, s_mode2, s_mode3, s_mode4, s_mode5, s_mode6
|
||||
};
|
||||
|
||||
+18
-3
@@ -2,10 +2,11 @@
|
||||
* @file app_pair.c
|
||||
* @brief 配对会话计时实现
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.25
|
||||
* @version V1.0.0
|
||||
* @date 2026.09.10
|
||||
* @version V1.1.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现配对会话计时与成功/超时判定
|
||||
* - V1.1.0, 2026.09.10, cyWu, 窗口超时停止绑定广播
|
||||
******************************************************************************/
|
||||
|
||||
#include "app_pair.h"
|
||||
@@ -26,6 +27,19 @@ typedef struct {
|
||||
|
||||
static AppPairCtx_t s_pair;
|
||||
|
||||
/**
|
||||
* @brief 配对超时回调:从任务上下文停绑定广播
|
||||
* @note app_pair_tick 跑在 usr_timer 硬件时基里,不断开/不停广播,
|
||||
* 把 BLE 动作丢到 sys_timeout,避免占中断时间。
|
||||
* @param priv 未使用
|
||||
* @return 无
|
||||
*/
|
||||
static void pair_timeout_stop_adv(void *priv)
|
||||
{
|
||||
(void)priv;
|
||||
usr_stop_bind_adv();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 开始一次配对会话(计时清零)
|
||||
* @return 无
|
||||
@@ -59,7 +73,8 @@ AppPairEvt_t app_pair_tick_1ms(uint16_t dt)
|
||||
if (s_pair.elapsed_ms >= BOARD_PAIR_TIMEOUT_MS) {
|
||||
s_pair.active = 0;
|
||||
s_pair.elapsed_ms = 0;
|
||||
log_info("pair timeout 120s, stay on\n");
|
||||
log_info("pair timeout 120s, stop bind adv\n");
|
||||
sys_timeout_add(NULL, pair_timeout_stop_adv, 10);
|
||||
return APP_PAIR_EVT_FAIL;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
/******************************************************************************
|
||||
* @file app_pair.h
|
||||
* @brief 配对会话计时:开机配对窗口是否成功/超时,与 BLE 配对本身解耦
|
||||
* @brief 配对会话计时:开机配对窗口是否成功/超时
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.25
|
||||
* @version V1.0.0
|
||||
* @date 2026.09.10
|
||||
* @version V1.1.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现配对会话计时与成功/超时判定
|
||||
* - V1.1.0, 2026.09.10, cyWu, 窗口超时停止绑定广播
|
||||
*
|
||||
* @note 真正的 BLE 广播/绑定逻辑在 usr_le_code;本模块只负责“进入配对后
|
||||
* 60s 内 usr_get_pair_flag() 是否变 1”这件事,纯状态,不碰灯/按键。
|
||||
* @note 真正的 BLE 广播/绑定逻辑在 usr_le_code;本模块负责进入配对后
|
||||
* 120s 内 usr_get_pair_flag() 是否变 1,超时则停绑定广播。
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __APP_PAIR_H__
|
||||
|
||||
+4
-1
@@ -3,9 +3,11 @@
|
||||
* @brief LED 事件框架:按通道掩码独立开关,红绿可同时亮
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.25
|
||||
* @version V1.0.0
|
||||
* @version V1.1.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现按通道掩码独立开关的 LED 事件框架
|
||||
* - V1.1.0, 2026.08.27, cyWu, 新增 LED_EVENT_BOOT_REJECT(拒绝开机低电闪),
|
||||
* 与运行时 LED_EVENT_EMPTY 参数解耦,各自独立可调
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __LED_H__
|
||||
@@ -53,6 +55,7 @@ typedef enum {
|
||||
LED_EVENT_PAIR_FAIL, /**< 配对失败红绿交替 */
|
||||
LED_EVENT_OTA, /**< OTA:红绿交替闪,直到升级结束/复位 */
|
||||
LED_EVENT_EMPTY, /**< 电量 5% 关机:关绿灯,红灯闪 5 下 */
|
||||
LED_EVENT_BOOT_REJECT, /**< 拒绝开机:电量过低,红灯闪 N 下后继续深睡 */
|
||||
LED_EVENT_MAX
|
||||
} LED_Event_t;
|
||||
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
* @file led_manager.c
|
||||
* @brief CBC2 灯事件:充电红与待机绿走不同通道,可同时亮
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.25
|
||||
* @version V1.0.0
|
||||
* @date 2026.09.10
|
||||
* @version V1.2.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现红绿独立通道灯事件表
|
||||
* - V1.1.0, 2026.08.27, cyWu, 新增 LED_EVENT_BOOT_REJECT:拒绝开机低电闪,
|
||||
* 供 app_boot 在按键判定阶段直接复用事件库
|
||||
* - V1.2.0, 2026.09.10, cyWu, 切模式闪时待机绿不写灯,闪完同一拍衔上常亮
|
||||
******************************************************************************/
|
||||
|
||||
#include "led_manager.h"
|
||||
@@ -21,6 +24,7 @@ static uint8_t led_pair_ok_handler(LED_Config_t *led_config);
|
||||
static uint8_t led_pair_fail_handler(LED_Config_t *led_config);
|
||||
static uint8_t led_ota_handler(LED_Config_t *led_config);
|
||||
static uint8_t led_empty_handler(LED_Config_t *led_config);
|
||||
static uint8_t led_boot_reject_handler(LED_Config_t *led_config);
|
||||
|
||||
/**
|
||||
* @brief 只改掩码里的通道,其它灯保持原样
|
||||
@@ -64,26 +68,29 @@ static uint8_t led_standby_handler(LED_Config_t *led_config)
|
||||
|
||||
/**
|
||||
* @brief 配对绿快闪(无限)
|
||||
* @note 返回 1 拦住待机绿,否则待机会把快闪盖成常亮
|
||||
* @param led_config 配置
|
||||
* @return 0
|
||||
* @return 1
|
||||
*/
|
||||
static uint8_t led_pairing_handler(LED_Config_t *led_config)
|
||||
{
|
||||
(void)LED_Update(led_config);
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 切模式闪完两下后删事件
|
||||
* @note 还在闪时返回 1,拦住待机绿,避免把闪烁盖成常亮
|
||||
* @param led_config 配置
|
||||
* @return 0
|
||||
* @return 1=还在闪,0=闪完已删
|
||||
*/
|
||||
static uint8_t led_mode_handler(LED_Config_t *led_config)
|
||||
{
|
||||
if (LED_Update(led_config) == 0) {
|
||||
LED_EventDelete(LED_EVENT_MODE); /* 闪完两下后删,绿灯还给待机/配对 */
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,6 +146,20 @@ static uint8_t led_empty_handler(LED_Config_t *led_config)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 拒绝开机低电闪:闪完 N 下后删事件,随后调用方直接回深睡
|
||||
* @param led_config 配置
|
||||
* @return 1=还在闪,0=闪完已删事件
|
||||
*/
|
||||
static uint8_t led_boot_reject_handler(LED_Config_t *led_config)
|
||||
{
|
||||
if (LED_Update(led_config) == 0) {
|
||||
LED_EventDelete(LED_EVENT_BOOT_REJECT);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注册回调并装载 CBC2 事件表(GPIO 已在 bsp_hw_init)
|
||||
* @return 无
|
||||
@@ -231,8 +252,7 @@ void user_led_init(void)
|
||||
.config.alternatingBlinkParams.Group[0] = LED_CHANNEL(LED_CH_RED),
|
||||
.config.alternatingBlinkParams.Group[1] = LED_CHANNEL(LED_CH_GREEN),
|
||||
.config.alternatingBlinkParams.Cycle = (uint32_t)(BOARD_LED_ALT_MS * 2u),
|
||||
.config.runNum = (uint16_t)((BOARD_LED_PAIR_FAIL_MS + (BOARD_LED_ALT_MS * 2u) - 1u)
|
||||
/ (BOARD_LED_ALT_MS * 2u)),
|
||||
.config.runNum = (uint16_t)((BOARD_LED_PAIR_FAIL_MS + (BOARD_LED_ALT_MS * 2u) - 1u)/ (BOARD_LED_ALT_MS * 2u)),
|
||||
.ledEventHandler = led_pair_fail_handler,
|
||||
},
|
||||
[LED_EVENT_OTA] = {
|
||||
@@ -259,6 +279,18 @@ void user_led_init(void)
|
||||
.config.runNum = BOARD_LED_EMPTY_BLINK_N,
|
||||
.ledEventHandler = led_empty_handler,
|
||||
},
|
||||
[LED_EVENT_BOOT_REJECT] = {
|
||||
/* 拒绝开机:电量过低,红灯闪 N 下,参数与运行时 EMPTY 独立解耦 */
|
||||
.event = LED_EVENT_BOOT_REJECT,
|
||||
.priority = PRIORITY_4,
|
||||
.config.ledMask = LED_CHANNEL(LED_CH_RED),
|
||||
.config.state = LED_BLINKING,
|
||||
.config.blinkParams.orDer = 0, /* 先亮后灭 */
|
||||
.config.blinkParams.onTime = BOARD_BOOT_LOWBAT_ON_MS,
|
||||
.config.blinkParams.offTime = BOARD_BOOT_LOWBAT_OFF_MS,
|
||||
.config.runNum = BOARD_BOOT_LOWBAT_BLINK_N,
|
||||
.ledEventHandler = led_boot_reject_handler,
|
||||
},
|
||||
};
|
||||
|
||||
LED_SetStateInit(set_led_multi);
|
||||
|
||||
@@ -69,6 +69,9 @@ void userHandle(void)
|
||||
* ════════════════════════════════════════════════════════════════ */
|
||||
int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
||||
{
|
||||
uint8_t other_cmd = 0;
|
||||
uint8_t got_power_off = 0;
|
||||
|
||||
if (!info || !ctrl)
|
||||
{
|
||||
return -1;
|
||||
@@ -89,6 +92,7 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
||||
else
|
||||
{
|
||||
gDevData.power = 0;
|
||||
got_power_off = 1;
|
||||
// TODO: 关 — 执行动作
|
||||
}
|
||||
break;
|
||||
@@ -96,6 +100,7 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
||||
/* ── DP1 自动模式 (enum) ── */
|
||||
case DP_ID_PLAY_MODE:
|
||||
gDevData.play_mode = ctrl->play_mode;
|
||||
other_cmd = 1;
|
||||
switch (ctrl->play_mode)
|
||||
{
|
||||
case PLAY_MODE_0: /* 停止复位 */
|
||||
@@ -127,6 +132,7 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
||||
/* ── DP2 手动模式 (raw) ── */
|
||||
case DP_ID_HAND_MODE:
|
||||
memcpy(gDevData.hand_mode, ctrl->hand_mode, 2);
|
||||
other_cmd = 1;
|
||||
/* 薄适配:通知业务层;03=停,00=空闲 */
|
||||
{
|
||||
extern void usr_dp_hand_write(uint8_t cmd);
|
||||
@@ -136,6 +142,7 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
||||
|
||||
/* ── DP3 绳索校准 (bool) ── */
|
||||
case DP_ID_CALIBRATION:
|
||||
other_cmd = 1;
|
||||
if (ctrl->calibration)
|
||||
{
|
||||
gDevData.calibration = 1;
|
||||
@@ -151,30 +158,35 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
||||
/* ── DP4 绳索圈数 (uint8) ── */
|
||||
case DP_ID_NUMBERTURNS:
|
||||
gDevData.numberturns = ctrl->numberturns;
|
||||
other_cmd = 1;
|
||||
// TODO: 根据 ctrl->numberturns 执行动作
|
||||
break;
|
||||
|
||||
/* ── DP8 勿扰模式开关 (raw) ── */
|
||||
case DP_ID_NO_DISTURB_ENABLE:
|
||||
memcpy(gDevData.no_disturb_enable, ctrl->no_disturb_enable, 13);
|
||||
other_cmd = 1;
|
||||
// TODO: 根据 gDevData.no_disturb_enable 执行动作 (长度 13 字节)
|
||||
break;
|
||||
|
||||
/* ── DP9 预留DP1 (uint32) ── */
|
||||
case DP_ID_RESERVA_DP1:
|
||||
gDevData.reserva_dp1 = ctrl->reserva_dp1;
|
||||
other_cmd = 1;
|
||||
// TODO: 根据 ctrl->reserva_dp1 执行动作
|
||||
break;
|
||||
|
||||
/* ── DP10 预留DP2 (uint32) ── */
|
||||
case DP_ID_RESERVA_DP2:
|
||||
gDevData.reserva_dp2 = ctrl->reserva_dp2;
|
||||
other_cmd = 1;
|
||||
// TODO: 根据 ctrl->reserva_dp2 执行动作
|
||||
break;
|
||||
|
||||
/* ── DP11 预留DP3 (uint32) ── */
|
||||
case DP_ID_RESERVA_DP3:
|
||||
gDevData.reserva_dp3 = ctrl->reserva_dp3;
|
||||
other_cmd = 1;
|
||||
// TODO: 根据 ctrl->reserva_dp3 执行动作
|
||||
break;
|
||||
|
||||
@@ -182,6 +194,13 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 软关后其它控制 DP 可唤醒;与关机 DP 同帧则关机优先,避免关完立刻被拉开 */
|
||||
if (other_cmd && !got_power_off)
|
||||
{
|
||||
extern void usr_dp_app_cmd(void);
|
||||
usr_dp_app_cmd();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
/* MCU software version: Vmajor.minor.patch */
|
||||
#define JB_MCU_SW_VERSION_MAJOR 1
|
||||
#define JB_MCU_SW_VERSION_MINOR 0
|
||||
#define JB_MCU_SW_VERSION_PATCH 0
|
||||
#define JB_MCU_SW_VERSION_PATCH 1
|
||||
|
||||
/* MCU hardware version: Vmajor.minor.patch */
|
||||
#define JB_MCU_HW_VERSION_MAJOR 1
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
* @file usr_jb_main.c
|
||||
* @brief CBC2 业务组合根:只做“先后调用谁、谁通知谁”,不含具体业务算法
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.25
|
||||
* @version V2.1.0
|
||||
* @date 2026.09.10
|
||||
* @version V2.3.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.21, cyWu, 首次发布
|
||||
* - V1.1.0, 2026.08.24, cyWu, 收口未接线模块;DP2 仅第 1 字节点动 1s
|
||||
@@ -18,6 +18,12 @@
|
||||
* 换向死区判定改用阈值检查而非精确计时,
|
||||
* 取消专门的 1ms 高频档,全系统统一 20ms 节拍,
|
||||
* 只保留"能不能休眠"这一个维度
|
||||
* - V2.2.0, 2026.08.27, cyWu, app_led_init() 提前到 app_boot_wait_key() 之前,
|
||||
* 开机 3s/5s/拒绝开机低电闪直接用灯效事件表,
|
||||
* 不用再各自摸裸 GPIO 手搓一份闪烁定时
|
||||
* - V2.3.0, 2026.09.10, cyWu, 配网期间禁睡:priority=0 时低功耗会把 20ms
|
||||
* 节拍拉长,绑定广播和 AUTHSETUP 握手变慢,
|
||||
* 实测配网可拖到数分钟
|
||||
*
|
||||
* @note 协议生成文件 jb_product.c 不要填业务。APP 下行写 gDevData。
|
||||
* usr_timer_loop 由 BLE 回调,勿再注册第二份。
|
||||
@@ -81,7 +87,7 @@ const uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN] = {
|
||||
*/
|
||||
typedef enum {
|
||||
USR_TICK_IDLE = 0, /**< 可休眠 */
|
||||
USR_TICK_BLOCK_SLEEP /**< 禁止休眠(按键按住/玩法中/关机等待/电机运转,含换向死区)*/
|
||||
USR_TICK_BLOCK_SLEEP /**< 禁止休眠(按键按住/玩法中/关机等待/电机运转/配网,含换向死区)*/
|
||||
} UsrTickMode_t;
|
||||
|
||||
/** 调度器状态,替代原来 2 个散落的 static */
|
||||
@@ -152,15 +158,19 @@ static void usr_poweroff_finalize_hook(void)
|
||||
/**
|
||||
* @brief 按当前业务状态判断能不能休眠
|
||||
* @note 换向死区(bsp_motor_is_busy 已把它算进"忙")、点动、玩法、按键按住、
|
||||
* 关机等待,统一都是"不能睡",节拍周期都一样用 USR_TIMER_IDLE_MS,
|
||||
* 不再区分要不要 1ms
|
||||
* 关机等待、配网窗口,统一都是"不能睡",节拍周期都一样用 USR_TIMER_IDLE_MS。
|
||||
* 配网只跟 120s 配对窗口(app_pair_is_active):窗口内禁睡。
|
||||
* 超时后配对失败灯还要再闪约 3s,这段也禁睡,否则节拍被拉长,
|
||||
* 红绿交替会又慢又少。
|
||||
* @return 本次应使用的档位
|
||||
*/
|
||||
static UsrTickMode_t usr_timer_pick_mode(void)
|
||||
{
|
||||
if (bsp_key_is_pressed() || app_power_poweroff_pending()
|
||||
|| app_mode_get_active() || bsp_motor_is_busy()
|
||||
|| app_function_is_hand_busy()) {
|
||||
|| app_function_is_hand_busy()
|
||||
|| app_pair_is_active()
|
||||
|| app_led_need_nosleep()) {
|
||||
return USR_TICK_BLOCK_SLEEP;
|
||||
}
|
||||
return USR_TICK_IDLE;
|
||||
@@ -256,9 +266,12 @@ static void usr_log_boot_version(void)
|
||||
/**
|
||||
* @brief 业务初始化(在 BLE 栈起来之前由 app_main 调用)
|
||||
* @note 内部会等按键 3s/5s;不足 3s 直接深睡,不会启动业务定时器。
|
||||
* 按键判定只摸 bsp_hw 的裸 GPIO,不经过协议栈/灯效表/电机状态机,
|
||||
* 所以只有 bsp_hw_init() 需要排在它前面——一旦判定要睡回去,
|
||||
* boot_sleep() 原地喂狗不再返回,后面这些初始化不会被浪费。
|
||||
* 按键判定只摸 bsp_hw 的裸 GPIO 和 led_manager 的事件表(3s 常亮 /
|
||||
* 5s 配对快闪 / 拒绝开机低电闪都要用灯),不经过协议栈/电机状态机,
|
||||
* 所以 app_led_init() 要排在它前面(跟 bsp_hw_init() 一样)——一旦
|
||||
* 判定要睡回去,boot_sleep() 原地喂狗不再返回,后面这些初始化不会
|
||||
* 被浪费。app_led_init() 内部有已初始化判断,这里提前调不会导致
|
||||
* 后面重复初始化出问题。
|
||||
* @return 无
|
||||
*/
|
||||
void usr_jb_init(void)
|
||||
@@ -266,14 +279,14 @@ void usr_jb_init(void)
|
||||
usr_log_boot_version(); /* UART 已在 app_main 起来,按键判定前就能看到版本 */
|
||||
|
||||
bsp_hw_init();
|
||||
app_led_init(); /* 提前到按键判定之前:3s/5s/拒绝开机低电闪都要直接用灯效事件表 */
|
||||
|
||||
app_boot_wait_key(); /* 深睡分支不会返回;返回即代表确定要开机 */
|
||||
|
||||
jbInit();
|
||||
userInit();
|
||||
app_led_init();
|
||||
if (usr_var.usr_goto_updata) {
|
||||
/* 开机 12s 已进 OTA:灯表刚起来,立刻挂红绿交替 */
|
||||
/* 开机 12s 已进 OTA:立刻挂红绿交替 */
|
||||
app_led_request_ota();
|
||||
}
|
||||
app_mode_init();
|
||||
@@ -286,8 +299,10 @@ void usr_jb_init(void)
|
||||
user_key_lock_if_held();
|
||||
app_function_power_on_silent();
|
||||
|
||||
usr_timer_rearm(USR_TICK_IDLE);
|
||||
log_info("timer idle %ums, sleep ok\n", (unsigned)USR_TIMER_IDLE_MS);
|
||||
/* 开机若已进配对,第一拍就禁睡,不要先挂可休眠档再等 20ms 才切 */
|
||||
usr_timer_rearm(usr_timer_pick_mode());
|
||||
log_info("timer %ums, mode=%d\n", (unsigned)USR_TIMER_IDLE_MS,
|
||||
(int)s_sched.mode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -300,6 +315,15 @@ void usr_dp_hand_write(uint8_t cmd)
|
||||
app_function_dp_hand_write(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief APP 下发了非关机控制 DP(jb_product 薄适配)
|
||||
* @return 无
|
||||
*/
|
||||
void usr_dp_app_cmd(void)
|
||||
{
|
||||
app_function_on_app_cmd();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief BLE 收到见宝载荷,送入协议环形缓冲
|
||||
* @param data 载荷指针
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
* @file usr_le_adv.c
|
||||
* @brief BLE 广播 / 配对 / OTA 广播切换
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.19
|
||||
* @version V1.1.0
|
||||
* @date 2026.09.10
|
||||
* @version V1.2.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.03, cyWu, 首次发布
|
||||
* - V1.1.0, 2026.08.19, cyWu, 对齐模组注释与助手;保留 SOC 上电绑广播策略
|
||||
* - V1.2.0, 2026.09.10, cyWu, 配对窗口超时停止绑定广播
|
||||
******************************************************************************/
|
||||
|
||||
#include "system/includes.h"
|
||||
@@ -138,16 +139,28 @@ void usr_goto_pair_mode(void)
|
||||
usr_ble_adv_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 停止绑定广播:清配网标志、关掉广播;未完成的连接一并断开
|
||||
* @note 只关广播,500ms 协议定时器还在,避免把 usr_timer_loop 停掉。
|
||||
* usr_adv_flag 清 0 后周期刷新不会再 enable,广播不会自己开回来。
|
||||
* @return 无
|
||||
*/
|
||||
void usr_stop_bind_adv(void)
|
||||
{
|
||||
printf("usr_stop_bind_adv\n");
|
||||
ble_multi_trans_disconnect();
|
||||
usr_var.usr_goto_pair = 0;
|
||||
usr_var.usr_adv_flag = 0;
|
||||
ble_trans_module_enable(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 断开 BLE 并停止广播(复位/重启前)
|
||||
*/
|
||||
void usr_ble_disconnect_and_stop_adv(void)
|
||||
{
|
||||
printf("usr_ble_disconnect_and_stop_adv\n");
|
||||
ble_multi_trans_disconnect();
|
||||
usr_var.usr_goto_pair = 0;
|
||||
usr_var.usr_adv_flag = 0;
|
||||
ble_trans_module_enable(0);
|
||||
usr_stop_bind_adv();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
* @file usr_le_api.h
|
||||
* @brief usr_le_code 对外应用 API(配网 / 配对 / OTA)
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.03
|
||||
* @version V1.0.2
|
||||
* @date 2026.09.10
|
||||
* @version V1.0.3
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.03, cyWu, 首次发布
|
||||
* - V1.0.1, 2026.08.19, cyWu, 收敛公开 API,移除 BLE 栈钩子声明
|
||||
* - V1.0.2, 2026.08.25, cyWu, 改为源码编译,去掉静态库
|
||||
* - V1.0.3, 2026.09.10, cyWu, 增加配对窗口超时停绑定广播
|
||||
*
|
||||
* @note usr_ble_adv_init / usr_le_data_recieve 为 BLE/RCSP 协议栈链接钩子,
|
||||
* 由 usr_le_code 内部实现并导出符号,不作为应用层公开接口。
|
||||
@@ -47,6 +48,14 @@ void usr_clear_pair_info(void);
|
||||
*/
|
||||
uint8_t usr_get_pair_flag(void);
|
||||
|
||||
/**
|
||||
* @brief 停止绑定广播(配对窗口超时)
|
||||
* @note 清 usr_goto_pair,关掉广播;若绑定未完成则断开连接。
|
||||
* 已配网的回连广播不走本接口。
|
||||
* @return 无
|
||||
*/
|
||||
void usr_stop_bind_adv(void);
|
||||
|
||||
/**
|
||||
* @brief 启动应用侧 OTA 流程
|
||||
* @note 置位 usr_goto_updata,并创建 1s 周期定时器驱动断连与 OTA 广播切换
|
||||
|
||||
+77
-33
@@ -1,11 +1,12 @@
|
||||
# 门夹弹力绳 需求文档(V1.1)
|
||||
# 门夹弹力绳 需求文档(V1.5)
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 产品名称 | 门夹弹力绳(智能逗宠玩具) |
|
||||
| 型号 | PICBC2 / CBC2 |
|
||||
| 主控 | 杰理 AC7016C |
|
||||
| 日期 | 2026-08-25 |
|
||||
| 日期 | 2026-09-10 |
|
||||
| 修订 | V1.5:配对窗口超时关闭绑定广播 |
|
||||
|
||||
---
|
||||
|
||||
@@ -13,26 +14,39 @@
|
||||
|
||||
门夹弹力绳是一款智能逗宠玩具。设备夹在门板上,内部电机带动弹力绳收放,让绳端玩具按不同"玩法"动起来逗宠物玩。支持手机 App 通过远程控制,也支持按键本地操作。
|
||||
|
||||
硬件:红绿双指示灯(低电平亮)+ 单按键(低电平按下)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心功能
|
||||
|
||||
### 2.1 开关机
|
||||
|
||||
| 操作 | 行为 |
|
||||
| --- | --- |
|
||||
| 深睡状态下按 3 秒 | 开机 |
|
||||
| 深睡状态下按 5 秒 | 开机并进入配对模式 |
|
||||
| 开机状态下按 3 秒 | 关机(深睡省电) |
|
||||
| 插着充电线时上电 | 自动开机,不用按键 |
|
||||
| 刚上电没按按键 | 保持深睡,不误开机 |
|
||||
| 操作 | 行为 | 灯效 |
|
||||
| --- | --- | --- |
|
||||
| 刚上电没按按键、也没插充电线 | 保持深睡,不误开机 | 灭 |
|
||||
| 插着充电线时上电 | 自动开机,跳过按键判定 | 进正常灯效 |
|
||||
| 深睡状态下按住 **3 秒** 后松手 | 开机 | 满 3 秒时绿灯常亮 |
|
||||
| 深睡状态下按住 **5 秒** 后松手 | 开机并进入配对(清配对信息) | 满 5 秒切成绿灯快闪 |
|
||||
| 深睡状态下按住 **12 秒** | 开机并进入 OTA(不等松手) | 随后红绿交替闪 |
|
||||
| 深睡状态下电量过低(约 ≤5%)且未充电 | 拒绝开机,红闪 5 下后继续深睡 | 红 200ms 亮/灭 ×5 |
|
||||
| 开机状态下按住 **3 秒** | 关机(深睡省电) | 立刻灭灯 |
|
||||
|
||||
- 深睡状态下,按按键(3 秒/5 秒)或插充电线均可唤醒开机。
|
||||
补充说明:
|
||||
|
||||
- 深睡唤醒:按按键(3 秒开机 / 5 秒配对 / 12 秒 OTA)或插充电线均可开机。
|
||||
- 满 5 秒只是「武装配对 + 快闪提示」,真正提交配对在 **松手** 那一刻。如果继续按到 12 秒,改道进 OTA,不再进配对。
|
||||
- 开机判定时如果手还按着,会锁住按键,松开后才认短按/长按,避免刚开机立刻关机。
|
||||
|
||||
### 2.2 开关(App 控制)
|
||||
|
||||
- App 可开/关设备功能;关闭后电机停转,蓝牙不断开,可随时再开。
|
||||
- 功能关闭后,以下操作都会退出软关并恢复功能:
|
||||
- App 下发开机 DP
|
||||
- App 再下发其它控制指令(自动玩法、手动点动等),并按该指令继续执行
|
||||
- 短按按键:只唤醒功能,**不切玩法**
|
||||
- 拔掉充电线会自动进入低功耗状态(电机停转、LED 关闭、蓝牙保持连接)。
|
||||
- 充电中、正在深睡关机:其它指令不会把功能拉开。
|
||||
|
||||
### 2.3 自动玩法(6 种,App 或按键选择)
|
||||
|
||||
@@ -48,46 +62,75 @@
|
||||
|
||||
- 玩法会循环播放,直到切换或停止。
|
||||
- 运行中可随时切换到其他玩法或停止。
|
||||
- 单次最长运行 **10 分钟**,到点自动停转回待机(保护性超时,不闪灯,避免误以为被按到)。
|
||||
- 充电中、配对中、正在深睡关机:不允许跑自动玩法,强制回到停转。
|
||||
- 功能关闭时若 App 下发玩法/点动:先退出软关,再按该指令执行。
|
||||
|
||||
### 2.4 手动控制(App 控制)
|
||||
|
||||
- App 可手动点动:控制电机**收绳**或**放绳**,松手/点停止后电机停止。
|
||||
- 手动控制进行中,自动玩法暂停。
|
||||
- 从自动/空闲切到手动时,绿灯闪两下提示。
|
||||
- 手动点动进行中,短按按键无效,避免打断收放绳。
|
||||
- 选中自动玩法会先停手动,再进入自动模式。
|
||||
|
||||
### 2.5 按键控制
|
||||
|
||||
| 操作 | 行为 |
|
||||
| --- | --- |
|
||||
| 短按(<1 秒) | 切换玩法:停 → 玩法1 → 玩法2 → … → 玩法6 → 停,循环 |
|
||||
| 长按 3 秒 | 关机 |
|
||||
| 充电中 / 配对中 | 按键无效 |
|
||||
开机后只有一颗按键,短按/长按含义如下:
|
||||
|
||||
| 操作 | 条件 | 行为 |
|
||||
| --- | --- | --- |
|
||||
| 短按(松开且未到 3 秒) | 功能开、空闲 | 切换玩法:停 → 起伏攻防战 → 飞鸟俯冲 → 闪电快闪 → 弹力伪装 → 云阶小陀螺 → 微风拂动 → 停,循环;绿灯闪两下。App 仍按 DP1 原编号选玩法 |
|
||||
| 短按 | 功能已关(App 软关 / 拔充电线后) | 只唤醒功能,不切玩法;挂待机绿灯 |
|
||||
| 短按 | 手动点动进行中 | 无效 |
|
||||
| 长按 3 秒 | 功能开 | 关机进深睡 |
|
||||
| 短按 / 长按 | 充电中 / 配对中 / 正在关机 | 无效 |
|
||||
|
||||
### 2.6 电量与充电
|
||||
|
||||
- 电量按电压换算:满电 4.2V = 100%,3.2V = 0%,电量自动上报 App 显示。
|
||||
- **低电量**(电池电压低于 **3.7V**):App 上报警告,指示灯红灯慢闪;持续 1 分钟未充电则自动关机保护电池。
|
||||
- **电量极低**(低于 3.2V):直接自动关机。
|
||||
- **插入充电线**:电机立即停止、进入充电状态,只有红灯常亮,表示充电中,充满转绿灯常亮;充电期间不执行玩法,按键无效。
|
||||
- **充电中拔掉充电线**:设备自动进入低功耗状态(电机停转、LED关闭、蓝牙保持连接),需按短按按键或 App 重新开启后才能使用。
|
||||
- 电池:锂电池,USB 5V 充电,充满自动转灯。
|
||||
- 电量按电压换算:满电约 4.12V = 100%,3.2V = 0%,电量自动上报 App 显示。充电内阻压降下,充满基准按 4.2V 计。
|
||||
- **低电量**(电池电压低于 **3.7V**):App 上报警告,指示灯红灯慢闪。只提示、不关机;电压回升超过 3.75V 后退出低电。
|
||||
- **空电关机**(显示电量 ≤ **5%**):立刻停电机,红灯闪 5 下后自动关机保护电池。充电中不判定。
|
||||
- **开机电量过低**(未充电且裸电压约 ≤ 3.25V,对应约 5%):拒绝开机,红闪 5 下后继续深睡。
|
||||
- 开机后约 3 秒内不因低电/空电关机,避免上电瞬间误判。
|
||||
- **插入充电线**:电机立即停止、进入充电状态,红灯常亮表示充电中,充满转绿灯常亮;充电期间不执行玩法,按键无效。功能关掉时充电灯仍继续亮。
|
||||
- **充电中拔掉充电线**:设备自动进入低功耗状态(电机停转、LED 关闭、蓝牙保持连接),需短按按键或 App 重新开启后才能使用。
|
||||
- 电池:锂电池,USB 5V 充电,充满自动转灯;硬件判定充满后电量强制收尾到 100%。
|
||||
|
||||
### 2.7 指示灯
|
||||
|
||||
| 状态 | 灯效 |
|
||||
| --- | --- |
|
||||
| 正常待机 | 绿灯常亮 |
|
||||
| 充电中 | 红灯常亮 |
|
||||
| 充满 | 绿灯常亮 |
|
||||
| 低电量 | 红灯慢闪 |
|
||||
| 配对中 | 绿灯快闪 |
|
||||
| 配对成功 | 绿灯常亮 3 秒 |
|
||||
| 配对失败 | 红绿交替闪 3 秒 |
|
||||
| 按键切玩法 | 绿灯闪两下 |
|
||||
红绿双灯,通道独立,可以同时亮。充电灯和功能开关无关。
|
||||
|
||||
| 状态 | 灯效 | 节奏 |
|
||||
| --- | --- | --- |
|
||||
| 正常待机(功能开、没充电) | 绿灯常亮 | 一直亮 |
|
||||
| 充电中 | 红灯常亮 | 一直亮 |
|
||||
| 充满 | 绿灯常亮 | 一直亮 |
|
||||
| 低电量(功能开、没接电) | 红灯慢闪 | 亮 200ms / 周期 2s |
|
||||
| 配对中 | 绿灯快闪 | 100ms 亮 / 100ms 灭 |
|
||||
| 配对成功 | 绿灯常亮 3 秒 | 一次性 |
|
||||
| 配对失败 | 红绿交替 3 秒 | 200ms 半周期 |
|
||||
| 切玩法 / 切手动 | 绿灯闪两下 | 150ms 灭 + 150ms 亮 ×2 |
|
||||
| OTA | 红绿交替,独占全部灯 | 200ms 半周期,一直闪到升级结束/复位 |
|
||||
| 运行中空电关机 | 关绿灯,红灯闪 5 下再掉电 | 200ms 亮/灭 ×5 |
|
||||
| 开机电量过低被拒 | 红灯闪 5 下后继续深睡 | 同上 |
|
||||
|
||||
优先级(高的盖住低的):
|
||||
|
||||
1. OTA / 空电关机红闪 / 切模式绿闪
|
||||
2. 配对快闪、配对成功、配对失败
|
||||
3. 充电红 / 充满绿 / 低电红闪
|
||||
4. 待机绿灯(最低)
|
||||
|
||||
OTA 和空电关机会清掉其它灯,只跑自己那一种。功能关掉时,待机/配对/切模式/低电灯会清掉,**充电灯保留**。
|
||||
|
||||
### 2.8 配对
|
||||
|
||||
- 首次使用:深睡状态长按 5 秒开机并进入配对,App 扫码连接即可。
|
||||
- 配对窗口 120 秒:期间连接成功绿灯 3 秒;超时未连接保持开机,可重新配对。
|
||||
- 首次使用:深睡状态长按 5 秒后松手,开机并进入配对,App 扫码连接即可。
|
||||
- 按满 5 秒绿灯先切到配对快闪;若继续按到 12 秒则改道 OTA,不再提交配对。
|
||||
- 配对窗口 120 秒:期间连接成功绿灯常亮 3 秒;超时未连接红绿交替 3 秒,关闭绑定广播,设备保持开机。要再配网:关机后长按 5 秒重新进入配对。
|
||||
- 配对窗口 120 秒内系统不进入休眠,避免广播/握手被低功耗拉慢;窗口结束(成功或超时)后恢复可休眠。
|
||||
- 配对期间不跑玩法、按键无效。
|
||||
- App 可解绑设备,解绑后设备清除配对信息,重新配对即可再次使用。
|
||||
|
||||
### 2.9 时间同步
|
||||
@@ -97,6 +140,7 @@
|
||||
### 2.10 固件升级(OTA)
|
||||
|
||||
- 支持 App 远程升级固件,升级协议已打通。
|
||||
- 本地入口:深睡状态按住 12 秒,开机并进入 OTA 广播,红绿交替闪。
|
||||
- 当前版本:固件 V1.0.0、硬件 V1.0.0。
|
||||
- 实际升级流程(写入芯片、校验、版本判断)待开发。
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$ProjectName = "PICBC2",
|
||||
[string]$ProjectName = "JBao_Door_Clamp",
|
||||
[string]$SdkRoot = ""
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user