diff --git a/APP/app_log.c b/APP/app_log.c index 4f1e2eb..a4f37b7 100644 --- a/APP/app_log.c +++ b/APP/app_log.c @@ -280,6 +280,12 @@ uint8_t app_log_try_command(const char *cmd) "FLASH_HAITIAN_GO", "FLASH_HAITIAN_OK\n"); 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) { uint8_t hdr[16]; 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) | (cur_hdr[14] << 16) | (cur_hdr[15] << 24))); 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), "TONE @BIN2 magic=%02X%02X%02X%02X max=%lu cnt=%lu name=%.16s\n", hdr[0], hdr[1], hdr[2], hdr[3], diff --git a/MyCode/App_Auto.c b/MyCode/App_Auto.c index 492beb0..63befd7 100644 --- a/MyCode/App_Auto.c +++ b/MyCode/App_Auto.c @@ -148,8 +148,12 @@ void App_Auto_Init(void) int ret = AutoBandTop1_LoadPresetItemFromFlash(0); if (ret != 0) { - LOG_E("AUTO", "LoadPreset fail ret=%d addr=0x%08X map=%s", - ret, (unsigned)ADDRESS, ToneFlashMapBank((uint32_t)ADDRESS)); + uint8_t hdr[16]; + 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 { diff --git a/UI/UI_global.c b/UI/UI_global.c index 9d8b31b..c3e5891 100644 --- a/UI/UI_global.c +++ b/UI/UI_global.c @@ -168,10 +168,23 @@ void UI_ReloadTonePreset(void) if (mGuiData[GUI_TAB_INDEX].Current == 0) { 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", - ParamGuiData[ALL_MODE_PARAM].Current, ret, - AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?", - (unsigned)ADDRESS, (unsigned)EXTFLASH_BIN3_UNIVERSAL_ADDR); + if (ret != 0) + { + uint8_t hdr[16]; + 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; } /* 普通/专业:本地曲目或节奏类型 */ diff --git a/tools/flash_bin3_universal.py b/tools/flash_bin3_universal.py new file mode 100644 index 0000000..9122fe9 --- /dev/null +++ b/tools/flash_bin3_universal.py @@ -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 +""" + +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 '." + ) + 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() diff --git a/tools/pack_extflash_tone_0903.py b/tools/pack_extflash_tone_0903.py index 3ea4b77..423086f 100644 --- a/tools/pack_extflash_tone_0903.py +++ b/tools/pack_extflash_tone_0903.py @@ -7,7 +7,7 @@ Layout (absolute W25Q128 offsets): 0x0000CB70 Charg.bin (legacy pad; charge UI uses UI0902_CHARGE_SCREEN full-screen) 0x0001B8F0 1.bin 普通/专业 31 rhythms 0x0009D07D 2.bin 海阔天空 - 0x000A8607 3.bin 万能模式 + 0x000A71AC 3.bin 万能模式(紧随 2.bin;具体偏移以打包结果为准) Outputs: tools/out/extflash_tone_0903.bin