Diagnose BIN3 LoadPreset failures and add flash bin3 tooling.

Log Flash magic on load failure, expose RTT flash bin3, and document the 0903 pack offset for the universal bank.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yuquanjun 2026-09-09 11:32:40 +08:00
parent 015417dc3d
commit 92e5e32de3
5 changed files with 235 additions and 7 deletions

View File

@ -280,6 +280,12 @@ uint8_t app_log_try_command(const char *cmd)
"FLASH_HAITIAN_GO", "FLASH_HAITIAN_OK\n"); "FLASH_HAITIAN_GO", "FLASH_HAITIAN_OK\n");
return 1U; return 1U;
} }
if (strncmp(cmd, "flash bin3 ", 11) == 0) {
uint32_t size = (uint32_t)strtoul(cmd + 11, NULL, 10);
app_log_flash_start_at(EXTFLASH_BIN3_UNIVERSAL_ADDR, size,
"FLASH_BIN3_GO", "FLASH_BIN3_OK\n");
return 1U;
}
if (strncmp(cmd, "tone status", 11) == 0) { if (strncmp(cmd, "tone status", 11) == 0) {
uint8_t hdr[16]; uint8_t hdr[16];
uint8_t name[40]; uint8_t name[40];
@ -320,6 +326,16 @@ uint8_t app_log_try_command(const char *cmd)
(unsigned long)(cur_hdr[12] | (cur_hdr[13] << 8) | (unsigned long)(cur_hdr[12] | (cur_hdr[13] << 8) |
(cur_hdr[14] << 16) | (cur_hdr[15] << 24))); (cur_hdr[14] << 16) | (cur_hdr[15] << 24)));
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line);
{
uint8_t b3[16];
W25Q128_Read(b3, EXTFLASH_BIN3_UNIVERSAL_ADDR, 16);
snprintf(line, sizeof(line),
"TONE @BIN3 magic=%02X%02X%02X%02X cnt=%lu\n",
b3[0], b3[1], b3[2], b3[3],
(unsigned long)(b3[12] | (b3[13] << 8) |
(b3[14] << 16) | (b3[15] << 24)));
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line);
}
snprintf(line, sizeof(line), snprintf(line, sizeof(line),
"TONE @BIN2 magic=%02X%02X%02X%02X max=%lu cnt=%lu name=%.16s\n", "TONE @BIN2 magic=%02X%02X%02X%02X max=%lu cnt=%lu name=%.16s\n",
hdr[0], hdr[1], hdr[2], hdr[3], hdr[0], hdr[1], hdr[2], hdr[3],

View File

@ -148,8 +148,12 @@ void App_Auto_Init(void)
int ret = AutoBandTop1_LoadPresetItemFromFlash(0); int ret = AutoBandTop1_LoadPresetItemFromFlash(0);
if (ret != 0) if (ret != 0)
{ {
LOG_E("AUTO", "LoadPreset fail ret=%d addr=0x%08X map=%s", uint8_t hdr[16];
ret, (unsigned)ADDRESS, ToneFlashMapBank((uint32_t)ADDRESS)); W25Q128_Read(hdr, (uint32_t)ADDRESS, 16);
LOG_E("AUTO", "LoadPreset fail ret=%d addr=0x%08X map=%s magic=%02X%02X%02X%02X cnt=%lu",
ret, (unsigned)ADDRESS, ToneFlashMapBank((uint32_t)ADDRESS),
hdr[0], hdr[1], hdr[2], hdr[3],
(unsigned long)(hdr[12] | (hdr[13] << 8) | (hdr[14] << 16) | (hdr[15] << 24)));
} }
else else
{ {

View File

@ -168,10 +168,23 @@ void UI_ReloadTonePreset(void)
if (mGuiData[GUI_TAB_INDEX].Current == 0) if (mGuiData[GUI_TAB_INDEX].Current == 0)
{ {
int ret = AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[ALL_MODE_PARAM].Current); int ret = AutoBandTop1_LoadPresetItemFromFlash(ParamGuiData[ALL_MODE_PARAM].Current);
LOG_I("tone", "load universal idx=%d ret=%d name=%s addr=0x%08X map=BIN3@0x%08X", if (ret != 0)
ParamGuiData[ALL_MODE_PARAM].Current, ret, {
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?", uint8_t hdr[16];
(unsigned)ADDRESS, (unsigned)EXTFLASH_BIN3_UNIVERSAL_ADDR); W25Q128_Read(hdr, (uint32_t)ADDRESS, 16);
LOG_E("tone", "load universal idx=%d ret=%d magic=%02X%02X%02X%02X cnt=%lu addr=0x%08X",
ParamGuiData[ALL_MODE_PARAM].Current, ret,
hdr[0], hdr[1], hdr[2], hdr[3],
(unsigned long)(hdr[12] | (hdr[13] << 8) | (hdr[14] << 16) | (hdr[15] << 24)),
(unsigned)ADDRESS);
}
else
{
LOG_I("tone", "load universal idx=%d ret=%d name=%s addr=0x%08X map=BIN3@0x%08X",
ParamGuiData[ALL_MODE_PARAM].Current, ret,
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?",
(unsigned)ADDRESS, (unsigned)EXTFLASH_BIN3_UNIVERSAL_ADDR);
}
return; return;
} }
/* 普通/专业:本地曲目或节奏类型 */ /* 普通/专业:本地曲目或节奏类型 */

View File

@ -0,0 +1,195 @@
#!/usr/bin/env python3
"""Flash Doc/音色文件/0903/3.bin to W25Q128 @ EXTFLASH_BIN3_UNIVERSAL_ADDR via RTT.
Requires firmware that accepts RTT command: flash bin3 <size>
"""
from __future__ import annotations
import argparse
import os
import time
DEFAULT_DEVICES = ("Cortex-M4", "AT32F403AC", "AT32F403A")
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
REPO = os.path.dirname(os.path.dirname(ROOT))
DEFAULT_BIN = os.path.join(REPO, "Doc", "音色文件", "0903", "3.bin")
EXTFLASH_BIN3_UNIVERSAL_ADDR = 0x000A71AC
DAB_MAGIC = bytes((0xAB, 0x44, 0x41, 0x42))
def connect_jlink(device: str):
import pylink
jlink = pylink.JLink()
jlink.open()
try:
jlink.exec_command("HideDeviceSelection = 1")
except Exception:
pass
jlink.set_tif(pylink.enums.JLinkInterfaces.SWD)
last_error = None
for candidate in (device, *DEFAULT_DEVICES):
try:
try:
jlink.exec_command(f"Device = {candidate}")
except Exception:
pass
jlink.connect(candidate)
print(f"Connected as {candidate}")
return jlink
except Exception as exc:
last_error = exc
jlink.close()
raise SystemExit(f"Failed to connect J-Link: {last_error}")
def find_rtt_control_block(jlink, ram_base: int = 0x20000000, ram_size: int = 0x18000):
needle = b"SEGGER RTT"
chunk = 0x1000
was_halted = jlink.halted()
if not was_halted:
jlink.halt()
try:
for off in range(0, ram_size, chunk):
data = bytes(jlink.memory_read8(ram_base + off, min(chunk, ram_size - off)))
idx = data.find(needle)
if idx >= 0:
return ram_base + off + idx
finally:
if not was_halted:
if hasattr(jlink, "restart"):
jlink.restart()
else:
jlink.go()
time.sleep(0.1)
return None
def rtt_read_text(jlink, timeout_s: float = 0.2) -> str:
deadline = time.time() + timeout_s
chunks: list[bytes] = []
while time.time() < deadline:
try:
data = bytes(jlink.rtt_read(0, 512) or [])
except Exception:
data = b""
if data:
chunks.append(data)
deadline = time.time() + timeout_s
else:
time.sleep(0.02)
return b"".join(chunks).decode("ascii", errors="replace")
def wait_for(jlink, marker: str, timeout_s: float = 30.0) -> str:
deadline = time.time() + timeout_s
buf = ""
while time.time() < deadline:
buf += rtt_read_text(jlink, 0.15)
if marker in buf:
return buf
raise SystemExit(f"Timeout waiting for {marker!r}\n--- RTT ---\n{buf[-800:]}")
def send_bytes(jlink, payload: bytes) -> None:
off = 0
while off < len(payload):
try:
n = jlink.rtt_write(0, list(payload[off : off + 64]))
except Exception:
time.sleep(0.05)
continue
if n is None or n <= 0:
time.sleep(0.02)
continue
off += n
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--bin", default=DEFAULT_BIN)
parser.add_argument("--device", default="Cortex-M4")
parser.add_argument("--no-reset", action="store_true")
args = parser.parse_args()
if not os.path.isfile(args.bin):
raise SystemExit(f"missing bin: {args.bin}")
data = open(args.bin, "rb").read()
size = len(data)
if size < 16 or data[:4] != DAB_MAGIC:
raise SystemExit(f"not a DAB bank (magic={data[:4].hex()})")
print(f"bin={args.bin} size={size} addr=0x{EXTFLASH_BIN3_UNIVERSAL_ADDR:08X}")
jlink = connect_jlink(args.device)
try:
cb = find_rtt_control_block(jlink)
if cb is None:
raise SystemExit("RTT control block not found")
print(f"RTT CB @ 0x{cb:08X}")
jlink.rtt_start(cb)
time.sleep(0.3)
_ = rtt_read_text(jlink, 0.3)
if not args.no_reset:
print("hardware reset...")
jlink.reset(halt=False)
time.sleep(3.5)
try:
jlink.rtt_stop()
except Exception:
pass
cb = find_rtt_control_block(jlink)
if cb is None:
raise SystemExit("RTT CB missing after reset")
print(f"RTT CB @ 0x{cb:08X}")
jlink.rtt_start(cb)
time.sleep(0.5)
_ = rtt_read_text(jlink, 0.5)
cmd = f"flash bin3 {size}\n".encode("ascii")
send_bytes(jlink, cmd)
print("sent flash command, waiting GO...")
try:
log = wait_for(jlink, "FLASH_BIN3_GO", 20.0)
except SystemExit:
raise SystemExit(
"Firmware did not accept 'flash bin3'. Rebuild App with app_log.c "
"support, or temporarily flash via: concatenate 2.bin+3.bin and "
"'flash haitian <combined_size>'."
)
print(log.strip().splitlines()[-1])
time.sleep(0.2)
chunk = 256
sent = 0
t0 = time.time()
while sent < size:
end = min(sent + chunk, size)
send_bytes(jlink, data[sent:end])
sent = end
log = wait_for(jlink, "FLASH_ACK", 60.0)
if "FLASH_BIN3_OK" in log:
print(log.strip().splitlines()[-1])
print("Done. Universal bank (3.bin) updated.")
return
if sent % (4 * 1024) == 0 or sent == size:
elapsed = time.time() - t0
print(f" {sent}/{size} ({100.0 * sent / size:.1f}%) {elapsed:.1f}s")
log = wait_for(jlink, "FLASH_BIN3_OK", 60.0)
print(log.strip().splitlines()[-1])
print("Done. Universal bank (3.bin) updated.")
finally:
try:
jlink.rtt_stop()
except Exception:
pass
jlink.close()
if __name__ == "__main__":
main()

View File

@ -7,7 +7,7 @@ Layout (absolute W25Q128 offsets):
0x0000CB70 Charg.bin (legacy pad; charge UI uses UI0902_CHARGE_SCREEN full-screen) 0x0000CB70 Charg.bin (legacy pad; charge UI uses UI0902_CHARGE_SCREEN full-screen)
0x0001B8F0 1.bin 普通/专业 31 rhythms 0x0001B8F0 1.bin 普通/专业 31 rhythms
0x0009D07D 2.bin 海阔天空 0x0009D07D 2.bin 海阔天空
0x000A8607 3.bin 万能模式 0x000A71AC 3.bin 万能模式紧随 2.bin具体偏移以打包结果为准
Outputs: Outputs:
tools/out/extflash_tone_0903.bin tools/out/extflash_tone_0903.bin