#!/usr/bin/env python3 """ 合成 GD32E230 初始烧录固件:bootloader + app → 完整 64KB Flash 镜像。 布局: 0x08000000 ┌──────────────┐ │ Bootloader │ APP_ADDR ├──────────────┤ │ Application │ │ ... │ FLAG_ADDR │ 0xEEEE标志 │ 0x08010000 └──────────────┘ 空白区域填充 0xFF(Flash 擦除后的默认值)。 用法: python merge_bin.py bootloader.bin app.bin -o merged.bin python merge_bin.py bootloader.bin app.bin --app-addr 0x08002000 --flag-addr 0x0800FFFC -o merged.bin """ import argparse import struct import sys import os FLASH_BASE = 0x08000000 FLASH_SIZE = 64 * 1024 # 64KB DEFAULT_BOOTLOADER = "gd32e230f8_bootloader_hulk.bin" def merge(bootloader_path, app_path, output_path, app_addr, flag_addr): app_offset = app_addr - FLASH_BASE flag_offset = flag_addr - FLASH_BASE # 校验地址合法性 if app_offset < 0 or app_offset >= FLASH_SIZE: print(f"错误:APP 地址 0x{app_addr:08X} 超出 Flash 范围", file=sys.stderr) return False if flag_offset < 0 or flag_offset + 4 > FLASH_SIZE: print(f"错误:标志地址 0x{flag_addr:08X} 超出 Flash 范围", file=sys.stderr) return False # 读取文件 with open(bootloader_path, "rb") as f: bootloader = f.read() with open(app_path, "rb") as f: app = f.read() # 校验 bootloader 不超过 APP 起始地址 if len(bootloader) > app_offset: print(f"错误:Bootloader ({len(bootloader)} 字节) 超过 APP 偏移 ({app_offset} 字节)", file=sys.stderr) return False # 校验 APP 不超过 flag 地址 app_end = app_offset + len(app) if app_end > flag_offset: print(f"错误:APP 结束地址 0x{FLASH_BASE + app_end:08X} 超过标志地址 0x{flag_addr:08X}", file=sys.stderr) return False # 创建 64KB 的 Flash 镜像,填充 0xFF image = bytearray(b'\xFF' * FLASH_SIZE) # 写入 bootloader(从 offset 0 开始) image[0:len(bootloader)] = bootloader # 写入 APP(从 app_offset 开始) image[app_offset:app_offset + len(app)] = app # 写入标志位 0xEEEEEEEE(little-endian uint32,填满 0x0800FFFC~0x0800FFFF) struct.pack_into("6} 字节 @ 0x{FLASH_BASE:08X}") print(f" Application: {len(app):>6} 字节 @ 0x{app_addr:08X}") print(f" Flag : 0xEEEE @ 0x{flag_addr:08X}") print(f" 总大小 : {FLASH_SIZE:>6} 字节 ({FLASH_SIZE // 1024}KB)") return True def main(): parser = argparse.ArgumentParser(description="合成 GD32E230 初始烧录固件 (bootloader + app)") parser.add_argument("app", help="Application bin 文件路径") parser.add_argument( "-b", "--bootloader", default=DEFAULT_BOOTLOADER, help=f"Bootloader bin 文件路径 (默认: {DEFAULT_BOOTLOADER})", ) parser.add_argument("-o", "--output", default=None, help="输出文件路径 (默认: APP文件名_BL.bin)") parser.add_argument("--app-addr", default="0x08002000", help="APP 起始地址 (默认: 0x08002000)") parser.add_argument("--flag-addr", default="0x0800FFFC", help="标志位地址 (默认: 0x0800FFFC)") args = parser.parse_args() if args.output is None: base, ext = os.path.splitext(os.path.basename(args.app)) args.output = f"{base}_BL{ext}" app_addr = int(args.app_addr, 0) flag_addr = int(args.flag_addr, 0) if not os.path.isfile(args.bootloader): print(f"错误:找不到文件 {args.bootloader}", file=sys.stderr) sys.exit(1) if not os.path.isfile(args.app): print(f"错误:找不到文件 {args.app}", file=sys.stderr) sys.exit(1) if not merge(args.bootloader, args.app, args.output, app_addr, flag_addr): sys.exit(1) if __name__ == "__main__": main()