Fix settings BT/restore icons and soft-off BLE protocol plumbing.
Point system-settings rows at the correct Flash icons, keep Main/UI alive across soft-off, and expose git build id for 01 0C with matching BLE tests. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
57448b3da8
commit
f1f9520637
|
|
@ -229,20 +229,23 @@ static void PowerOff(void)
|
|||
|
||||
LCD_FillByColor(0, 0, 240, 320, BLACK);
|
||||
LCD_BLK_Clr();
|
||||
|
||||
LCD_WR_REG(0x28); /* Display OFF */
|
||||
|
||||
app_tm1629_all_off();
|
||||
|
||||
|
||||
app_tm1617_off();
|
||||
/* 关闭电源硬件;MCU 保持运行以便长按开机 / 关机充电画面 */
|
||||
/* 关掉主电源/音源/功放/蓝牙;MCU 仍跑以便长按开机与 Type-C 检测 */
|
||||
BSP_MainPowerEnable(0);
|
||||
BSP_DreamCorePowerEnable(0);
|
||||
BSP_HT7178PowerEnable(0);
|
||||
/* BT 最后关:给已排队的 UART4 ACK(如 05 00)留出模组转发窗口 */
|
||||
rt_thread_mdelay(30);
|
||||
BSP_BlueToothPowerEnable(0);
|
||||
CurrUIProcress = IdleProcess;
|
||||
MainTask_Sendmsg(MSG_ID_POWER_OFF, 0, 0, 0);
|
||||
LOG_I("PWR", "power_off done (soft-off, no reset)");
|
||||
LOG_I("PWR", "power_off done (soft-off)");
|
||||
|
||||
/* 关机时若已插着 Type-C,立刻进充电画面 */
|
||||
/* Type-C 有供电:维持现有逻辑,显示 Charging Image(POWOFF_CHARG) */
|
||||
{
|
||||
uint8_t i;
|
||||
for (i = 0; i < 4; i++)
|
||||
|
|
@ -256,7 +259,7 @@ static void PowerOff(void)
|
|||
{
|
||||
usb_charging_state = 1;
|
||||
usb_charging_laststate = 1;
|
||||
LOG_I("PWR", "soft-off with USB → charge UI");
|
||||
LOG_I("PWR", "soft-off with Type-C → charge UI");
|
||||
MainTask_Sendmsg(MSG_ID_POWOFF_CHARG, percentage, 1, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,8 +179,9 @@ void IdleProcess(DisplayTaskMessage_Type msg)
|
|||
break;
|
||||
|
||||
case MSG_ID_POWER_OFF:
|
||||
/* 关机:熄屏 + 关背光 */
|
||||
/* 无 Type-C:整机关机熄屏;有 Type-C 时随后 POWOFF_CHARG 会再亮充电图 */
|
||||
LCD_BLK_Clr();
|
||||
LCD_WR_REG(0x28); /* Display OFF */
|
||||
LCD_FillByColor(0, 0, 240, 320, BLACK);
|
||||
break;
|
||||
|
||||
|
|
@ -190,6 +191,8 @@ void IdleProcess(DisplayTaskMessage_Type msg)
|
|||
|
||||
case MSG_ID_TOUCH:
|
||||
{
|
||||
if (!powon)
|
||||
break; /* 软关机已停触摸;兜底勿进模式选择 */
|
||||
static uint32_t s_last_enter_ms;
|
||||
uint32_t now = rt_tick_get() * 1000U / RT_TICK_PER_SECOND;
|
||||
uint8_t hit = 0U;
|
||||
|
|
|
|||
|
|
@ -1223,11 +1223,11 @@ void Draw_System_Item_List(const LEBEL_UI *UI, GUI_SWITCH *Group)
|
|||
}
|
||||
break;
|
||||
case GUI_BLUETOOTH_SW:
|
||||
/* 素材:系统设置图标-11(白蓝牙符);Flash 槽名 RESTORE,尺寸 13x21 */
|
||||
/* 素材:系统设置图标-11(白蓝牙符)→ ICON_SET_BLUETOOTH 13x21 */
|
||||
LCD_WR_PIC_FROM_FLASH_Trans(UI->x + 12,
|
||||
(uint16_t)(UI->y + (UI->h - UI0902_ICON_SYS_RESTORE_H) / 2),
|
||||
UI0902_ICON_SYS_RESTORE_W, UI0902_ICON_SYS_RESTORE_H,
|
||||
UI0902_ICON_SYS_RESTORE_ADDR);
|
||||
(uint16_t)(UI->y + (UI->h - UI0902_ICON_SET_BLUETOOTH_H) / 2),
|
||||
UI0902_ICON_SET_BLUETOOTH_W, UI0902_ICON_SET_BLUETOOTH_H,
|
||||
UI0902_ICON_SET_BLUETOOTH_ADDR);
|
||||
UI_DrawTriLeft(val_x1, chev_cy);
|
||||
if(Group->Current)
|
||||
LCD_ShowChinese_AutoAlign_Gradient(val_x1 + UI0902_TRI_L_W, val_x2 - UI0902_TRI_R_W, text_y,(const uint8_t*)"\xCA\xC7",LCD_ALIGN_CENTER, WHITE,c_top, c_bot,value_sz);
|
||||
|
|
@ -1264,11 +1264,11 @@ void Draw_System_Item_List(const LEBEL_UI *UI, GUI_SWITCH *Group)
|
|||
drv_nvm_save_to_flash();
|
||||
break;
|
||||
case GUI_RESTORE:
|
||||
/* 逆时针箭头:当前在 VERSION 槽(19x18);RESTORE 槽曾误为蓝牙符 */
|
||||
/* 素材:系统设置图标-13(逆时针恢复)→ ICON_SYS_RESTORE 19x18 */
|
||||
LCD_WR_PIC_FROM_FLASH_Trans(UI->x + 12,
|
||||
(uint16_t)(UI->y + (UI->h - UI0902_ICON_SYS_VERSION_H) / 2),
|
||||
UI0902_ICON_SYS_VERSION_W, UI0902_ICON_SYS_VERSION_H,
|
||||
UI0902_ICON_SYS_VERSION_ADDR);
|
||||
(uint16_t)(UI->y + (UI->h - UI0902_ICON_SYS_RESTORE_H) / 2),
|
||||
UI0902_ICON_SYS_RESTORE_W, UI0902_ICON_SYS_RESTORE_H,
|
||||
UI0902_ICON_SYS_RESTORE_ADDR);
|
||||
/* 右三角与蓝牙/自动关机同一右缘(val_x2) */
|
||||
UI_DrawTriRight((uint16_t)(val_x2 - UI0902_TRI_R_W), chev_cy);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -663,7 +663,7 @@
|
|||
<name>BUILDACTION</name>
|
||||
<archiveVersion>1</archiveVersion>
|
||||
<data>
|
||||
<prebuild></prebuild>
|
||||
<prebuild>python "$PROJ_DIR$\..\..\tools\gen_git_user_fw_ver.py"</prebuild>
|
||||
<postbuild></postbuild>
|
||||
</data>
|
||||
</settings>
|
||||
|
|
@ -1650,7 +1650,7 @@
|
|||
<name>BUILDACTION</name>
|
||||
<archiveVersion>1</archiveVersion>
|
||||
<data>
|
||||
<prebuild></prebuild>
|
||||
<prebuild>python "$PROJ_DIR$\..\..\tools\gen_git_user_fw_ver.py"</prebuild>
|
||||
<postbuild></postbuild>
|
||||
</data>
|
||||
</settings>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@
|
|||
void USART2_SendData(uint8_t *data, uint16_t len);
|
||||
void USART4_SendData(uint8_t *data, uint16_t len);
|
||||
|
||||
/* 等待 UART4 发送环形缓冲排空(供关机 ACK 等场景) */
|
||||
void USART4_WaitTxIdle(uint32_t timeout_ms);
|
||||
|
||||
/* 接收出队:返回 1=取到数据,0=队列空 */
|
||||
uint8_t USART2_RxPop(uint8_t *c);
|
||||
uint8_t UART4_RxPop(uint8_t *c);
|
||||
|
|
|
|||
|
|
@ -101,6 +101,23 @@ void USART4_SendData(uint8_t *data, uint16_t len)
|
|||
uart_ring_send(&uart4, data, len);
|
||||
}
|
||||
|
||||
void USART4_WaitTxIdle(uint32_t timeout_ms)
|
||||
{
|
||||
uint32_t t0 = rt_tick_get();
|
||||
while ((rt_tick_get() - t0) < rt_tick_from_millisecond(timeout_ms ? timeout_ms : 1))
|
||||
{
|
||||
uint16_t h = uart4.tx_head;
|
||||
uint16_t t = uart4.tx_tail;
|
||||
if (IS_EMPTY(h, t))
|
||||
{
|
||||
/* <20><><EFBFBD><EFBFBD>պ<EFBFBD><D5BA>ٵ<EFBFBD><D9B5><EFBFBD>λ<EFBFBD>Ĵ<EFBFBD><C4B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
||||
if (usart_flag_get(uart4.usart, USART_TDC_FLAG) == SET)
|
||||
return;
|
||||
}
|
||||
rt_thread_mdelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* USART2 ???????????? */
|
||||
uint8_t USART2_RxPop(uint8_t *c)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "includes.h"
|
||||
#include "git_user_fw_ver.h"
|
||||
|
||||
|
||||
// Э<><D0AD>֡ͷ/֡β
|
||||
|
|
@ -162,12 +163,13 @@ static const BLE_SysExCmdItem bleSysExCmdTable[] =
|
|||
*/
|
||||
void processBLESysEXData(uint8_t* data, uint8_t cnt)
|
||||
{
|
||||
//ResetAutoPowerCount();
|
||||
// ֡У<D6A1><D0A3>
|
||||
if (data[0] != FRAME_SYS_HEAD || data[cnt - 1] != FRAME_SYS_TAIL || cnt > UART4_PROCESS_BUFF_SIZE || data[1] != 0x60)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* App/BLE 活动视为用户操作,避免测协议时静置触发自动关机 */
|
||||
ResetAutoPowerCount();
|
||||
LOG_I("BLE", "sysex ok len=%u cmd=%02X %02X", (unsigned)cnt, data[2], data[3]);
|
||||
|
||||
// ȡ<><C8A1>ָ<EFBFBD><D6B8> + <20><>ָ<EFBFBD><D6B8>
|
||||
|
|
@ -584,10 +586,11 @@ static void BL_Sendmsg(uint16_t Data1, uint16_t Data2, uint16_t Data3, uint16_t
|
|||
/*-------- 1. device info 0x01 --------*/
|
||||
#define AT32_UID_BASE 0x1FFFF7E8UL /* 96-bit unique device ID */
|
||||
|
||||
/* placeholders: 01 04/05/0C — bump manually at each release (Dream has no ver query) */
|
||||
static const uint8_t SOUND_VER[4] = {0x4D, 0x06, 0x7F, 0x01};
|
||||
static const uint8_t UI_VER[4] = {0x21, 0x05, 0x7F, 0x01};
|
||||
static const uint8_t USER_FW_VER[4] = {0x4D, 0x06, 0x7F, 0x01};
|
||||
/* 01 04 音源版本:YY.M.D = 音色资源日期(Dream 无版本查询,发版时手改)
|
||||
* 26.9.8 → 2026-09-08(Doc/音色文件/0908) */
|
||||
static const uint8_t SOUND_VER[4] = {26, 9, 8, 0x01};
|
||||
/* 01 05 UI 资源/界面日期:YY.M.D(与 MCU 逻辑版本 Version[] 分开维护) */
|
||||
static const uint8_t UI_VER[4] = {26, 9, 9, 0x01};
|
||||
|
||||
static void handleDevDisconnect(uint8_t *data)
|
||||
{
|
||||
|
|
@ -676,9 +679,16 @@ static void handleAutoPowerOff(uint8_t *data)
|
|||
|
||||
static void handleUserFwVer(uint8_t *data)
|
||||
{
|
||||
uint8_t resp[9] = {0xF0, 0x60, 0x01, 0x0C, USER_FW_VER[0], USER_FW_VER[1], USER_FW_VER[2], USER_FW_VER[3], 0xF7};
|
||||
/* 用户固件版本 = "{branch}_{short6}"[+ '*'],如 develop_0aedb4(ASCII,BLE-MIDI 安全) */
|
||||
uint8_t resp[4u + GIT_BUILD_ID_LEN + 1u];
|
||||
(void)data;
|
||||
USART4_SendData(resp, sizeof(resp));
|
||||
resp[0] = 0xF0;
|
||||
resp[1] = 0x60;
|
||||
resp[2] = 0x01;
|
||||
resp[3] = 0x0C;
|
||||
memcpy(&resp[4], GIT_BUILD_ID, GIT_BUILD_ID_LEN);
|
||||
resp[4u + GIT_BUILD_ID_LEN] = 0xF7;
|
||||
USART4_SendData(resp, (uint16_t)sizeof(resp));
|
||||
}
|
||||
|
||||
/* F0 60 01 11 <minutes> F7 : set auto power-off, 0 = disable */
|
||||
|
|
@ -827,6 +837,11 @@ static void handleDeviceReset(uint8_t *data)
|
|||
{
|
||||
uint8_t resp[6] = {0xF0, 0x60, 0x05, 0x00, 0x01, 0xF7};
|
||||
(void)data;
|
||||
LOG_I("BLE", "05 00 ack then soft power off");
|
||||
USART4_SendData(resp, sizeof(resp));
|
||||
/* Soft-off cuts BT power immediately in PowerOff(); give UART4 + ATS2853
|
||||
time to push the ACK Notify before requesting power-off. */
|
||||
USART4_WaitTxIdle(50);
|
||||
rt_thread_mdelay(120);
|
||||
System_RequestPowerOff();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
/* Auto-generated by tools/gen_git_user_fw_ver.py — do not edit.
|
||||
* git HEAD (dirty): 57448b3da8a3f912503e9d50313b5ec635888b69
|
||||
* branch: develop → develop
|
||||
* 01 0C wire: develop_57448b*
|
||||
*/
|
||||
#ifndef GIT_USER_FW_VER_H
|
||||
#define GIT_USER_FW_VER_H
|
||||
|
||||
#define GIT_COMMIT_ID_FULL "57448b3da8a3f912503e9d50313b5ec635888b69"
|
||||
#define GIT_BRANCH_NAME "develop"
|
||||
#define GIT_COMMIT_SHORT6 "57448b"
|
||||
#define GIT_DIRTY (1)
|
||||
#define GIT_BUILD_ID "develop_57448b*"
|
||||
#define GIT_BUILD_ID_LEN 15u
|
||||
|
||||
#endif /* GIT_USER_FW_VER_H */
|
||||
|
|
@ -240,6 +240,35 @@ void StartTask(void)
|
|||
/* 扫描任务可能已由 StartScanTask 拉起;重复 startup 会被 RT-Thread 忽略 */
|
||||
StartScanTask();
|
||||
|
||||
/* Soft-off(StopFullTask) 会 detach 下列线程;开机须能重建,否则 UI/消息永久失效 */
|
||||
if ((TaskMainThread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_CLOSE)
|
||||
{
|
||||
rt_thread_init(&TaskMainThread,
|
||||
"TaskMainThread",
|
||||
TaskMainThread_entry,
|
||||
RT_NULL,
|
||||
&TaskMainThread_Stack,
|
||||
sizeof(TaskMainThread_Stack),
|
||||
5,
|
||||
20);
|
||||
}
|
||||
if ((TaskMainThread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT)
|
||||
rt_thread_startup(&TaskMainThread);
|
||||
|
||||
if ((TaskUIThread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_CLOSE)
|
||||
{
|
||||
rt_thread_init(&TaskUIThread,
|
||||
"TaskUIThread",
|
||||
TaskUIThread_entry,
|
||||
RT_NULL,
|
||||
&TaskUIThread_Stack,
|
||||
sizeof(TaskUIThread_Stack),
|
||||
5,
|
||||
20);
|
||||
}
|
||||
if ((TaskUIThread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT)
|
||||
rt_thread_startup(&TaskUIThread);
|
||||
|
||||
if ((TaskBTHandleThread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_CLOSE)
|
||||
{
|
||||
rt_thread_init(&TaskBTHandleThread,
|
||||
|
|
@ -336,18 +365,12 @@ void StopFullTask(void)
|
|||
LOG_I("TASK", "stop ScanThread");
|
||||
rt_thread_detach(&TaskScanThread);
|
||||
}
|
||||
if((TaskUIThread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE) {
|
||||
LOG_I("TASK", "stop UIThread");
|
||||
rt_thread_detach(&TaskUIThread);
|
||||
}
|
||||
/* 软关机 = 系统关闭:停触摸/扫描/BT/伴奏。
|
||||
Main/UI 保留:Type-C 时要画 Charging Image,长按开机要收 POWER_ON。 */
|
||||
if((TaskTouchThread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE) {
|
||||
LOG_I("TASK", "stop TouchThread");
|
||||
rt_thread_detach(&TaskTouchThread);
|
||||
}
|
||||
if((TaskMainThread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE) {
|
||||
LOG_I("TASK", "stop MainThread");
|
||||
rt_thread_detach(&TaskMainThread);
|
||||
}
|
||||
if((TaskBTHandleThread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE) {
|
||||
LOG_I("TASK", "stop BTHandleThread");
|
||||
rt_thread_detach(&TaskBTHandleThread);
|
||||
|
|
@ -356,8 +379,12 @@ void StopFullTask(void)
|
|||
LOG_I("TASK", "stop BTRecvThread");
|
||||
rt_thread_detach(&TaskBTRecvThread);
|
||||
}
|
||||
if((TaskAutobandThread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE) {
|
||||
LOG_I("TASK", "stop AutobandThread");
|
||||
rt_thread_detach(&TaskAutobandThread);
|
||||
}
|
||||
LOG_I("TASK", "StopFullTask done");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void OutTime_Stop_timer_callback(void *parameter)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Generate protocol/git_user_fw_ver.h from current git HEAD + branch.
|
||||
|
||||
用户固件版本 (01 0C) 线格式: {branch}_{short6}[optional '*']
|
||||
例: develop_0aedb4 / feature-ui_57448b*
|
||||
|
||||
- short6 = git rev-parse --short=6 HEAD(小写 hex)
|
||||
- branch 清洗为 [A-Za-z0-9.-],整串(不含 dirty '*') ≤ 24
|
||||
- dirty working tree 时末尾追加 '*'
|
||||
|
||||
用法(IAR Pre-build):
|
||||
python \"$PROJ_DIR$\\..\\..\\tools\\gen_git_user_fw_ver.py\"
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
ROOT = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
|
||||
OUT = os.path.join(ROOT, "protocol", "git_user_fw_ver.h")
|
||||
BUILD_ID_MAX = 24 # without trailing dirty '*'
|
||||
|
||||
|
||||
def _git(*args: str) -> str | None:
|
||||
try:
|
||||
return subprocess.check_output(
|
||||
["git", *args],
|
||||
cwd=ROOT,
|
||||
stderr=subprocess.DEVNULL,
|
||||
text=True,
|
||||
).strip()
|
||||
except (subprocess.CalledProcessError, FileNotFoundError, OSError):
|
||||
return None
|
||||
|
||||
|
||||
def sanitize_branch(name: str) -> str:
|
||||
if not name or name == "HEAD":
|
||||
return "DETACHED"
|
||||
# feature/foo → feature-foo;去掉其它非法字符
|
||||
s = name.replace("/", "-")
|
||||
s = re.sub(r"[^A-Za-z0-9.-]+", "-", s)
|
||||
s = re.sub(r"-{2,}", "-", s).strip("-.")
|
||||
return s or "DETACHED"
|
||||
|
||||
|
||||
def make_build_id(branch: str, short6: str) -> str:
|
||||
"""Ensure '{branch}_{short6}' length ≤ BUILD_ID_MAX; keep commit suffix intact."""
|
||||
short6 = short6.lower()[:6].ljust(6, "0")
|
||||
suffix = "_" + short6
|
||||
max_br = BUILD_ID_MAX - len(suffix)
|
||||
if max_br < 1:
|
||||
return short6[:BUILD_ID_MAX]
|
||||
br = branch[:max_br]
|
||||
return br + suffix
|
||||
|
||||
|
||||
def main() -> int:
|
||||
full = _git("rev-parse", "HEAD") or ("0" * 40)
|
||||
if len(full) < 6 or not all(c in "0123456789abcdefABCDEF" for c in full):
|
||||
full = "0" * 40
|
||||
full = full.lower()
|
||||
short6 = full[:6]
|
||||
|
||||
br_raw = _git("rev-parse", "--abbrev-ref", "HEAD") or "DETACHED"
|
||||
branch = sanitize_branch(br_raw)
|
||||
|
||||
dirty = False
|
||||
st = _git("status", "--porcelain")
|
||||
if st:
|
||||
dirty = True
|
||||
|
||||
build_id = make_build_id(branch, short6)
|
||||
wire = build_id + ("*" if dirty else "")
|
||||
# C string escape
|
||||
wire_c = wire.replace("\\", "\\\\").replace('"', '\\"')
|
||||
|
||||
text = f"""/* Auto-generated by tools/gen_git_user_fw_ver.py — do not edit.
|
||||
* git HEAD{' (dirty)' if dirty else ''}: {full}
|
||||
* branch: {br_raw} → {branch}
|
||||
* 01 0C wire: {wire}
|
||||
*/
|
||||
#ifndef GIT_USER_FW_VER_H
|
||||
#define GIT_USER_FW_VER_H
|
||||
|
||||
#define GIT_COMMIT_ID_FULL "{full}"
|
||||
#define GIT_BRANCH_NAME "{branch}"
|
||||
#define GIT_COMMIT_SHORT6 "{short6}"
|
||||
#define GIT_DIRTY ({1 if dirty else 0})
|
||||
#define GIT_BUILD_ID "{wire_c}"
|
||||
#define GIT_BUILD_ID_LEN {len(wire)}u
|
||||
|
||||
#endif /* GIT_USER_FW_VER_H */
|
||||
"""
|
||||
os.makedirs(os.path.dirname(OUT), exist_ok=True)
|
||||
old = ""
|
||||
if os.path.isfile(OUT):
|
||||
with open(OUT, "r", encoding="utf-8") as f:
|
||||
old = f.read()
|
||||
if old != text:
|
||||
with open(OUT, "w", encoding="utf-8", newline="\n") as f:
|
||||
f.write(text)
|
||||
print(f"gen_git_user_fw_ver: wrote {OUT} ({wire})")
|
||||
else:
|
||||
print(f"gen_git_user_fw_ver: up-to-date ({wire})")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
|
@ -140,6 +140,21 @@ def expect(resp, prefix, length=None):
|
|||
return True, hexs(resp)
|
||||
|
||||
|
||||
def fmt_ver4(payload: bytes, *, kind: str = "semver") -> str:
|
||||
"""解码版本字节为可读串。
|
||||
|
||||
kind:
|
||||
semver — 前 3 字节 major.minor.patch(01 03/0C)
|
||||
date — YY.M.D 日期(01 04 音源 / 01 05 UI),并展开为 20YY-MM-DD
|
||||
"""
|
||||
if len(payload) != 4:
|
||||
return f"ver_raw={payload!r}"
|
||||
a, b, c, _d = payload
|
||||
if kind == "date":
|
||||
return f"ver={a}.{b}.{c} date=20{a:02d}-{b:02d}-{c:02d}"
|
||||
return f"ver={a}.{b}.{c}"
|
||||
|
||||
|
||||
def run_tests(sim, allow_poweroff=False):
|
||||
R = Results()
|
||||
|
||||
|
|
@ -156,12 +171,39 @@ def run_tests(sim, allow_poweroff=False):
|
|||
d += f" name={name!r}"
|
||||
R.add("01 02 设备名", "PASS" if ok else "FAIL", d)
|
||||
|
||||
for sub, name in ((0x03, "固件主版本"), (0x04, "音源版本"),
|
||||
(0x05, "UI版本"), (0x0C, "用户固件版本")):
|
||||
for sub, name, kind in (
|
||||
(0x03, "固件主版本", "semver"),
|
||||
(0x04, "音源版本", "date"),
|
||||
(0x05, "UI版本", "date"),
|
||||
):
|
||||
r = sim.query(build(0x01, sub))
|
||||
ok, d = expect(r, (0x60, 0x01, sub), 9)
|
||||
if ok:
|
||||
d += f" {fmt_ver4(bytes(r[4:-1]), kind=kind)}"
|
||||
R.add(f"01 {sub:02X} {name}", "PASS" if ok else "FAIL", d)
|
||||
|
||||
# 01 0C:{branch}_{short6}[optional '*'],如 develop_0aedb4
|
||||
r = sim.query(build(0x01, 0x0C))
|
||||
ok, d = expect(r, (0x60, 0x01, 0x0C))
|
||||
if ok:
|
||||
raw = bytes(r[4:-1])
|
||||
ok = (
|
||||
3 <= len(raw) <= 25
|
||||
and all(0x20 <= c < 0x7F for c in raw)
|
||||
and b"_" in raw.rstrip(b"*")
|
||||
)
|
||||
build_s = raw.decode("ascii", "replace")
|
||||
dirty = build_s.endswith("*")
|
||||
core = build_s[:-1] if dirty else build_s
|
||||
branch, _, short = core.rpartition("_")
|
||||
extra = f" build={build_s!r}"
|
||||
if len(short) == 6 and all(c in "0123456789abcdef" for c in short.lower()):
|
||||
extra += f" branch={branch!r} commit={short.lower()}"
|
||||
if dirty:
|
||||
extra += " dirty=1"
|
||||
d += extra
|
||||
R.add("01 0C 用户固件版本", "PASS" if ok else "FAIL", d)
|
||||
|
||||
r = sim.query(build(0x01, 0x06))
|
||||
ok, d = expect(r, (0x60, 0x01, 0x06), 13)
|
||||
R.add("01 06 其他信息", "PASS" if ok else "FAIL", d)
|
||||
|
|
@ -341,8 +383,17 @@ def run_tests(sim, allow_poweroff=False):
|
|||
|
||||
# ---------------- 5. 复位/关机 0x05 ----------------
|
||||
if allow_poweroff:
|
||||
r = sim.query(build(0x05, 0x00), timeout=2.0)
|
||||
time.sleep(0.3)
|
||||
# 禁止 query 自动重发:第二次会打在已断电的 BT 上
|
||||
sim.drain()
|
||||
sim.send(build(0x05, 0x00))
|
||||
r = sim.read_frame(timeout=3.0)
|
||||
ok, d = expect(r, (0x60, 0x05, 0x00), 6)
|
||||
if not ok and r is None and hasattr(sim, "is_connected"):
|
||||
# ACK 可能被软关机掐断 BT 抢走;链路断开也视为关机已执行
|
||||
time.sleep(0.5)
|
||||
if not sim.is_connected():
|
||||
ok, d = True, "no ACK but BLE link dropped (soft-off likely)"
|
||||
R.add("05 00 复位/关机", "PASS" if ok else "FAIL", d + " (设备将软关机)")
|
||||
else:
|
||||
R.add("05 00 复位/关机", "SKIP", "需 --allow-poweroff")
|
||||
|
|
|
|||
|
|
@ -270,17 +270,24 @@ class BleMidiSim:
|
|||
while self.read_frame(timeout=quiet) is not None:
|
||||
pass
|
||||
|
||||
def query(self, frame: bytes, timeout=3.0):
|
||||
def query(self, frame: bytes, timeout=3.0, retry=True):
|
||||
self.drain(quiet=0.15)
|
||||
self.send(frame)
|
||||
r = self.read_frame(timeout=timeout)
|
||||
if r is None:
|
||||
# BLE 偶发丢 Notify:短间隔重发一次
|
||||
if r is None and retry:
|
||||
# BLE 偶发丢 Notify:短间隔重发一次(关机指令不可重试)
|
||||
time.sleep(0.25)
|
||||
self.send(frame)
|
||||
r = self.read_frame(timeout=timeout)
|
||||
return r
|
||||
|
||||
def is_connected(self) -> bool:
|
||||
c = self._client
|
||||
try:
|
||||
return bool(c is not None and c.is_connected)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
async def _reconnect_clean(self, name, address, scan_timeout):
|
||||
"""Drop session, unpair, rescan, reconnect — recovers Windows stale pairing."""
|
||||
await self._disconnect()
|
||||
|
|
@ -345,7 +352,7 @@ def write_report(R: Results, info: dict, path: str, notes: list[str] | None = No
|
|||
for note in notes or []:
|
||||
lines.append(f"- {note}")
|
||||
lines += [
|
||||
"- `01 03` 为 MCU 实际版本;`01 0F` 固件编码暂 `BRS08L`(原 `01 FF`,BLE-MIDI 下 0xFF 非法)。",
|
||||
"- `01 03`=`Version[]`;`01 04/05`=日期;`01 0C`=`branch_short6`(如 develop_0aedb4);`01 0F`=`BRS08L`。",
|
||||
"- `01 07` UID 为 24 字符 hex ASCII(96-bit),保证 SysEx 7-bit 安全。",
|
||||
"- `04 xx`/`06 xx` 无应答,SENT 表示已发送。",
|
||||
"- `FD 01` 升级指令不在本协议范围。",
|
||||
|
|
|
|||
Loading…
Reference in New Issue