diff --git a/Global/Global.c b/Global/Global.c index 3d8b500..3e0254f 100644 --- a/Global/Global.c +++ b/Global/Global.c @@ -101,7 +101,7 @@ int16_t MIC_VOL_MAP[11]= {-9000,-7000,-5000,-3000,-1000,1000,3000,5000,7000,9000 uint8_t TOUCH_AREA_NUM = (sizeof(Touch_Areas)/sizeof(Touch_AreaTypeDef)); -const uint8_t Version[3] = {0,2,6}; /* major.minor.patch — 0.2.6: local song 2.bin from Doc/音色文件/0908/2ss_2.bin */ +const uint8_t Version[3] = {0,2,7}; /* major.minor.patch — 0.2.7: 1.bin from Doc/音色文件/0909/31cs.bin; fixed BIN3 pack */ uint8_t Led = 8; bool PressFlag = 0; diff --git a/project/inc/ExtFlash_Tone_Addr.h b/project/inc/ExtFlash_Tone_Addr.h index db50753..7eeab71 100644 --- a/project/inc/ExtFlash_Tone_Addr.h +++ b/project/inc/ExtFlash_Tone_Addr.h @@ -18,7 +18,7 @@ #define EXTFLASH_CHARGING_H 190 #define EXTFLASH_BIN1_RHYTHM_ADDR 0x0001B8F0UL -#define EXTFLASH_BIN1_RHYTHM_SIZE 530317UL +#define EXTFLASH_BIN1_RHYTHM_SIZE 530273UL #define EXTFLASH_BIN2_SONG_HAITIAN_ADDR 0x0009D07DUL #define EXTFLASH_BIN2_SONG_HAITIAN_SIZE 41263UL diff --git a/protocol/git_user_fw_ver.h b/protocol/git_user_fw_ver.h index 4442a7a..0d43ffc 100644 --- a/protocol/git_user_fw_ver.h +++ b/protocol/git_user_fw_ver.h @@ -1,16 +1,16 @@ /* Auto-generated by tools/gen_git_user_fw_ver.py — do not edit. - * git HEAD (dirty): 57448b3da8a3f912503e9d50313b5ec635888b69 + * git HEAD (dirty): 99f7ed3e5a7750dd37de907bc97dcc7b4e8d8739 * branch: develop → develop - * 01 0C wire: develop_57448b* + * 01 0C wire: develop_99f7ed* */ #ifndef GIT_USER_FW_VER_H #define GIT_USER_FW_VER_H -#define GIT_COMMIT_ID_FULL "57448b3da8a3f912503e9d50313b5ec635888b69" +#define GIT_COMMIT_ID_FULL "99f7ed3e5a7750dd37de907bc97dcc7b4e8d8739" #define GIT_BRANCH_NAME "develop" -#define GIT_COMMIT_SHORT6 "57448b" +#define GIT_COMMIT_SHORT6 "99f7ed" #define GIT_DIRTY (1) -#define GIT_BUILD_ID "develop_57448b*" +#define GIT_BUILD_ID "develop_99f7ed*" #define GIT_BUILD_ID_LEN 15u #endif /* GIT_USER_FW_VER_H */ diff --git a/tools/pack_extflash_tone_0903.py b/tools/pack_extflash_tone_0903.py index 3b9973d..255d82c 100644 --- a/tools/pack_extflash_tone_0903.py +++ b/tools/pack_extflash_tone_0903.py @@ -6,11 +6,13 @@ Layout (absolute W25Q128 offsets — BIN2/BIN3 are FIXED so FW addresses stay st 0x00000000 logo.bin (legacy pad; boot UI uses UI0902 full-screen logo) 0x0000CB70 Charg.bin (legacy pad; charge UI uses UI0902_CHARGE_SCREEN full-screen) 0x0001B8F0 1.bin 普通/专业 31 rhythms - 0x0009D07D 2.bin 本地曲目(变长,尾部 0xFF 填到 BIN3) + 0x0009D07D 2.bin 本地曲目(变长,尾部 0xFF 填到 BIN3;<=41KB) 0x000A71AC 3.bin 万能模式(固定;勿随 2.bin 长度漂移) Outputs: tools/out/extflash_tone_0903.bin + tools/out/extflash_tone_0903.res (same bytes, .res alias) + tools/out/extflash_ALL_tone0903_ui0902.res (tone + pad + ui0902, if ui pack present) project/inc/ExtFlash_Tone_Addr.h Doc/音色文件/0903/FLASH_MAP.txt """ @@ -25,8 +27,11 @@ ROOT = Path(__file__).resolve().parents[1] # firmware project root REPO = ROOT.parent.parent # 一诺国际吉他 OUT_DIR = ROOT / "tools" / "out" OUT_BIN = OUT_DIR / "extflash_tone_0903.bin" +OUT_RES = OUT_DIR / "extflash_tone_0903.res" +OUT_ALL_RES = OUT_DIR / "extflash_ALL_tone0903_ui0902.res" OUT_HDR = ROOT / "project" / "inc" / "ExtFlash_Tone_Addr.h" OUT_MAP = REPO / "Doc" / "音色文件" / "0903" / "FLASH_MAP.txt" +UI0902_BIN = OUT_DIR / "ui0902_res.bin" TONE_DIR = REPO / "Doc" / "音色文件" / "0903" @@ -38,6 +43,8 @@ OFF_BIN2 = 0x0009D07D OFF_BIN3 = 0x000A71AC # FIXED: never place 3.bin by concatenating after variable 2.bin UI0902_RES_BASE = 0x00100000 DAB_MAGIC = b"\xABDAB" +MAX_SONG_BIN_BYTES = 41 * 1024 # 曲目文件(2.bin)硬上限 41KB +BIN2_SLOT_BYTES = OFF_BIN3 - OFF_BIN2 # 41263; must keep BIN3 fixed def find_ziliao() -> Path: @@ -60,16 +67,38 @@ def dab_ok(blob: bytes, off: int, expect_cnt: int | None = None) -> None: raise SystemExit(f"DAB check @0x{off:X}: cnt={cnt} want {expect_cnt}") +def require_region_equals(blob: bytes, off: int, src: bytes, label: str) -> None: + end = off + len(src) + if end > len(blob): + raise SystemExit(f"{label}: pack too short for region @0x{off:X}+{len(src)}") + if blob[off:end] != src: + raise SystemExit(f"{label}: bytes @0x{off:X} do not match source file") + + def main() -> None: ziliao = find_ziliao() + bin1_path = TONE_DIR / "1.bin" + bin2_path = TONE_DIR / "2.bin" + bin3_path = TONE_DIR / "3.bin" parts = [ ("LOGO", ziliao / "logo.bin", OFF_LOGO, "legacy pad; boot uses UI0902_BOOT_LOGO"), ("CHARGING", ziliao / "Charg.bin", OFF_CHARGING, "legacy pad; UI uses UI0902_CHARGE_SCREEN"), - ("BIN1_RHYTHM", TONE_DIR / "1.bin", OFF_BIN1, "普通/专业 31 条节奏"), - ("BIN2_SONG_HAITIAN", TONE_DIR / "2.bin", OFF_BIN2, "本地曲目"), - ("BIN3_UNIVERSAL", TONE_DIR / "3.bin", OFF_BIN3, "万能模式(固定偏移)"), + ("BIN1_RHYTHM", bin1_path, OFF_BIN1, "普通/专业 31 条节奏"), + ("BIN2_SONG_HAITIAN", bin2_path, OFF_BIN2, "本地曲目"), + ("BIN3_UNIVERSAL", bin3_path, OFF_BIN3, "万能模式(固定偏移)"), ] + bin2_data = bin2_path.read_bytes() + if len(bin2_data) > MAX_SONG_BIN_BYTES: + raise SystemExit( + f"2.bin (曲目) too large: {len(bin2_data)} bytes > {MAX_SONG_BIN_BYTES} (41KB)" + ) + if len(bin2_data) > BIN2_SLOT_BYTES: + raise SystemExit( + f"2.bin too large for fixed BIN3 slot: {len(bin2_data)} > {BIN2_SLOT_BYTES} " + f"(would shift 3.bin past 0x{OFF_BIN3:X})" + ) + blobs: list[bytes] = [] cursor = 0 rows: list[tuple[str, int, int, str]] = [] @@ -85,13 +114,6 @@ def main() -> None: blobs.append(b"\xFF" * pad) rows.append(("(pad)", cursor, pad, "gap fill 0xFF")) cursor = force_off - # BIN2 must not overflow into fixed BIN3 slot - if name == "BIN2_SONG_HAITIAN" and force_off + len(data) > OFF_BIN3: - raise SystemExit( - f"BIN2 too large ({len(data)} bytes): " - f"0x{force_off:X}+{len(data)} overflows fixed BIN3 @0x{OFF_BIN3:X} " - f"(max {OFF_BIN3 - force_off} bytes)" - ) off = cursor blobs.append(data) cursor += len(data) @@ -100,21 +122,25 @@ def main() -> None: OUT_DIR.mkdir(parents=True, exist_ok=True) packed = b"".join(blobs) OUT_BIN.write_bytes(packed) + OUT_RES.write_bytes(packed) # 总音色 .bin 同步为 .res by_name = {r[0]: r for r in rows if not r[0].startswith("(")} + bin1_data = bin1_path.read_bytes() + bin3_data = bin3_path.read_bytes() - # Hard integrity gates — catch 2.bin drift / missing 3.bin before publish. - dab_ok(packed, by_name["BIN1_RHYTHM"][1], expect_cnt=31) - dab_ok(packed, by_name["BIN2_SONG_HAITIAN"][1], expect_cnt=1) + # Hard integrity gates + dab_ok(packed, OFF_BIN1, expect_cnt=31) + dab_ok(packed, OFF_BIN2, expect_cnt=1) dab_ok(packed, OFF_BIN3, expect_cnt=3) + if by_name["BIN1_RHYTHM"][1] != OFF_BIN1: + raise SystemExit(f"BIN1 placed @0x{by_name['BIN1_RHYTHM'][1]:X} want 0x{OFF_BIN1:X}") + if by_name["BIN2_SONG_HAITIAN"][1] != OFF_BIN2: + raise SystemExit(f"BIN2 placed @0x{by_name['BIN2_SONG_HAITIAN'][1]:X} want 0x{OFF_BIN2:X}") if by_name["BIN3_UNIVERSAL"][1] != OFF_BIN3: - raise SystemExit( - f"BIN3 placed @0x{by_name['BIN3_UNIVERSAL'][1]:X} but FW requires 0x{OFF_BIN3:X}" - ) - if packed[OFF_BIN3 : OFF_BIN3 + len(Path(TONE_DIR / "3.bin").read_bytes())] != ( - TONE_DIR / "3.bin" - ).read_bytes(): - raise SystemExit("BIN3 region does not match Doc/.../0903/3.bin") + raise SystemExit(f"BIN3 placed @0x{by_name['BIN3_UNIVERSAL'][1]:X} want 0x{OFF_BIN3:X}") + require_region_equals(packed, OFF_BIN1, bin1_data, "BIN1/1.bin") + require_region_equals(packed, OFF_BIN2, bin2_data, "BIN2/2.bin") + require_region_equals(packed, OFF_BIN3, bin3_data, "BIN3/3.bin") hdr = f"""#ifndef __EXTFLASH_TONE_ADDR_H #define __EXTFLASH_TONE_ADDR_H @@ -135,10 +161,10 @@ def main() -> None: #define EXTFLASH_CHARGING_W 160 #define EXTFLASH_CHARGING_H 190 -#define EXTFLASH_BIN1_RHYTHM_ADDR 0x{by_name['BIN1_RHYTHM'][1]:08X}UL +#define EXTFLASH_BIN1_RHYTHM_ADDR 0x{OFF_BIN1:08X}UL #define EXTFLASH_BIN1_RHYTHM_SIZE {by_name['BIN1_RHYTHM'][2]}UL -#define EXTFLASH_BIN2_SONG_HAITIAN_ADDR 0x{by_name['BIN2_SONG_HAITIAN'][1]:08X}UL +#define EXTFLASH_BIN2_SONG_HAITIAN_ADDR 0x{OFF_BIN2:08X}UL #define EXTFLASH_BIN2_SONG_HAITIAN_SIZE {by_name['BIN2_SONG_HAITIAN'][2]}UL #define EXTFLASH_BIN3_UNIVERSAL_ADDR 0x{OFF_BIN3:08X}UL @@ -164,10 +190,10 @@ def main() -> None: map_lines = [ "K1 external Flash map — tone pack 0903", f"Packed file: Code/.../tools/out/{OUT_BIN.name} ({len(packed)} bytes)", + f"Also: {OUT_RES.name}; ALL.res when ui0902_res.bin present", f"Ends at 0x{cursor:X}; UI0902_RES_BASE=0x00100000; free gap={0x100000 - cursor} bytes", "", - "NOTE: BIN3 is FIXED at 0x000A71AC. Shrinking 2.bin pads 0xFF up to BIN3;", - " growing 2.bin beyond (0xA71AC-0x9D07D)=41263 bytes fails the pack.", + "NOTE: BIN3 is FIXED at 0x000A71AC. 2.bin must be <= 41KB and <= slot 41263 bytes.", "", f"{'Name':<22} {'Offset':>10} {'Size':>10} Note", "-" * 72, @@ -177,15 +203,15 @@ def main() -> None: map_lines += [ "", "Firmware ADDRESS mapping:", - " 普通/专业 -> FLASH_ADDR_MODE_NORMAL/EXPERT (1.bin)", - " 本地曲目 -> FLASH_ADDR_SONG_HAITIAN (2.bin @ 0x9D07D)", + " 普通/专业 -> FLASH_ADDR_MODE_NORMAL/EXPERT (1.bin @ 0x1B8F0)", + " 本地曲目 -> FLASH_ADDR_SONG_HAITIAN (2.bin @ 0x9D07D, max 41KB)", " 万能 -> FLASH_ADDR_MODE_UNIVERSAL (3.bin @ 0xA71AC FIXED)", " AutoBand -> FLASH_ADDR_AUTOBAND_LEGACY 0x9EB5F (unchanged; overlaps 2.bin region — do not enable until remapped)", " Boot logo -> UI0902_BOOT_LOGO_ADDR (full-screen); packed logo.bin only pads 0x0..0xCB70", " Charging -> EXTFLASH_CHARGING_ADDR", "", "Add a local song:", - " 1. Pack the new preset into 2.bin (must stay <= 41263 bytes unless BIN3 offset is raised in pack script + FW)", + " 1. Pack the new preset into 2.bin (must stay <= 41KB / 41263 slot)", " 2. Append a row to local_songs.csv (index,code,name)", " 3. python tools/pack_extflash_tone_0903.py (also regenerates LocalSongNames.h)", " 4. Rebuild firmware and flash MCU + ExtFlash ALL.res", @@ -193,6 +219,7 @@ def main() -> None: OUT_MAP.write_text("\n".join(map_lines) + "\n", encoding="utf-8", newline="\n") print(f"Wrote {OUT_BIN} ({len(packed)} bytes)") + print(f"Wrote {OUT_RES} ({len(packed)} bytes)") print(f"Wrote {OUT_HDR}") print(f"Wrote {OUT_MAP}") for name, off, size, note in rows: @@ -200,7 +227,27 @@ def main() -> None: if cursor > UI0902_RES_BASE: raise SystemExit("ERROR: pack overflows into UI0902_RES_BASE") print(f"OK: {UI0902_RES_BASE - cursor} bytes free before UI0902 @ 0x{UI0902_RES_BASE:X}") - print(f"OK: BIN3 fixed @0x{OFF_BIN3:X} magic=ABDAB cnt=3 matches 3.bin") + print("OK: BIN1/BIN2/BIN3 present at fixed addresses and match source files") + print(f"OK: 2.bin size {len(bin2_data)} <= 41KB ({MAX_SONG_BIN_BYTES})") + + if UI0902_BIN.is_file(): + ui = UI0902_BIN.read_bytes() + all_res = packed + (b"\xFF" * (UI0902_RES_BASE - len(packed))) + ui + require_region_equals(all_res, OFF_BIN1, bin1_data, "ALL.res BIN1") + require_region_equals(all_res, OFF_BIN2, bin2_data, "ALL.res BIN2") + require_region_equals(all_res, OFF_BIN3, bin3_data, "ALL.res BIN3") + dab_ok(all_res, OFF_BIN1, 31) + dab_ok(all_res, OFF_BIN2, 1) + dab_ok(all_res, OFF_BIN3, 3) + OUT_ALL_RES.write_bytes(all_res) + # Mirror under repo tools/out for publish/consumers + repo_out = REPO / "tools" / "out" + repo_out.mkdir(parents=True, exist_ok=True) + (repo_out / OUT_ALL_RES.name).write_bytes(all_res) + (repo_out / OUT_RES.name).write_bytes(packed) + print(f"Wrote {OUT_ALL_RES} ({len(all_res)} bytes) — verified 1/2/3.bin") + else: + print(f"WARN: {UI0902_BIN} missing; skipped ALL.res") gen = ROOT / "tools" / "gen_local_song_names.py" r = subprocess.run([sys.executable, str(gen)], check=False) diff --git a/tools/publish_k1_release.py b/tools/publish_k1_release.py index 41372d1..bbc014d 100644 --- a/tools/publish_k1_release.py +++ b/tools/publish_k1_release.py @@ -35,7 +35,7 @@ MAP_TXT = REPO / "Doc" / "音色文件" / "0903" / "FLASH_MAP.txt" IAP_DIR = REPO / "升级" / "MCU主控升级" OUT_ROOT = REPO / "tools" / "out" -FW_VER = "0.2.6" +FW_VER = "0.2.7" TONE_RES_VER = "0903" UI0902_RES_BASE = 0x00100000 BOOT_FLASH_ADDR = 0x08000000 @@ -69,15 +69,15 @@ def build_combined_extflash(tone: bytes, ui: bytes) -> bytes: return tone + (b"\xFF" * pad) + ui -def verify_tone_layout(tone: bytes) -> None: - """Refuse to ship ALL.res if BIN3 is missing/shifted (2.bin length drift).""" +def verify_tone_layout(tone: bytes, check_sources: bool = True) -> None: + """Refuse to ship ALL.res if 1/2/3.bin missing, shifted, or content mismatch.""" dab = b"\xABDAB" - checks = [ - (0x0001B8F0, 31, "BIN1"), - (0x0009D07D, 1, "BIN2"), - (0x000A71AC, 3, "BIN3"), - ] - for off, want_cnt, name in checks: + sources = { + "BIN1": (0x0001B8F0, 31, REPO / "Doc" / "音色文件" / "0903" / "1.bin"), + "BIN2": (0x0009D07D, 1, REPO / "Doc" / "音色文件" / "0903" / "2.bin"), + "BIN3": (0x000A71AC, 3, REPO / "Doc" / "音色文件" / "0903" / "3.bin"), + } + for name, (off, want_cnt, src_path) in sources.items(): if off + 16 > len(tone): raise SystemExit(f"verify {name}: tone pack too short for 0x{off:X}") magic = tone[off : off + 4] @@ -85,10 +85,15 @@ def verify_tone_layout(tone: bytes) -> None: if magic != dab or cnt != want_cnt: raise SystemExit( f"verify {name} @0x{off:X} failed: magic={magic.hex()} cnt={cnt} " - f"(want ABDAB/{want_cnt}). Re-run pack_extflash_tone_0903.py; " - f"do not publish a pack where 2.bin length shifted 3.bin." + f"(want ABDAB/{want_cnt}). Re-run pack_extflash_tone_0903.py." ) - print("verify tone layout: BIN1/BIN2/BIN3 DAB OK at fixed FW addresses") + if check_sources and src_path.is_file(): + src = src_path.read_bytes() + if name == "BIN2" and len(src) > 41 * 1024: + raise SystemExit(f"verify BIN2: source 2.bin {len(src)} > 41KB") + if tone[off : off + len(src)] != src: + raise SystemExit(f"verify {name}: content @0x{off:X} != {src_path}") + print("verify tone layout: BIN1/BIN2/BIN3 addresses + content OK") def main() -> None: