Compare commits

..
13 Commits
Author SHA1 Message Date
zengzhiheng b63a928b92 feat: 完成弹力魔盒初版固件 2026-08-31 20:15:19 +08:00
wuchuyuan 026d6d5da7 1、开机按满 5s 绿灯先切配对快闪提示,松手才清绑定并进配网,继续按到 12s 仍可改道 OTA
2、app_led_init 提前到按键判定前,3s 常亮 / 5s 快闪 / 拒绝开机低电闪统一走灯效事件表,去掉开机阶段手搓 GPIO 闪烁
2026-08-27 20:19:57 +08:00
wuchuyuan 215e1be6bb 1、运行中空电关机改看显示百分比,到 5% 立刻停电机、关绿灯,红灯闪 5 下后再深睡,避免电压先掉、APP 还没到 5% 就关机
2、充电/放电电量缓冲加快,放电突变拦截先关掉,电量曲线满电点收到 4.12V
3、上电串口打印软硬件版本和编译时间
2026-08-27 11:40:57 +08:00
wuchuyuan fdb43011b7 1、低电 3.7V 只闪红灯不再 1 分钟后关机;电量低于约 5% 自动关机,未充电开机先闪 5 下红灯再睡回,只能充电后才能开机
2、自动/手动互相切换都闪 2 下灯;APP 发停止后再发自动模式可正常启动,手动残留停止不再挡住自动
3、自动模式 1~6 单次最长跑 10 分钟后回待机;关机时长按 12 秒进 OTA,OTA 期间红绿交替闪
2026-08-27 10:20:48 +08:00
wuchuyuanandCursor 3d445ae5c5 1、业务调度统一为 20ms 节拍,取消电机运转时的 1ms 高频定时器,避免配网后 BLE 收发与 1kHz 中断抢时序导致看门狗复位
2、调度器只保留可休眠/禁休眠两档,换向死区改由阈值检查判定,不再单独切 1ms 精度

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-26 17:16:08 +08:00
wuchuyuan 4ee0f86d13 1、Makefile 与 CodeBlocks 编译单元对齐,补 make pack / BURN_KEY,增加 build.bat 与固件打包脚本
2、下载脚本同时生成无 Key / 带 Key 镜像,USB 默认烧无 Key,build.bat key 烧带 Key
3、配对超时从 60s 改为 120s
4、增加需求文档
2026-08-26 17:06:28 +08:00
wuchuyuan 93c1943992 1、充电时不再叠待机绿灯;意外复位不再跳过 3s 开机按键判定
2、电机停转去掉反接刹车改自由滑行,避免电流冲击触发 LVD 复位;
3、关机直接 power_set_soft_poweroff,跳过耳机 SDK 优雅关机流程,按键关机从十几秒压到瞬间掉电
2026-08-25 20:05:59 +08:00
wuchuyuan 5e5f921a3a 添加音频智能语音功能配置,不添加待机功耗降不下去,现在LED亮着待机功耗有500多uA,LED灭着待机功耗有280uA,关机功耗8uA 2026-08-25 18:50:28 +08:00
wuchuyuan 5252471aca 优化程序架构 2026-08-25 14:59:39 +08:00
wuchuyuan d5ec6ecde0 功耗减低到900uA,还有优化空间,还要陈杰明那边支持一下 2026-08-25 11:16:54 +08:00
wuchuyuan e0f5da6f39 把静态库换成源码方便调试 2026-08-25 10:20:14 +08:00
wuchuyuan 0e6a93c12d 基本功能已完成,还差功耗和架构优化 2026-08-24 18:03:39 +08:00
wuchuyuan 81bd2f724d 生成了代码仓库 2026-08-21 18:41:42 +08:00
36 changed files with 866 additions and 1284 deletions
-9
View File
@@ -833,15 +833,6 @@
<Option compilerVar="CC" />
</Unit>
<Unit filename="apps/usr_app/app_function.h" />
<Unit filename="apps/usr_app/app_audio_debug.h" />
<Unit filename="apps/usr_app/car_audio_test.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="apps/usr_app/car_audio_test.h" />
<Unit filename="apps/usr_app/app_no_disturb.c">
<Option compilerVar="CC" />
</Unit>
<Unit filename="apps/usr_app/app_no_disturb.h" />
<Unit filename="apps/usr_app/app_shake_wake.c">
<Option compilerVar="CC" />
</Unit>
-2
View File
@@ -411,8 +411,6 @@ 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_no_disturb.c \
apps/usr_app/car_audio_test.c \
apps/usr_app/app_shake_wake.c \
apps/usr_le_code/ble_proto.c \
apps/usr_le_code/bleproto.c \
+3 -3
View File
@@ -236,9 +236,9 @@ _notify:
}
if (key_event_remap(&e)) {
sys_event_notify(&e);
// #if TCFG_KEY_TONE_EN
// audio_key_tone_play();
// #endif
#if TCFG_KEY_TONE_EN
audio_key_tone_play();
#endif
}
_scan_end:
scan_para->last_key = cur_key_value;
+3 -3
View File
@@ -735,7 +735,8 @@ struct port_wakeup port0 = {
};
#if BOARD_CHG_ENABLE
/* TP4056 CHRG falls when charging starts; use it for softoff charge wake. */
/* 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,
@@ -1000,9 +1001,8 @@ void board_set_soft_poweroff(void)
soff_gpio_protect(TCFG_IOKEY_POWER_ONE_PORT);
#endif
#if BOARD_CHG_ENABLE
/* Preserve both TP4056 open-drain inputs and their pull-ups in softoff. */
/* Preserve PG8 input state and its external pull-up for softoff wake. */
soff_gpio_protect(BOARD_CHG_PIN);
soff_gpio_protect(BOARD_STDBY_PIN);
#endif
#if (!(TCFG_LP_TOUCH_KEY_ENABLE && TCFG_LP_TOUCH_KEY1_EN))
File diff suppressed because it is too large Load Diff
-14
View File
@@ -1,14 +0,0 @@
#ifndef APP_AUDIO_DEBUG_H
#define APP_AUDIO_DEBUG_H
#include "app_config.h"
#include "system/includes.h"
/* UTF-8 中文日志;只在状态变化时打印,不在音频采样循环内连续打印。 */
#if TCFG_MAGIC_AUDIO_DEBUG_ENABLE
#define MAGIC_AUDIO_LOG(fmt, ...) printf("[魔盒声音] " fmt "\n", ##__VA_ARGS__)
#else
#define MAGIC_AUDIO_LOG(...) do { } while (0)
#endif
#endif /* APP_AUDIO_DEBUG_H */
+2 -4
View File
@@ -127,10 +127,8 @@ static uint8_t boot_skip_key_wait(void)
/* USB 在位(充电口有电):说明是插着电源,不是用户凭空按键开机 */
for (i = 0; i < 10; i++) {
/* VPWR/LDO5V are the authoritative external-power indicators.
* CHRG is open-drain and is only meaningful after VPWR is present;
* do not let an abnormal low PG8 skip the key wait by itself. */
if (get_ldo5v_online_hw()) {
if (bsp_chg_is_low() || bsp_vpwr_is_online()
|| get_ldo5v_online_hw()) {
log_info("boot usb, skip key wait\n");
return 1;
}
+87 -226
View File
@@ -19,9 +19,6 @@
#include "app_mode.h"
#include "app_led.h"
#include "app_shake_wake.h"
#include "app_no_disturb.h"
#include "car_audio_test.h"
#include "app_audio_debug.h"
#include "app_time_util.h"
#include "bsp_hw.h"
#include "board_pin.h"
@@ -30,23 +27,18 @@
#include "system/includes.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[APP_FUNC]"
#define LOG_TAG "[APP_FUNC]"
#define LOG_INFO_ENABLE
#include "debug.h"
/** 功能模块总状态,替代原来一堆散落的 static 变量 */
typedef struct
{
uint8_t app_power; /**< DP0:0=软关,电机/玩法停,BLE 仍在 */
uint8_t play_mode; /**< 当前自动模式 0~5 */
uint32_t play_ms; /**< 当前自动模式已运行时长,超时回待机 */
uint8_t hand_busy; /**< 手动点动进行中 */
uint8_t hand_cmd_last; /**< 最近一次手动命令 */
typedef struct {
uint8_t app_power; /**< DP00=软关,电机/玩法停,BLE 仍在 */
uint8_t play_mode; /**< 当前自动模式 0~5 */
uint32_t play_ms; /**< 当前自动模式已运行时长,超时回待机 */
uint8_t hand_busy; /**< 手动点动进行中 */
uint8_t hand_cmd_last; /**< 最近一次手动命令 */
uint16_t hand_ms;
uint8_t hand_queue[BOARD_HAND_QUEUE_LEN]; /**< DP2 连点排队:待执行命令 FIFO */
uint8_t hand_queue_head; /**< 队首下标 */
uint8_t hand_queue_count; /**< 待执行条数 */
volatile uint8_t hand_write_pending; /**< DP2 刚写入(03 停转要靠事件) */
volatile uint8_t hand_write_cmd;
volatile uint8_t power_write_pending;
@@ -54,13 +46,10 @@ typedef struct
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;
static void function_hand_apply(uint8_t cmd);
static uint16_t function_hand_action_ms(void);
static void function_hand_queue_push(uint8_t cmd);
static uint8_t function_hand_queue_pop(uint8_t *cmd);
static void function_hand_stop(void);
static void function_play_mode_apply(uint8_t mode);
@@ -82,6 +71,7 @@ void app_function_init(void)
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);
}
@@ -99,7 +89,7 @@ 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)
@@ -115,37 +105,37 @@ void app_function_set_shake_wake(uint8_t enable)
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)
{
if (s_func.shake_wake != !!gDevData.shake_wake)
{
if (s_func.shake_wake != !!gDevData.shake_wake) {
app_function_set_shake_wake(gDevData.shake_wake);
}
if (s_func.power_write_pending)
{
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))
{
if (enable) {
if (function_power_on(1)) {
log_info("APP power on\n");
}
else
{
} else {
gDevData.power = s_func.app_power;
}
}
else if (!enable && s_func.app_power)
{
} else if (!enable && s_func.app_power) {
app_function_soft_off();
}
else
{
} else {
gDevData.power = s_func.app_power;
}
}
@@ -164,29 +154,19 @@ void app_function_tick_1ms(uint16_t dt)
/* 与业务节拍共用一次唤醒,避免震动检测另开 10ms sys_timer。 */
app_shake_wake_poll();
if (s_func.hand_busy) {
uint8_t next_cmd;
if (s_func.hand_busy) {
bsp_motor_switch_tick(); /* 手动点动期间也要走换向死区 */
if (gDevData.hand_mode == HAND_MODE_1) {
app_mode_run(); /* cmd=1 的上电机波形靠自动引擎推进 */
}
s_func.hand_ms = app_add_ms(s_func.hand_ms, dt);
if (s_func.hand_ms >= function_hand_action_ms()) {
if (function_hand_queue_pop(&next_cmd)) {
s_func.hand_ms = 0; /* 接着补做排队的,不打断 */
function_hand_apply(next_cmd);
log_info("manual next %d\n", next_cmd);
} else {
function_hand_stop();
app_led_request_mode_blink();
log_info("manual done\n");
}
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 分钟回待机 */
}
}
}
/**
@@ -197,88 +177,10 @@ static void function_hand_stop(void)
{
s_func.hand_busy = 0;
s_func.hand_ms = 0;
s_func.hand_queue_head = 0;
s_func.hand_queue_count = 0;
gDevData.hand_mode = HAND_MODE_0;
app_mode_stop();
bsp_drive_stop();
}
/**
* @brief 把一条 DP2 命令压入排队 FIFO
* @param cmd HAND_MODE_1/2/3
* @return 无
*/
static void function_hand_queue_push(uint8_t cmd)
{
uint8_t idx;
if (s_func.hand_queue_count >= BOARD_HAND_QUEUE_LEN) {
log_info("hand queue full, drop %d\n", cmd);
return;
}
idx = (uint8_t)((s_func.hand_queue_head + s_func.hand_queue_count)
% BOARD_HAND_QUEUE_LEN);
s_func.hand_queue[idx] = cmd;
s_func.hand_queue_count++;
}
/**
* @brief 取出队首命令
* @param cmd 出参
* @return 1=取到,0=队列空
*/
static uint8_t function_hand_queue_pop(uint8_t *cmd)
{
if (s_func.hand_queue_count == 0) {
return 0;
}
*cmd = s_func.hand_queue[s_func.hand_queue_head];
s_func.hand_queue_head = (uint8_t)((s_func.hand_queue_head + 1)
% BOARD_HAND_QUEUE_LEN);
s_func.hand_queue_count--;
return 1;
}
/**
* @brief 下发一轮手动动作(驱动层唯一出口)
* @param cmd HAND_MODE_1/2/3
* @return 无
*/
static void function_hand_apply(uint8_t cmd)
{
app_mode_stop(); /* 清掉上一轮残留,含 cmd=1 借用的自动引擎 */
gDevData.hand_mode = cmd;
switch (cmd) {
case HAND_MODE_1:
app_mode_start(APP_MODE_UPPER_ONLY);
break;
case HAND_MODE_2:
bsp_drive_set(BSP_MOTOR_STOP, 0, BSP_MOTOR_FORWARD, 8000);
break;
case HAND_MODE_3:
bsp_drive_set(BSP_MOTOR_STOP, 0, BSP_MOTOR_BACKWARD, 8000);
break;
default:
bsp_drive_stop();
break;
}
}
/**
* @brief 当前手动动作的单轮时长
* @return 毫秒
*/
static uint16_t function_hand_action_ms(void)
{
if (gDevData.hand_mode == HAND_MODE_2 || gDevData.hand_mode == HAND_MODE_3) {
return BOARD_HAND_STEP_MS; /* 下电机:一轮 = 半圈 */
}
return BOARD_HAND_PULSE_MS; /* 上电机波形:保留 5s 上限 */
}
/**
* @brief 停手动 + 自动模式,并清 DP1
* @return 无
@@ -363,45 +265,27 @@ static void function_play_mode_apply(uint8_t mode)
{
uint8_t old_mode = s_func.play_mode;
MAGIC_AUDIO_LOG("模式切换请求:旧=%u,请求=%u,DP4=%u,功能开关=%u,充电=%u,关机中=%uOTA=%u",
old_mode, mode, gDevData.beep_trig, s_func.app_power,
app_power_usb_online(), app_power_poweroff_pending(), app_led_is_ota());
if (mode > PLAY_MODE_5)
{
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_led_is_ota())
{
if (app_power_usb_online() || !s_func.app_power || app_power_poweroff_pending()
|| app_led_is_ota()) {
mode = 0;
}
if (mode != 0 && s_func.hand_busy)
{
if (mode != 0 && s_func.hand_busy) {
function_hand_stop(); /* 手动切到自动:先停手动点动,再进自动模式 */
}
s_func.play_mode = mode;
s_func.play_ms = 0; /* 每次切模式都重新计 10 分钟 */
gDevData.play_mode = mode;
if (mode == 0)
{
if (mode == 0) {
app_mode_stop();
}
else
{
} else {
app_mode_start(mode);
}
// MAGIC_AUDIO_LOG("模式切换结果:旧=%u,实际=%u;当前测试不拦截DP4", old_mode, mode);
if (mode != old_mode)
{
if (mode != old_mode) {
app_led_request_mode_blink(); /* 模式真变了才闪两下 */
/* DP4:0=关闭模式切换鸟叫,1=允许播放 */
//&& gDevData.beep_trig
if ((mode != PLAY_MODE_0) && gDevData.beep_trig)
{
Car_Audio_Test_Play();
}
}
}
@@ -415,23 +299,20 @@ static void function_play_mode_poll(void)
{
uint8_t mode;
if (!s_func.play_write_pending)
{
if (!s_func.play_write_pending) {
return;
}
s_func.play_write_pending = 0;
if (app_power_usb_online() || !s_func.app_power || app_power_poweroff_pending() || app_led_is_ota())
{
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;
}
mode = s_func.play_write_mode;
if (mode > PLAY_MODE_5)
{
if (mode > PLAY_MODE_5) {
mode = 0;
}
if (mode != PLAY_MODE_0 && mode == s_func.play_mode)
{
if (mode != PLAY_MODE_0 && mode == s_func.play_mode) {
mode = PLAY_MODE_0;
}
function_play_mode_apply(mode);
@@ -447,13 +328,11 @@ static void function_play_mode_poll(void)
*/
static void function_play_mode_timeout(uint16_t dt)
{
if (s_func.play_mode == 0)
{
if (s_func.play_mode == 0) {
return;
}
s_func.play_ms += dt;
if (s_func.play_ms < BOARD_AUTO_MODE_MAX_MS)
{
if (s_func.play_ms < BOARD_AUTO_MODE_MAX_MS) {
return;
}
log_info("auto mode %d run %ums, timeout to standby\n",
@@ -462,7 +341,7 @@ static void function_play_mode_timeout(uint16_t dt)
}
/**
* @brief DP2 手动:只看第 1 字节。0=上下都停,1=仅上电机往复,2=仅下电机顺时针,3=仅下电机逆时针
* @brief DP2 手动:只看第 1 字节。0=空闲,1=顺时针,2=逆时针,3=停
* @return 无
*/
static void function_hand_poll(void)
@@ -471,36 +350,29 @@ static void function_hand_poll(void)
uint8_t got_write = s_func.hand_write_pending;
/* 优先用 DP 写入钩子带来的边沿;没有新写入才读协议层当前值 */
if (got_write)
{
if (got_write) {
s_func.hand_write_pending = 0;
cmd = s_func.hand_write_cmd;
}
else
{
} else {
cmd = gDevData.hand_mode;
}
if (app_power_usb_online() || !s_func.app_power || app_power_poweroff_pending() || app_led_is_ota())
{
if (s_func.hand_busy)
{
if (app_power_usb_online() || !s_func.app_power || app_power_poweroff_pending()
|| app_led_is_ota()) {
if (s_func.hand_busy) {
app_function_stop_all();
}
s_func.hand_cmd_last = cmd;
return;
}
if (!got_write)
{
if (!got_write) {
s_func.hand_cmd_last = cmd;
return;
}
if (cmd == HAND_MODE_0)
{
if (s_func.hand_busy)
{
if (cmd == HAND_MODE_0) {
if (s_func.hand_busy) {
function_hand_stop();
app_led_request_mode_blink();
}
@@ -508,32 +380,33 @@ static void function_hand_poll(void)
return;
}
if (cmd >= HAND_MODE_1 && cmd <= HAND_MODE_3) {
if (s_func.hand_busy) {
/* 正在动作中:不打断、不清零计时,只把新指令排到队尾 */
function_hand_queue_push(cmd);
s_func.hand_cmd_last = cmd;
return;
}
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;
function_hand_apply(cmd);
app_led_request_mode_blink();
if (gDevData.beep_trig) {
Car_Audio_Test_Play();
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 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;
}
/**
@@ -542,12 +415,12 @@ static void function_hand_poll(void)
*/
static void function_on_click(void)
{
if (s_func.hand_busy)
{
if (s_func.hand_busy) {
log_info("key click ignored, hand busy\n");
return;
}
function_play_mode_apply((s_func.play_mode >= PLAY_MODE_5) ? PLAY_MODE_1 : (uint8_t)(s_func.play_mode + 1));
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);
}
@@ -557,25 +430,20 @@ static void function_on_click(void)
*/
void usr_on_key_short(void)
{
if (app_power_poweroff_pending())
{
if (app_power_poweroff_pending()) {
return;
}
if (app_power_usb_online())
{
if (app_power_usb_online()) {
log_info("key click ignored, charging\n");
return;
}
if (app_led_is_ota())
{
if (app_led_is_ota()) {
log_info("key click ignored, ota\n");
return;
}
if (!s_func.app_power)
{
if (!s_func.app_power) {
/* 软关状态下短按:只唤醒功能,不切模式 */
if (function_power_on(1))
{
if (function_power_on(1)) {
log_info("key click cancel soft off\n");
}
return;
@@ -589,17 +457,14 @@ void usr_on_key_short(void)
*/
void usr_on_key_long(void)
{
if (app_power_poweroff_pending())
{
if (app_power_poweroff_pending()) {
return;
}
if (app_power_usb_online())
{
if (app_power_usb_online()) {
log_info("key hold 3s ignored, charging\n");
return;
}
if (app_led_is_ota())
{
if (app_led_is_ota()) {
log_info("key hold 3s, exit ota and poweroff\n");
usr_app_ota_exit();
app_led_exit_ota();
@@ -614,12 +479,10 @@ void usr_on_key_long(void)
static uint8_t function_power_on(uint8_t show_hint)
{
if (app_power_usb_online() || app_power_poweroff_pending() ||
app_led_is_ota())
{
app_led_is_ota()) {
return 0;
}
if (s_func.app_power)
{
if (s_func.app_power) {
gDevData.power = 1;
return 1;
}
@@ -629,8 +492,7 @@ static uint8_t function_power_on(uint8_t show_hint)
gDevData.power = 1;
app_shake_wake_reset();
usr_ble_set_standby(0);
if (show_hint)
{
if (show_hint) {
app_led_hint_power_on();
}
return 1;
@@ -639,7 +501,7 @@ static uint8_t function_power_on(uint8_t show_hint)
static uint8_t function_shake_wake_allowed(void)
{
return s_func.shake_wake &&
!app_no_disturb_is_active() &&
!s_func.no_disturb &&
!s_func.app_power &&
!app_power_usb_online() &&
!app_power_poweroff_pending() &&
@@ -648,8 +510,7 @@ static uint8_t function_shake_wake_allowed(void)
static void function_shake_wake_trigger(void)
{
if (function_power_on(1))
{
if (function_power_on(1)) {
log_info("shake wake power on\n");
}
}
+7 -4
View File
@@ -23,22 +23,25 @@
*/
void app_function_init(void);
/** CBC4 DP0 写入:排队到业务节拍统一执行。 */
/** CBC3 DP0 写入:排队到业务节拍统一执行。 */
void app_function_dp_power_write(uint8_t enable);
/** CBC4 DP1 写入:支持重复下发当前模式时停止。 */
/** CBC3 DP1 写入:支持重复下发当前模式时停止。 */
void app_function_dp_play_write(uint8_t mode);
/**
* @brief DP2 写入钩子:仅记录第 1 字节,真正执行在 app_function_poll
* @param cmd 0=停止1=上部电机探出,2=下部电机探出
* @param cmd 0=忽略1=原地右转,2=原地左转
* @return 无
*/
void app_function_dp_hand_write(uint8_t cmd);
/** CBC4 DP6 震动开机开关。 */
/** 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 无
+1 -1
View File
@@ -1,6 +1,6 @@
/******************************************************************************
* @file app_led.c
* @brief CBC4 灯效适配:充电红与待机绿可同时亮,不再 if-else 互斥
* @brief CBC2 灯效适配:充电红与待机绿可同时亮,不再 if-else 互斥
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
* @version V2.0.0
+1 -1
View File
@@ -1,6 +1,6 @@
/******************************************************************************
* @file app_led.h
* @brief CBC4 灯效:红绿独立通道,充电红与待机绿可同时亮
* @brief CBC2 灯效:红绿独立通道,充电红与待机绿可同时亮
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
* @version V2.0.0
+4 -9
View File
@@ -28,7 +28,6 @@ typedef struct {
uint8_t initialized;
uint8_t active;
uint8_t output_valid;
uint8_t upper_only; /* 1=只输出上电机,下电机恒停 */
uint8_t upper_phase;
uint8_t lower_pattern;
uint8_t lower_segment;
@@ -271,10 +270,8 @@ static void app_mode5_process(uint32_t now_ms)
app_mode5_select_lower_pattern(now_ms);
}
}
app_mode_outputs_set(s_mode.upper_direction, s_mode.upper_speed,
s_mode.upper_only ? BSP_MOTOR_STOP : s_mode.lower_direction,
s_mode.upper_only ? 0 : MAGIC_BOX_HIGH_PERCENT);
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)
@@ -300,13 +297,12 @@ AppMode_Ret_t app_mode_start(uint8_t mode)
if (!s_mode.initialized) {
return APP_MODE_ERR_NOT_INIT;
}
if (mode < 1 || mode > APP_MODE_UPPER_ONLY) { /* 放宽到 6 */
if (mode < 1 || mode > 5) {
app_mode_stop();
return APP_MODE_ERR_PARAM;
}
s_mode.upper_only = (mode == APP_MODE_UPPER_ONLY);
s_mode.active = s_mode.upper_only ? 5 : mode; /* 内部仍按模式 5 跑 */
s_mode.active = mode;
app_mode_start_current(timer_get_ms());
log_info("magic box mode %d start\n", mode);
return APP_MODE_OK;
@@ -315,7 +311,6 @@ AppMode_Ret_t app_mode_start(uint8_t mode)
void app_mode_stop(void)
{
s_mode.active = 0;
s_mode.upper_only = 0;
s_mode.output_valid = 0;
bsp_drive_stop();
}
-5
View File
@@ -12,14 +12,9 @@
#ifndef __APP_MODE_H__
#define __APP_MODE_H__
#define APP_MODE_UPPER_ONLY 6
#include <stdint.h>
/** 特殊玩法编号:复用模式 5 的上电机波形,但下电机恒停(供 DP2=1 使用) */
typedef enum {
APP_MODE_OK = 0,
APP_MODE_ERR_PARAM,
-145
View File
@@ -1,145 +0,0 @@
#include "app_no_disturb.h"
#include "system/includes.h"
#include "usr_rtc.h"
#define APP_NO_DISTURB_VM_ID 28U
#define APP_NO_DISTURB_MAGIC 0x4e44U
#define APP_NO_DISTURB_VERSION 1U
#define APP_MINUTES_PER_DAY (24U * 60U)
typedef struct __attribute__((packed)) {
uint16_t magic;
uint8_t version;
uint8_t cfg[APP_NO_DISTURB_CFG_LEN];
} AppNoDisturbVm_t;
typedef struct {
uint8_t enabled;
uint8_t weekdays_mask;
uint16_t start_minute;
uint16_t end_minute;
} AppNoDisturbCtx_t;
static AppNoDisturbCtx_t s_no_disturb;
static uint8_t app_no_disturb_cfg_valid(const uint8_t cfg[APP_NO_DISTURB_CFG_LEN])
{
uint8_t i;
if (!cfg || cfg[0] > 1U || cfg[9] >= 24U || cfg[11] >= 24U ||
cfg[10] >= 60U || cfg[12] >= 60U) {
return 0;
}
for (i = 0; i < 7U; i++) {
if (cfg[2U + i] > 1U) {
return 0;
}
}
return 1;
}
void app_no_disturb_configure(uint8_t enable, uint8_t weekdays_mask,
uint16_t start_minute, uint16_t end_minute)
{
s_no_disturb.enabled = !!enable;
s_no_disturb.weekdays_mask = weekdays_mask & 0x7fU;
s_no_disturb.start_minute = start_minute;
s_no_disturb.end_minute = end_minute;
}
uint8_t app_no_disturb_storage_load(uint8_t cfg[APP_NO_DISTURB_CFG_LEN])
{
AppNoDisturbVm_t vm;
int ret;
if (!cfg) {
return 0;
}
memset(&vm, 0, sizeof(vm));
ret = syscfg_read(APP_NO_DISTURB_VM_ID, &vm, sizeof(vm));
if (ret != (int)sizeof(vm) || vm.magic != APP_NO_DISTURB_MAGIC ||
vm.version != APP_NO_DISTURB_VERSION || !app_no_disturb_cfg_valid(vm.cfg)) {
memset(cfg, 0, APP_NO_DISTURB_CFG_LEN);
return 0;
}
memcpy(cfg, vm.cfg, APP_NO_DISTURB_CFG_LEN);
return 1;
}
uint8_t app_no_disturb_storage_save(const uint8_t cfg[APP_NO_DISTURB_CFG_LEN])
{
AppNoDisturbVm_t vm;
if (!app_no_disturb_cfg_valid(cfg)) {
return 0;
}
memset(&vm, 0, sizeof(vm));
vm.magic = APP_NO_DISTURB_MAGIC;
vm.version = APP_NO_DISTURB_VERSION;
memcpy(vm.cfg, cfg, APP_NO_DISTURB_CFG_LEN);
return (uint8_t)(syscfg_write(APP_NO_DISTURB_VM_ID, &vm, sizeof(vm)) ==
(int)sizeof(vm));
}
static uint8_t app_no_disturb_weekday_monday0(const struct sys_time *time)
{
static const uint8_t month_offset[12] = {
0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4
};
uint32_t year = time->year;
uint8_t sunday0;
if (time->month < 3U) {
year--;
}
sunday0 = (uint8_t)((year + year / 4U - year / 100U + year / 400U +
month_offset[time->month - 1U] + time->day) % 7U);
return (uint8_t)((sunday0 + 6U) % 7U);
}
static uint8_t app_no_disturb_day_selected(uint8_t weekday)
{
return (uint8_t)((s_no_disturb.weekdays_mask & (1U << weekday)) != 0U);
}
uint8_t app_no_disturb_is_active_at(const struct sys_time *time)
{
uint8_t weekday;
uint16_t now_minute;
if (!time || !s_no_disturb.enabled || !s_no_disturb.weekdays_mask ||
s_no_disturb.start_minute >= APP_MINUTES_PER_DAY ||
s_no_disturb.end_minute >= APP_MINUTES_PER_DAY ||
time->month < 1U || time->month > 12U || time->day < 1U ||
time->day > 31U || time->hour > 23U || time->min > 59U) {
return 0;
}
weekday = app_no_disturb_weekday_monday0(time);
now_minute = (uint16_t)(time->hour * 60U + time->min);
if (s_no_disturb.start_minute == s_no_disturb.end_minute) {
return app_no_disturb_day_selected(weekday);
}
if (s_no_disturb.start_minute < s_no_disturb.end_minute) {
return app_no_disturb_day_selected(weekday) &&
now_minute >= s_no_disturb.start_minute &&
now_minute < s_no_disturb.end_minute;
}
if (now_minute >= s_no_disturb.start_minute) {
return app_no_disturb_day_selected(weekday);
}
if (now_minute < s_no_disturb.end_minute) {
return app_no_disturb_day_selected((uint8_t)((weekday + 6U) % 7U));
}
return 0;
}
uint8_t app_no_disturb_is_active(void)
{
struct sys_time time;
if (!usr_rtc_get_time(&time)) {
return 0;
}
return app_no_disturb_is_active_at(&time);
}
-17
View File
@@ -1,17 +0,0 @@
#ifndef __APP_NO_DISTURB_H__
#define __APP_NO_DISTURB_H__
#include <stdint.h>
#include "system/sys_time.h"
#define APP_NO_DISTURB_CFG_LEN 13
/* DP7: bytes 2..8 are Monday..Sunday, byte 1 is reserved. */
void app_no_disturb_configure(uint8_t enable, uint8_t weekdays_mask,
uint16_t start_minute, uint16_t end_minute);
uint8_t app_no_disturb_storage_load(uint8_t cfg[APP_NO_DISTURB_CFG_LEN]);
uint8_t app_no_disturb_storage_save(const uint8_t cfg[APP_NO_DISTURB_CFG_LEN]);
uint8_t app_no_disturb_is_active(void);
uint8_t app_no_disturb_is_active_at(const struct sys_time *time);
#endif /* __APP_NO_DISTURB_H__ */
+30 -71
View File
@@ -36,21 +36,14 @@
#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 CHG_ON_CONFIRM_MS 50u
#define CHG_OFF_CONFIRM_MS 150u
#define CHG_FULL_RECHECK_MS 1000u
#define BAT_LOW_WIN 3 /* 连续约 2.4s 才进低电告警(仅闪灯,不关机) */
/** TP4056 CHRG/STDBY debounced charging state. */
/** 充电检测:CHG/VPWR 消抖后的结果 */
typedef struct {
uint8_t usb_online;
uint8_t charge_led;
uint8_t charge_led_last;
uint16_t charging_ms;
uint16_t unplugged_ms;
uint16_t full_ms;
uint16_t full_recheck_ms;
uint8_t full_latched;
uint8_t charge_led; /**< 0=无 1=充电红 2=充满绿 */
uint8_t charge_led_last; /**< 仅用于变化时打日志 */
uint16_t full_ms; /**< 拔电瞬间 CHG 先变高,需等满 1s 才当充满 */
} AppChargeState_t;
/** 电量采样:滑动平均 + 百分比 */
@@ -228,7 +221,7 @@ static void power_do_poweroff(void *priv)
}
/**
* @brief TP4056 CHRG/STDBY 组合检测,稳定状态变化后报告 USB 事件
* @brief 充电检测:CHG 低=充电中;VPWR 在且 CHG 高满 1s=充满;拔电报 USB_OUT
* @param dt 距上次调用的毫秒数
* @return USB 插拔边沿事件
*/
@@ -236,71 +229,37 @@ static AppPowerEvt_t power_charge_tick(uint16_t dt)
{
AppChargeState_t *c = &s_power.charge;
uint8_t chg_low = bsp_chg_is_low();
uint8_t stby_low = bsp_stby_is_low();
uint8_t usb = c->usb_online;
uint8_t vpwr = bsp_vpwr_is_online();
uint8_t usb;
AppPowerEvt_t evt = APP_POWER_EVT_NONE;
if (chg_low && stby_low) {
/* 00 is invalid: keep the previous stable state. */
c->charging_ms = 0;
c->unplugged_ms = 0;
if (chg_low) {
/* CHG 拉低:正在充电,红灯 */
c->full_ms = 0;
c->full_recheck_ms = 0;
} else if (!chg_low && !stby_low) {
/* 11: charger absent. */
c->charging_ms = 0;
c->full_ms = 0;
c->full_recheck_ms = 0;
c->unplugged_ms = app_add_ms(c->unplugged_ms, dt);
if (c->unplugged_ms >= CHG_OFF_CONFIRM_MS) {
c->charge_led = 0;
c->full_latched = 0;
usb = 0;
}
} else if (chg_low) {
/* 01: charging. */
c->unplugged_ms = 0;
c->full_ms = 0;
if (c->full_latched) {
c->charging_ms = 0;
c->full_recheck_ms = app_add_ms(c->full_recheck_ms, dt);
if (c->full_recheck_ms >= CHG_FULL_RECHECK_MS) {
c->full_latched = 0;
c->charge_led = 1;
}
usb = 1;
} else {
c->full_recheck_ms = 0;
c->charging_ms = app_add_ms(c->charging_ms, dt);
if (c->charging_ms >= CHG_ON_CONFIRM_MS) {
c->charge_led = 1;
usb = 1;
}
}
} else {
/* 10: charge complete. */
c->charging_ms = 0;
c->unplugged_ms = 0;
c->full_recheck_ms = 0;
if (c->full_latched) {
c->charge_led = 1;
gDevData.charge = CHARGE_1;
usb = 1;
} else if (vpwr) {
/* VPWR 在但 CHG 已高:可能是充满,也可能是拔电瞬间 CHG 先变高。
* 等满 BOARD_CHG_FULL_MS 才当真充满,避免拔电误报绿灯 */
c->full_ms = app_add_ms(c->full_ms, dt);
if (c->full_ms >= BOARD_CHG_FULL_MS) {
c->charge_led = 2;
usb = 1;
} else {
c->full_ms = app_add_ms(c->full_ms, dt);
if (c->full_ms >= BOARD_CHG_FULL_MS) {
c->charge_led = 2;
c->full_latched = 1;
usb = 1;
}
}
gDevData.charge = (c->charge_led == 1) ? CHARGE_1 : CHARGE_2;
usb = 1;
} else {
/* USB 不在位:充电灯灭 */
c->full_ms = 0;
c->charge_led = 0;
gDevData.charge = CHARGE_2;
usb = 0;
}
gDevData.charge = (c->charge_led == 1) ? CHARGE_1 : CHARGE_2;
if (usb != c->usb_online) {
c->usb_online = usb;
log_info("charge input %s chg_low=%d stby_low=%d pair=%d goto=%d\n",
usb ? "in" : "out", chg_low, stby_low,
log_info("usb %s vpwr=%d chg=%d mv=%d pair=%d goto=%d\n",
usb ? "in" : "out", vpwr, chg_low, bsp_vpwr_mv(),
usr_get_pair_flag(), usr_var.usr_goto_pair);
usr_var.usr_power_charge_flag = usb ? 1 : 2;
if (usb) {
@@ -313,10 +272,10 @@ static AppPowerEvt_t power_charge_tick(uint16_t dt)
if (c->charge_led != c->charge_led_last) {
c->charge_led_last = c->charge_led;
log_info("charge_led=%d chg_low=%d stby_low=%d\n",
c->charge_led, chg_low, stby_low);
log_info("charge_led=%d chg=%d vpwr=%d mv=%d\n",
c->charge_led, chg_low, vpwr, bsp_vpwr_mv());
if (c->charge_led == 2) {
/* STDBY 持续为低满 BOARD_CHG_FULL_MS判定充满,电量强制收尾到
/* 硬件(CHG+VPWR 满 BOARD_CHG_FULL_MS判定充满,电量强制收尾到
* 100%,避免电压拟合曲线因基准/温漂等原因走不到 100% 就卡住 */
app_battery_set_percent(100);
s_power.sample.pct = 100;
-51
View File
@@ -1,51 +0,0 @@
/******************************************************************************
* @file car_audio_test.c
* @brief Car audio hardware test using the SDK key-tone sine path.
*
* Notes:
* - The SDK sine sample rate is 16 kHz; points is the PCM sample count.
* - freq is specified as actual frequency * 512.
* - decay=0 keeps amplitude constant for observing DAC/PA end behavior.
* - The parameter array has static storage because playback is asynchronous.
******************************************************************************/
#include <stdint.h>
#include "system/includes.h"
#include "tone_player.h"
#include "application/audio_dec_app.h"
#include "car_audio_test.h"
#include "app_audio_debug.h"
static uint16_t s_car_audio_restart_guard_ms;
int Car_Audio_Test_Play(void)
{
/* bird.mp3 is packed as a standalone file in the internal resource area. */
int ret;
MAGIC_AUDIO_LOG("鸟叫接口进入:资源=%s,当前播放状态=%d",
SDFILE_RES_ROOT_PATH"bird.mp3", audio_key_tone_is_play());
ret = audio_key_tone_play_name(SDFILE_RES_ROOT_PATH"bird.mp3", 1);
MAGIC_AUDIO_LOG("鸟叫请求返回=%d(0仅表示请求已提交,不代表已经出声)", ret);
return ret;
}
void Car_Audio_Test_Loop(uint16_t dt_ms)
{
if (s_car_audio_restart_guard_ms > dt_ms) {
s_car_audio_restart_guard_ms -= dt_ms;
return;
}
if (audio_key_tone_is_play()) {
return;
}
if (Car_Audio_Test_Play() == 0) {
/*
* 防止播放请求刚入队、SDK 尚未更新播放状态时被重复提交。
*/
s_car_audio_restart_guard_ms = 50;
}
}
-26
View File
@@ -1,26 +0,0 @@
/******************************************************************************
* @file car_audio_test.h
* @brief Car audio hardware test API using the SDK key-tone sine path.
******************************************************************************/
#include <stdint.h>
#ifndef __CAR_AUDIO_TEST_H__
#define __CAR_AUDIO_TEST_H__
/**
* @brief Play the bird-call MP3 stored in the internal resource area.
*
* The API only queues a request. Playback is asynchronous in the SDK key-tone
* task; it does not wait for completion or block the caller. This test is not
* triggered automatically; call it explicitly from test code.
*
* @return 0=request queued; negative value=module unavailable/request failed.
*/
int Car_Audio_Test_Play(void);
/**
* @brief 开机状态下维护循环测试音。
* @param dt_ms 距离上次调用的毫秒数
*/
void Car_Audio_Test_Loop(uint16_t dt_ms);
#endif /* __CAR_AUDIO_TEST_H__ */
+1 -1
View File
@@ -1,6 +1,6 @@
/******************************************************************************
* @file key_manager.c
* @brief CBC4 按键:读 PB1,短按/长按交给 usr_jb_main
* @brief CBC2 按键:读 PB1,短按/长按交给 usr_jb_main
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
* @version V1.0.0
+1 -1
View File
@@ -1,6 +1,6 @@
/******************************************************************************
* @file key_manager.h
* @brief CBC4 单按键:短按切模式,长按 3s 关机;开机 3s/5s 不走本库
* @brief CBC2 单按键:短按切模式,长按 3s 关机;开机 3s/5s 不走本库
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
* @version V1.0.0
+2 -2
View File
@@ -1,6 +1,6 @@
/******************************************************************************
* @file led_manager.c
* @brief CBC4 灯事件:充电红与待机绿走不同通道,可同时亮
* @brief CBC2 灯事件:充电红与待机绿走不同通道,可同时亮
* @author cyWu <1917507415@qq.com>
* @date 2026.08.27
* @version V1.1.0
@@ -157,7 +157,7 @@ static uint8_t led_boot_reject_handler(LED_Config_t *led_config)
}
/**
* @brief 注册回调并装载 CBC4 事件表(GPIO 已在 bsp_hw_init
* @brief 注册回调并装载 CBC2 事件表(GPIO 已在 bsp_hw_init
* @return 无
*/
void user_led_init(void)
+2 -2
View File
@@ -1,6 +1,6 @@
/******************************************************************************
* @file led_manager.h
* @brief CBC4 红绿 LED 通道与事件表
* @brief CBC2 红绿 LED 通道与事件表
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
* @version V1.0.0
@@ -20,7 +20,7 @@ typedef enum {
} LedChannel_t;
/**
* @brief 注册 GPIO 回调并装载 CBC4 事件表
* @brief 注册 GPIO 回调并装载 CBC2 事件表
* @return 无
*/
void user_led_init(void);
+65 -19
View File
@@ -1,6 +1,6 @@
/* ================================================================
* jb_product.c — 用户层实现(请填补空白处)
* 设备: 弹力魔盒 | 生成时间: 2026-08-18 16:24:47
* 设备: 猫猫捕猎罩 | 生成时间: 2026-08-18 16:24:47
*
* ══ 开发流程 ══
* 1. 实现硬件抽象: jbGetTimerCount(定时器由 SOC SDK 提供)
@@ -14,7 +14,7 @@
#include "jb_product.h"
#include "app_function.h"
#include "app_no_disturb.h"
#include "usr_rtc.h"
/* ════════════════════════════════════════════════════════════════
* 全局变量
@@ -30,26 +30,70 @@ static volatile uint32_t timerMs;
#define JB_DND_START_MIN_OFFSET 10
#define JB_DND_END_HOUR_OFFSET 11
#define JB_DND_END_MIN_OFFSET 12
static void jbNoDisturbApply(uint8_t *cfg)
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;
uint8_t weekdays = 0;
uint16_t start_minute;
uint16_t end_minute;
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]) {
weekdays |= (uint8_t)(1U << i);
return 1;
}
}
start_minute = (uint16_t)(cfg[JB_DND_START_HOUR_OFFSET] * 60U +
cfg[JB_DND_START_MIN_OFFSET]);
end_minute = (uint16_t)(cfg[JB_DND_END_HOUR_OFFSET] * 60U +
cfg[JB_DND_END_MIN_OFFSET]);
app_no_disturb_configure(cfg[JB_DND_ENABLE_OFFSET], weekdays,
start_minute, end_minute);
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);
}
/* ════════════════════════════════════════════════════════════════
@@ -63,14 +107,14 @@ void userInit(void)
gDevData.play_mode = 0; /* PLAY_MODE_0 (停止复位) */
gDevData.hand_mode = 0; /* HAND_MODE_0 (忽略) */
gDevData.alm_low_battery = 0; /* DP3 低电量报警 0=关 1=开 */
gDevData.beep_trig = 1; /* DP4 声音开关 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, APP_NO_DISTURB_CFG_LEN); /* DP7 勿扰模式开关 */
app_no_disturb_storage_load(gDevData.no_disturb_enable);
memset(gDevData.no_disturb_enable, 0, 13); /* DP7 勿扰模式开关 */
gDevData.charge = 0; /* CHARGE_0 (不支持充电) */
gDevData.reserva_dp3 = 0; /* DP10 预留DP3 */
jbNoDisturbApply(gDevData.no_disturb_enable);
jbLastNoDisturbActive = 0xff;
jbNoDisturbUpdate();
}
/* ════════════════════════════════════════════════════════════════
@@ -79,6 +123,8 @@ void userInit(void)
* ════════════════════════════════════════════════════════════════ */
void userHandle(void)
{
jbNoDisturbUpdate();
/* ── 可上报 DP (RO / RW): 用户数据采集 ── */
// gDevData.power = read_sensor(); /* DP0 开关 */
// gDevData.play_mode = read_sensor(); /* DP1 自动模式 */
@@ -150,8 +196,8 @@ int8_t jbEventProcess(jb_event_info_t *info, jb_data_point_t *ctrl)
case DP_ID_NO_DISTURB_ENABLE:
memcpy(gDevData.no_disturb_enable, ctrl->no_disturb_enable,
JB_DND_LEN);
jbNoDisturbApply(gDevData.no_disturb_enable);
app_no_disturb_storage_save(gDevData.no_disturb_enable);
jbNoDisturbNormalize(gDevData.no_disturb_enable);
jbNoDisturbUpdate();
break;
/* ── DP10 预留DP3 (uint32) ── */
+1 -1
View File
@@ -1,6 +1,6 @@
/* ================================================================
* jb_product.h — 用户层头文件(自动生成)
* 设备: 弹力魔盒 | 生成时间: 2026-08-18 16:24:47
* 设备: 猫猫捕猎罩 | 生成时间: 2026-08-18 16:24:47
*
* 声明用户需实现的函数。数据结构定义见 jb_protocol.h
* ================================================================
+1 -1
View File
@@ -1,6 +1,6 @@
/* ================================================================
* jb_protocol.c — 见宝 IOT 协议实现(自动生成)
* 设备: 弹力魔盒 | 生成时间: 2026-08-18 16:24:47
* 设备: 猫猫捕猎罩 | 生成时间: 2026-08-18 16:24:47
* ================================================================
*/
+4 -6
View File
@@ -1,6 +1,6 @@
/* ================================================================
* jb_protocol.h — 见宝 IOT 协议层(自动生成)
* 设备: 弹力魔盒 | DP 数: 10 | Bitmap: 2B
* 设备: 猫猫捕猎罩 | DP 数: 10 | Bitmap: 2B
* 协议版本: V1.0 | 生成时间: 2026-08-18 16:24:47
*
* 帧格式: [55 AA] [LEN_H LEN_L] [CMD] [SN] [payload...] [CKSUM]
@@ -159,13 +159,11 @@ typedef enum
/* 手动模式 */
typedef enum
{
HAND_MODE_0 = 0, /* 上下电机都停 */
HAND_MODE_1 = 1, /* 仅上电机:按玩法5波形往复 */
HAND_MODE_2 = 2, /* 仅下电机:顺时针 */
HAND_MODE_3 = 3, /* 仅下电机:逆时针 */
HAND_MODE_0 = 0, /* 停 */
HAND_MODE_1 = 1, /* 上部电机探出 */
HAND_MODE_2 = 2, /* 下部电机探出 */
} HAND_MODE_t;
/* 充电状态 */
typedef enum
{
+1 -11
View File
@@ -1,6 +1,6 @@
/******************************************************************************
* @file usr_jb_main.c
* @brief CBC4 弹力魔盒业务组合根:只做调用编排,不含具体业务算法
* @brief CBC1 猫猫捕猎罩业务组合根:只做调用编排,不含具体业务算法
* @author cyWu <1917507415@qq.com>
* @date 2026.08.27
* @version V2.3.0
@@ -36,7 +36,6 @@
#include "system/includes.h"
#include "app_config.h"
#include "asm/dac.h"
#include "app_main.h"
#include "app_task.h"
#include "user_cfg.h"
@@ -53,13 +52,11 @@
#include "app_pair.h"
#include "app_boot.h"
#include "app_function.h"
#include "app_audio_debug.h"
#include "app_mode.h"
#include "app_led.h"
#include "key_manager.h"
#include "asm/power/power_api.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[USR_JB]"
#define LOG_INFO_ENABLE
@@ -288,13 +285,6 @@ void usr_jb_init(void)
{
usr_log_boot_version(); /* UART 已在 app_main 起来,按键判定前就能看到版本 */
MAGIC_AUDIO_LOG("诊断固件启动:编译=%s %s,UART0使能=%dTX引脚编号=%u,波特率=%u",
__DATE__, __TIME__, TCFG_UART0_ENABLE,
(unsigned)TCFG_UART0_TX_PORT, (unsigned)TCFG_UART0_BAUDRATE);
MAGIC_AUDIO_LOG("配置:提示音=%dMP3=%dDAC=%dDAC通道=%dDAC模式=%d,音量类型=%d",
TCFG_KEY_TONE_EN, TCFG_DEC_MP3_ENABLE, TCFG_AUDIO_DAC_ENABLE,
TCFG_AUDIO_DAC_CONNECT_MODE, TCFG_AUDIO_DAC_MODE, SYS_VOL_TYPE);
MAGIC_AUDIO_LOG("鸟叫资源路径=%s,中文显示请使用UTF-8", SDFILE_RES_ROOT_PATH"bird.mp3");
bsp_hw_init();
app_led_init(); /* 提前到按键判定之前:3s/5s/拒绝开机低电闪都要直接用灯效事件表 */
+11 -14
View File
@@ -1,6 +1,6 @@
/******************************************************************************
* @file board_pin.h
* @brief CBC4 弹力魔盒板级引脚、电平与时序(换板只改本文件)
* @brief CBC1 猫猫捕猎罩板级引脚、电平与时序(换板只改本文件)
* @author cyWu <1917507415@qq.com>
* @date 2026.08.27
* @version V1.5.0
@@ -26,7 +26,7 @@
#define BOARD_LED_ENABLE 1 /**< 1=红绿 LED */
#define BOARD_KEY_ENABLE 1 /**< 1=按键 PB1 */
#define BOARD_CHG_ENABLE 1 /**< 1=TP4056: PG8 CHRG + PC5 STDBY */
#define BOARD_CHG_ENABLE 1 /**< 1=充电检测 PG8 + VPWR */
#define BOARD_MOTOR_ENABLE 1 /**< 1=双电机 U13 + U18 */
#define BOARD_SHAKE_ENABLE 1 /**< 1=震动输入 PA3 */
@@ -39,16 +39,14 @@
#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_07 /**< 红灯 LED_PWR,低电平亮 */
#define BOARD_LED_GREEN_PIN IO_PORTA_08 /**< 绿灯 LED_CHG,低电平亮 */
#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 /**< TP4056 CHRG, active low, internal pull-up */
#define BOARD_STDBY_PIN IO_PORTC_05 /**< TP4056 STDBY, active low, internal pull-up */
#define BOARD_CHG_PIN IO_PORTG_08 /**< ME4054 CHG,外部 2.2k 上拉 */
#define BOARD_LED_ON_LEVEL 0 /**< LED 点亮电平:0=低亮 */
#define BOARD_KEY_ACTIVE_LEVEL 0 /**< 按键有效电平:0=按下为低 */
#define BOARD_CHG_CHARGING_LEVEL 0 /**< CHG 充电中为低 */
#define BOARD_STDBY_FULL_LEVEL 0 /**< STDBY 充满时为低 */
#define BOARD_SHAKE_ACTIVE_LEVEL 1 /**< 常闭震动开关:断开时高脉冲有效 */
@@ -64,9 +62,9 @@
#define BOARD_VBAT_LOW_HYST_MV 50 /**< 低电恢复回差 */
#define BOARD_VBAT_SHUTDOWN_MV 3250 /**< 约 5%,仅开机拦截用:关机状态下 app_battery 尚未运行,只能摸裸电压 */
#define BOARD_BATTERY_SHUTDOWN_PCT 5 /**< 运行中显示电量 <= 此值才自动关机,跟 APP 看到的百分比对齐 */
#define BOARD_VPWR_ONLINE_MV 4000
#define BOARD_VPWR_OFF_MV 1500
#define BOARD_CHG_FULL_MS 1000 /**< STDBY low confirmation time */
#define BOARD_VPWR_ONLINE_MV 4000 /**< VPWR 判定 USB 插入 */
#define BOARD_VPWR_OFF_MV 1500 /**< 低于此值认定已拔出 */
#define BOARD_CHG_FULL_MS 1000 /**< CHG 已高且 VPWR 在,满 1s 认充满 */
/*============================================================================*/
/* 按键 / 灯效 */
@@ -107,9 +105,7 @@
#define BOARD_MOTOR_REVERSE_MS 1 /**< 换向前先松开,防 H 桥直通 */
#define BOARD_HAND_PULSE_MS 5000 /**< 手动右转/左转单次最长 5s */
#define BOARD_HAND_STEP_MS 1500 /**< DP2 下电机单轮动作时长(=半圈),按实测标定 */
#define BOARD_HAND_QUEUE_LEN 8
#define BOARD_MOTOR_PWM_HZ 200 /**< 弹力魔盒验证过的 MCPWM 频率(Hz) */
#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=保持开着,方便对比待机电流 */
@@ -128,7 +124,8 @@
#define BOARD_SHAKE_TRIGGER_WINDOW_MS 250u /**< 保留窗口参数,便于恢复多脉冲策略 */
#define BOARD_SHAKE_POLL_MS 10u /**< 与业务调度器共用的采样节拍 */
/* PG8/CHRG is used to detect charge insertion while in softoff. */
/* 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__ */
+4 -21
View File
@@ -1,6 +1,6 @@
/******************************************************************************
* @file bsp_hw.c
* @brief CBC4 板级 GPIO / ADC上下双电机 H 桥、震动、LED、按键、充电检测
* @brief CBC1 板级 GPIO / ADC电机 H 桥、震动、LED、按键、充电检测
* @author cyWu <1917507415@qq.com>
* @date 2026.08.24
* @version V1.5.0
@@ -233,20 +233,13 @@ BspHw_Ret_t bsp_hw_init(void)
#if BOARD_CHG_ENABLE
/* PORTG 作数字输入必须开 dieh,否则读不到 ME4054 CHG */
gpio_disable_fun_output_port(BOARD_CHG_PIN);
/* ME4054 CHRG is open-drain/high-Z when not charging. Keep PG8 pulled
* high so the digital input has a deterministic level in that state. */
gpio_set_pull_up(BOARD_CHG_PIN, 1);
gpio_set_pull_up(BOARD_CHG_PIN, 0);
gpio_set_pull_down(BOARD_CHG_PIN, 0);
gpio_set_die(BOARD_CHG_PIN, 1);
gpio_set_dieh(BOARD_CHG_PIN, 1);
gpio_direction_input(BOARD_CHG_PIN);
gpio_disable_fun_output_port(BOARD_STDBY_PIN);
gpio_set_pull_up(BOARD_STDBY_PIN, 1);
gpio_set_pull_down(BOARD_STDBY_PIN, 0);
gpio_set_die(BOARD_STDBY_PIN, 1);
gpio_set_dieh(BOARD_STDBY_PIN, 1);
gpio_direction_input(BOARD_STDBY_PIN);
adc_add_sample_ch(AD_CH_LDO5V);
adc_set_sample_freq(AD_CH_LDO5V, 500);
#endif
#if BOARD_MOTOR_ENABLE
@@ -430,16 +423,6 @@ uint8_t bsp_chg_is_low(void)
#endif
}
/** @return 1 when TP4056 STDBY is low; otherwise 0. */
uint8_t bsp_stby_is_low(void)
{
#if BOARD_CHG_ENABLE
return (uint8_t)(gpio_read(BOARD_STDBY_PIN) == BOARD_STDBY_FULL_LEVEL);
#else
return 0;
#endif
}
/**
* @brief 读取电池电压
* @return 电池电压,单位 mV
+3 -7
View File
@@ -1,6 +1,6 @@
/******************************************************************************
* @file bsp_hw.h
* @brief CBC4 板级硬件:上下双电机 / 震动 / LED / 按键 / 充电 / 电量
* @brief CBC1 板级硬件:电机 / 震动 / LED / 按键 / 充电 / 电量
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
* @version V1.6.0
@@ -67,8 +67,7 @@ void bsp_led_all_off(void);
/**
* @brief 设置电机方向。STOP 时直接切断驱动,自由滑行到停(不做反接刹车)
* @note 占空比用 BOARD_MOTOR_PWM_DUTY(手动点动)
* @param left_dir/right_dir 左右轮方向
* @param left_duty/right_duty 左右轮占空比
* @param dir 收绳 / 放绳 / 停转
* @return 无
*/
void bsp_drive_set(BspMotorDir_t left_dir, uint16_t left_duty,
@@ -78,7 +77,7 @@ void bsp_drive_set(BspMotorDir_t left_dir, uint16_t left_duty,
void bsp_drive_stop(void);
/**
* @brief 兼容旧调用;弹力魔盒每个动作一次性更新上下电机四路 PWM
* @brief 兼容旧调用;猫猫捕猎罩每个动作更新左轮对应的两路 PWM
* @return 无
*/
void bsp_motor_switch_tick(void);
@@ -101,9 +100,6 @@ uint8_t bsp_key_is_pressed(void);
*/
uint8_t bsp_chg_is_low(void);
/** @return 1 when TP4056 STDBY is low; otherwise 0. */
uint8_t bsp_stby_is_low(void);
/**
* @brief 读取电池电压
* @return 电池电压,单位 mV
Binary file not shown.
@@ -0,0 +1 @@
3DBA0ACD41A68B534365C721F7B23619914B4106E1024B5592A0A49D1DD417152E4DCF18
@@ -4,7 +4,6 @@ cd %~dp0
copy ..\..\script.ver .
copy ..\..\tone.cfg .
copy ..\..\bird.mp3 .
copy ..\..\p11_code.bin .
copy ..\..\br28loader.bin .
copy ..\..\ota.bin .
@@ -16,7 +15,7 @@ if not exist p11_code.bin (
exit /b 1
)
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 bird.mp3 p11_code.bin -uboot_compress"
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"
if not exist RS-5B88.key (
echo [download] ERROR: RS-5B88.key not found
+62
View File
@@ -0,0 +1,62 @@
# SOC 代码生成模板 — 分支索引
本仓库按 **「厂商-型号」** 组织分支,每个分支是一套完整的 SOC 工程模板。
远程仓库:[soc-codegen-template](https://git.jianbao-tech.com/pets-device/soc-codegen-template)
---
## 分支列表
| 分支 | 厂商 | 型号 | 平台 | 默认能力 | 状态 |
|------|------|------|------|----------|------|
| [`JieLi-JL7016`](https://git.jianbao-tech.com/pets-device/soc-codegen-template/src/branch/JieLi-JL7016) | 杰理 JieLi | JL7016 / AC7016C | BR28 | 耳机 SDK + BLE + jb_protocol + OTA | ✅ 可用 |
> 新模板上线后在此表追加一行即可。
---
## 如何获取某 SOC 模板
### 见宝工具箱 / 自动化脚本
```bash
git clone https://git.jianbao-tech.com/pets-device/soc-codegen-template.git
cd soc-codegen-template
git checkout JieLi-JL7016
```
### 仅下载指定分支(浅克隆)
```bash
git clone --branch JieLi-JL7016 --single-branch \
https://git.jianbao-tech.com/pets-device/soc-codegen-template.git jl7016-template
```
---
## 分支命名规范
```
<厂商>-<型号>
```
示例:`JieLi-JL7016``JieLi-AC701N`
- 默认包含耳机应用与 jb_protocol,分支名**不加**多余后缀
- 同一芯片多种工程结构差异过大时,再考虑加后缀:`JieLi-JL7016-dongle`
---
## 新增 SOC 模板(维护者)
1.`main` 拉新分支:`git checkout -b <厂商>-<型号> main`
2. 合入该 SOC 完整工程(apps + cpu + include_lib + tools
3. 更新本文件分支表 + `main/README.md` 快速索引
4. 推送分支并在 Gitea 设为受保护分支(可选)
---
## 各分支 README
每个 SOC 分支根目录有独立的 `README.md`(构建、打包、协议对接说明)。
+176 -162
View File
@@ -1,164 +1,178 @@
{
"product": {
"name": "弹力魔盒",
"code": "PICBC4",
"type": "CBC4",
"protocol": "V1.0.0"
},
"dps": [
{
"id": 0,
"display_name": "开关",
"key": "power",
"kind": "normal",
"type": "bool",
"len": 1,
"scale": 1,
"dir": "rw",
"description": "设备总开关",
"values": {
"1": "",
"0": ""
}
},
{
"id": 1,
"display_name": "自动模式",
"key": "play_mode",
"kind": "normal",
"type": "enum",
"len": 1,
"scale": 1,
"dir": "rw",
"description": "5种玩法按键也可切换并上报",
"values": {
"5": "上下电机随机组合",
"4": "上部慢探、下部随机摇摆",
"0": "停止复位",
"3": "上部间歇、下部均匀摇摆",
"2": "上部快速往复",
"1": "上部慢速往复"
}
},
{
"id": 2,
"display_name": "手动模式",
"key": "hand_mode",
"kind": "normal",
"type": "enum",
"len": 1,
"scale": 1,
"dir": "wo",
"description": "上部或下部电机单次探出最长5秒",
"values": {
"2": "下部探出",
"1": "上部探出",
"0": "停止"
}
},
{
"id": 3,
"display_name": "低电量报警",
"key": "alm_low_battery",
"kind": "alarm",
"type": "bool",
"len": 1,
"scale": 1,
"dir": "ro",
"description": "低电量告警",
"values": {
"1": "告警",
"0": "正常"
},
"level": 1
},
{
"id": 4,
"display_name": "声音开关",
"key": "beep_trig",
"kind": "normal",
"type": "bool",
"len": 1,
"scale": 1,
"dir": "rw",
"description": "声音触发开关",
"values": {
"1": "开",
"0": "关"
}
},
{
"id": 5,
"display_name": "电量",
"key": "battery",
"kind": "normal",
"type": "uint8",
"len": 1,
"scale": 1,
"dir": "ro",
"description": "电量上报",
"values": {
}
},
{
"id": 6,
"display_name": "震动开机",
"key": "shake_wake",
"kind": "normal",
"type": "bool",
"len": 1,
"scale": 1,
"dir": "rw",
"description": "震动触发开机",
"values": {
"1": "开",
"0": "关"
}
},
{
"id": 7,
"display_name": "勿扰模式",
"key": "no_disturb_enable",
"kind": "normal",
"type": "raw",
"len": 13,
"scale": 1,
"dir": "rw",
"description": "APP控制勿扰模式",
"values": {
}
},
{
"id": 8,
"display_name": "充电状态",
"key": "charge",
"kind": "normal",
"type": "enum",
"len": 1,
"scale": 1,
"dir": "ro",
"description": "充电状态上报",
"values": {
"2": "未充电",
"1": "充电中",
"0": "不支持充电"
}
},
{
"id": 10,
"display_name": "预留DP3",
"key": "reserva_dp3",
"kind": "normal",
"type": "uint32",
"len": 4,
"scale": 1,
"dir": "rw",
"description": "预留",
"values": {
}
}
]
"product": {
"name": "弹力魔盒",
"code": "PICBC4",
"type": "CBC4",
"protocol": "V1.0.0"
},
"dps": [
{
"id": 0,
"display_name": "开关",
"key": "power",
"kind": "normal",
"type": "bool",
"len": 1,
"scale": 1.0,
"dir": "rw",
"description": "设备总开关",
"values": {
"0": "",
"1": ""
}
},
{
"id": 1,
"display_name": "自动模式",
"key": "play_mode",
"kind": "normal",
"type": "enum",
"len": 1,
"scale": 1.0,
"dir": "rw",
"description": "5种玩法,按键也可切换并上报",
"values": {
"0": "停止复位",
"1": "随机躲猫猫",
"2": "洞口接力跑",
"3": "快闪+快摇组合",
"4": "慢探+随机摇摆",
"5": "终极大乱斗"
}
},
{
"id": 2,
"display_name": "手动模式",
"key": "hand_mode",
"kind": "normal",
"type": "enum",
"len": 1,
"scale": 1.0,
"dir": "wo",
"description": "上部或下部电机单次探出,最长5秒",
"values": {
"0": "停止",
"1": "上部探出",
"2": "下部探出"
}
},
{
"id": 3,
"display_name": "绳索校准",
"key": "calibration",
"kind": "normal",
"type": "bool",
"len": 1,
"scale": 1.0,
"dir": "wo",
"description": "用来校准绳子位置",
"values": {
"0": "停止校准",
"1": "校准"
}
},
{
"id": 4,
"display_name": "绳索圈数",
"key": "numberturns",
"kind": "normal",
"type": "uint8",
"len": 1,
"scale": 1.0,
"dir": "wo",
"description": "根据不同的绳子设置圈数",
"values": {}
},
{
"id": 5,
"display_name": "低电量报警",
"key": "alm_low_battery",
"kind": "alarm",
"type": "bool",
"len": 1,
"scale": 1.0,
"dir": "ro",
"description": "<3.7V上报,1分钟后休眠",
"values": {
"0": "正常",
"1": "电量<20%告警"
},
"level": 1
},
{
"id": 6,
"display_name": "电量上报",
"key": "battery",
"kind": "normal",
"type": "uint8",
"len": 1,
"scale": 1.0,
"dir": "ro",
"description": "电量上报",
"values": {}
},
{
"id": 7,
"display_name": "充电状态",
"key": "charge",
"kind": "normal",
"type": "enum",
"len": 1,
"scale": 1.0,
"dir": "ro",
"description": "充电状态上报",
"values": {
"1": "充电中",
"2": "未充电"
}
},
{
"id": 8,
"display_name": "勿扰模式开关",
"key": "no_disturb_enable",
"kind": "normal",
"type": "raw",
"len": 13,
"scale": 1.0,
"dir": "rw",
"description": "APP控制打开或关闭勿扰模式",
"values": {}
},
{
"id": 9,
"display_name": "预留DP1",
"key": "reserva_dp1",
"kind": "normal",
"type": "uint32",
"len": 4,
"scale": 1.0,
"dir": "rw",
"description": "预留",
"values": {}
},
{
"id": 10,
"display_name": "预留DP2",
"key": "reserva_dp2",
"kind": "normal",
"type": "uint32",
"len": 4,
"scale": 1.0,
"dir": "rw",
"description": "预留",
"values": {}
},
{
"id": 11,
"display_name": "预留DP3",
"key": "reserva_dp3",
"kind": "normal",
"type": "uint32",
"len": 4,
"scale": 1.0,
"dir": "rw",
"description": "预留",
"values": {}
}
]
}
-49
View File
@@ -1,49 +0,0 @@
# 改动日志
日期:2026-09-08
## 本次 MAGIC_BOX_FW 音频与充电对齐
1. 文件:`apps/earphone/board/br28/board_jl701n_demo_cfg.h`
- 行号:104,369(修改后行号以当前文件为准)
- 原代码:`TCFG_KEY_TONE_EN``DISABLE`DAC 模式为 `DAC_MODE_L_SINGLE`
- 修改后:启用 `TCFG_KEY_TONE_EN`DAC 模式改为 `DAC_MODE_H2_SINGLE`
- 原因:启用 SDK key-tone 播放任务,并匹配 FK_CAR_FW 使用 ROUT 单端输出的配置。
2. 文件:`apps/usr_app/app_function.c`
- 行号:约 26、254-259
- 原代码:模式改变时只调用 `app_led_request_mode_blink()`
- 修改后:模式改变且 `mode != PLAY_MODE_0` 时调用 `Car_Audio_Test_Play()`
- 原因:每次实际切换到运行模式时播放一次鸟叫;不放入 1ms tick,因此不会连续调用。
3. 文件:`apps/usr_app/car_audio_test.c``apps/usr_app/car_audio_test.h`
- 原代码:MAGIC_BOX_FW 未纳入该音频测试模块。
- 修改后:加入 FK_CAR_FW 当前版本的鸟叫播放接口,路径为 `mnt/sdfile/res/bird.mp3`
- 原因:提供模式切换调用的播放实现。
4. 文件:`Makefile`
- 行号:约 414
- 原代码:源文件列表不含 `apps/usr_app/car_audio_test.c`
- 修改后:加入该源文件。
- 原因:确保编译、链接播放接口实现。
5. 文件:`cpu/br28/tools/download/earphone/download_app_ota.bat`
- 行号:约 5、18
- 原代码:未复制 `bird.mp3`,资源参数只有 `tone.cfg p11_code.bin`
- 修改后:复制 `bird.mp3`,并在 `-res` 参数中加入 `bird.mp3`
- 原因:将鸟叫文件打包进内部资源区。
6. 文件:`cpu/br28/tools/bird.mp3`
- 原代码:MAGIC_BOX_FW 中缺少可供下载脚本复制的鸟叫文件。
- 修改后:加入当前 FK_CAR_FW 使用的 `bird.mp3`
- 原因:提供资源打包输入文件。
说明:MAGIC_BOX_FW 当前 `app_power.c` 的充电状态主逻辑已经是 CHRG+STDBY 组合检测,与当前 FK_CAR_FW 一致;本次未重复改写该逻辑,保留其 VPWR 辅助函数未被应用层使用。
7. 文件:`apps/earphone/board/br28/board_jl701n_demo_cfg.h`
- 行号:825
- 原代码:`#define TCFG_DRC_ENABLE 0 /*DRC .../`(块注释未闭合)。
- 修改后:将该注释改为合法的 `/*DRC */`
- 原因:首次完整编译发现该处导致 `line 828: expected identifier or '('`,属于当前工程已有的语法错误,必须修复后才能验证本次功能。
8. 同一配置文件第1197行:补齐损坏的 `#error` 字符串结束引号,消除预处理语法错误。