feat(pack): v2 双固件 profile 打包 — fw_pack 公共库 + CLI 重构 + GUI 打包弹窗
This commit is contained in:
+236
-119
@@ -1,32 +1,110 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
GD32E230 固件工具:整页补齐 + 合成烧录镜像,二合一。
|
||||
GD32E230 固件打包工具 v2 — 布局 profile 化双固件打包 (A/B 槽 + 记录/CRC)
|
||||
================================================================
|
||||
|
||||
模式(互斥):
|
||||
-m / --merge 合成完整 Flash 镜像 (bootloader + APP + FLAG)
|
||||
→ 用于 OpenOCD / J-Link 整片烧录,不补齐
|
||||
-f / --fill 仅补齐 APP 到整页边界
|
||||
→ 用于 Bootloader Ymodem 升级
|
||||
v2 模式(默认): 输入编译好的 APP bin, 自动识别 A/B 槽, 产出
|
||||
*_UPDATE.bin A 槽 OTA/Ymodem 包 (整 N 页 + 末 4B trailer {页数16|CRC16})
|
||||
*_B.bin B 槽底包 (固定满槽 + 槽末 8B 记录 {magic|页数|CRC|2B FF})
|
||||
*_FULL.bin 整片镜像 (BL + A 包 + 槽尾锚点 + B 满槽 + 0xEEEE 标志)
|
||||
*.md5.txt 产物 MD5 清单 + 布局摘要 (随 FULL 生成)
|
||||
打包规则与 BL (dev_fact v5.1) 逐字一致, 写盘后回读复核。计算与校验在
|
||||
fw_pack.py 公共库 (FlashTool GUI 共用)。
|
||||
|
||||
Flash 布局:
|
||||
0x08000000 ┌──────────────┐
|
||||
│ Bootloader │ 8KB
|
||||
APP_ADDR ├──────────────┤
|
||||
│ Application │ 最大 54KB (F8) / 22KB (F6) / 6KB (F4)
|
||||
│ ... │
|
||||
CAL_ADDR │ 校准数据 │ 1KB (预留)
|
||||
FLAG_ADDR │ 0xEEEE 标志 │ 1KB
|
||||
FLASH_END └──────────────┘
|
||||
legacy 模式(过渡期兼容, -f/-m 二选一时启用): 旧单槽方案
|
||||
-f/--fill 纯 0xFF 补页, 无 trailer (旧 BL 的 Ymodem 升级包)
|
||||
-m/--merge BL + APP + 0xEEEE 标志 拼整片镜像 (不补齐)
|
||||
|
||||
v2 Flash 布局(默认 profile ceramixnano_mb_v51, GD32E230F8/64K):
|
||||
0x08000000 Bootloader 8K 页 0~7
|
||||
0x08002000 FW_A 运行槽 36K 页 8~43 OTA 唯一接收区, 槽尾锚点@0x0800AFFC
|
||||
0x0800B000 FW_B 保底槽 19K 页 44~62 固定满槽, B 记录@0x0800FBF8
|
||||
0x0800FC00 标志页 1K 页 63 0xEEEE@0x0800FFFC
|
||||
其他工程换 profile 即可(保底槽 8K/28K/无保底槽等), 见 --profile。
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
import os
|
||||
|
||||
import fw_pack
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, SCRIPT_DIR)
|
||||
|
||||
TOOL_VERSION = '2.0.0'
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
# 常量
|
||||
# v2 模式: 双固件 profile 打包
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
|
||||
def _collect_inputs(args, profile):
|
||||
"""汇合 位置参数(自动识别) 与 --a/--b(显式指定), 冲突报错。
|
||||
返回 (a_path|None, b_path|None)。"""
|
||||
if (args.a or args.b) and args.bins:
|
||||
raise fw_pack.FwPackError(
|
||||
'位置输入(自动识别)与显式 --a/--b 不能混用, 二选一')
|
||||
if args.bins:
|
||||
found = {}
|
||||
for path in args.bins:
|
||||
if not os.path.isfile(path):
|
||||
raise fw_pack.FwPackError(f'找不到输入文件: {path}')
|
||||
data = open(path, 'rb').read()
|
||||
slot = fw_pack.detect_slot(data, profile)
|
||||
if slot in found:
|
||||
raise fw_pack.FwPackError(
|
||||
f'{path!r} 与 {found[slot]!r} 都被识别为 {slot.upper()} 包, '
|
||||
f'一个槽只能有一个输入')
|
||||
found[slot] = path
|
||||
return found.get('a'), found.get('b')
|
||||
if args.a and args.b and os.path.abspath(args.a) == os.path.abspath(args.b):
|
||||
raise fw_pack.FwPackError('--a 与 --b 指向同一个文件')
|
||||
for opt, path, slot in (('--a', args.a, 'a'), ('--b', args.b, 'b')):
|
||||
if not path:
|
||||
continue
|
||||
if not os.path.isfile(path):
|
||||
raise fw_pack.FwPackError(f'找不到 {opt} 输入文件: {path}')
|
||||
data = open(path, 'rb').read()
|
||||
which = fw_pack.detect_slot(data, profile)
|
||||
if which != slot: # 显式指定与向量表判定冲突 → 报错(规格 4.1)
|
||||
raise fw_pack.FwPackError(
|
||||
f'{opt} 显式指定为 {slot.upper()} 槽, 但其复位向量落在 '
|
||||
f'{which.upper()} 槽 — 请改用 --{which} 或调整输入')
|
||||
return args.a, args.b
|
||||
|
||||
|
||||
def run_v2(args):
|
||||
fw_pack.self_test()
|
||||
try:
|
||||
profile = fw_pack.load_profile(args.profile)
|
||||
except fw_pack.FwPackError as exc:
|
||||
print(f'错误:{exc}', file=sys.stderr)
|
||||
return 2
|
||||
try:
|
||||
a_path, b_path = _collect_inputs(args, profile)
|
||||
if not a_path and not b_path:
|
||||
print('错误:v2 模式至少需要一个输入 bin(A 包或 B 包)。\n'
|
||||
'示例: python gd32e230_firmware_tool.py app_a.bin app_b.bin -b bl.bin',
|
||||
file=sys.stderr)
|
||||
return 2
|
||||
out_dir = args.output
|
||||
if out_dir and os.path.splitext(args.output)[1].lower() == '.bin':
|
||||
print('错误:v2 模式 -o 是输出目录(产物自动命名);'
|
||||
'legacy 模式 -o 才是输出文件。', file=sys.stderr)
|
||||
return 2
|
||||
fw_pack.pack_all(
|
||||
args.profile, a_path, b_path=b_path, bl_path=args.bootloader,
|
||||
out_dir=out_dir, make_full=not args.no_full)
|
||||
except fw_pack.FwPackError as exc:
|
||||
print(f'\n错误:{exc}', file=sys.stderr)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
# legacy 模式: 旧单槽方案 (纯 0xFF 补页 / 拼接, 无 trailer)
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
|
||||
FLASH_BASE = 0x08000000
|
||||
@@ -44,33 +122,34 @@ CHIP_CONFIG = {
|
||||
}
|
||||
|
||||
DEFAULT_CHIP = "F8"
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
DEFAULT_BOOTLOADER = os.path.join(SCRIPT_DIR, "gd32e230f8_bootloader_hulk.bin")
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
# 地址计算
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
|
||||
def calc_layout(flash_size):
|
||||
app_addr = FLASH_BASE + BOOTLOADER_SIZE # 0x08002000
|
||||
cal_addr = FLASH_BASE + flash_size - CAL_PAGE_SIZE - FLAG_PAGE_SIZE
|
||||
flag_addr = FLASH_BASE + flash_size - 4 # 最后一页末尾 - 4B
|
||||
app_max = cal_addr - app_addr
|
||||
def calc_layout(flash_size, app_addr=None):
|
||||
if app_addr is None:
|
||||
app_addr = FLASH_BASE + BOOTLOADER_SIZE # 0x08002000
|
||||
cal_addr = FLASH_BASE + flash_size - CAL_PAGE_SIZE - FLAG_PAGE_SIZE
|
||||
app_max = cal_addr - app_addr
|
||||
cal_reserved = True
|
||||
else:
|
||||
# 自定义落位(如 FW_B 槽 0x0800B000):APP 可用到标志页前,校准页让渡给 APP
|
||||
cal_addr = None
|
||||
app_max = FLASH_BASE + flash_size - FLAG_PAGE_SIZE - app_addr
|
||||
cal_reserved = False
|
||||
flag_addr = FLASH_BASE + flash_size - 4 # 最后一页末尾 - 4B
|
||||
flag_page = FLASH_BASE + flash_size - FLAG_PAGE_SIZE
|
||||
return {
|
||||
"flash_size": flash_size,
|
||||
"flash_end": FLASH_BASE + flash_size,
|
||||
"app_addr": app_addr,
|
||||
"app_max": app_max,
|
||||
"cal_addr": cal_addr,
|
||||
"cal_reserved": cal_reserved,
|
||||
"flag_page": flag_page,
|
||||
"flag_addr": flag_addr,
|
||||
}
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
# 补齐
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
|
||||
def pad_to_page(data, page_size=PAGE_SIZE, fill_byte=FILL_BYTE):
|
||||
remainder = len(data) % page_size
|
||||
if remainder == 0:
|
||||
@@ -79,42 +158,39 @@ def pad_to_page(data, page_size=PAGE_SIZE, fill_byte=FILL_BYTE):
|
||||
return data + bytes([fill_byte]) * pad_len, pad_len
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
# 合成模式
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
|
||||
def merge(bootloader_path, app_path, output_path, layout):
|
||||
# 读取
|
||||
with open(bootloader_path, "rb") as f:
|
||||
bl = f.read()
|
||||
with open(app_path, "rb") as f:
|
||||
app = f.read()
|
||||
|
||||
# 校验
|
||||
app_offset = layout["app_addr"] - FLASH_BASE
|
||||
|
||||
if len(bl) > app_offset:
|
||||
print(f"错误:Bootloader ({len(bl)}B) 超过 APP 偏移 ({app_offset}B)", file=sys.stderr)
|
||||
return False
|
||||
if len(app) > layout["app_max"]:
|
||||
limit_desc = (
|
||||
f"{layout['flash_size']//1024}KB - 8KB(BL) - 1KB(校准) - 1KB(Flag) "
|
||||
f"= {layout['app_max']//1024}KB"
|
||||
if layout["cal_reserved"] else
|
||||
f"APP 0x{layout['app_addr']:08X} ~ 0x{layout['flag_page']:08X}(Flag 页前)"
|
||||
f"= {layout['app_max']//1024}KB"
|
||||
)
|
||||
print(
|
||||
f"错误:APP ({len(app)}B / {len(app)/1024:.1f}KB) 超过最大容量 "
|
||||
f"({layout['app_max']}B / {layout['app_max']/1024:.0f}KB)\n"
|
||||
f" 芯片 {layout['flash_size']//1024}KB: "
|
||||
f"{layout['flash_size']//1024}KB - 8KB(BL) - 1KB(校准) - 1KB(Flag) "
|
||||
f"= {layout['app_max']//1024}KB",
|
||||
f"({layout['app_max']}B / {layout['app_max']//1024:.0f}KB)\n"
|
||||
f" 芯片 {layout['flash_size']//1024}KB: {limit_desc}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return False
|
||||
|
||||
# 构建镜像
|
||||
image = bytearray(b'\xFF' * layout["flash_size"])
|
||||
image[0:len(bl)] = bl
|
||||
image[app_offset:app_offset + len(app)] = app
|
||||
flag_offset = layout["flag_addr"] - FLASH_BASE
|
||||
struct.pack_into("<I", image, flag_offset, FLAG_VALUE)
|
||||
|
||||
# 输出
|
||||
with open(output_path, "wb") as f:
|
||||
f.write(image)
|
||||
|
||||
@@ -123,30 +199,31 @@ def merge(bootloader_path, app_path, output_path, layout):
|
||||
print(f" Bootloader : {len(bl):>6}B @ 0x{FLASH_BASE:08X}")
|
||||
print(f" Application: {len(app):>6}B @ 0x{layout['app_addr']:08X}")
|
||||
print(f" Flag : 0x{FLAG_VALUE:04X} @ 0x{layout['flag_addr']:08X}")
|
||||
print(f" 校准预留 : 1KB @ 0x{layout['cal_addr']:08X}")
|
||||
if layout["cal_reserved"]:
|
||||
print(f" 校准预留 : 1KB @ 0x{layout['cal_addr']:08X}")
|
||||
print(f" APP 剩余 : {layout['app_max'] - len(app):>6}B")
|
||||
print(f" 总大小 : {layout['flash_size']:>6}B ({layout['flash_size']//1024}KB)")
|
||||
return True
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
# 补齐模式
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
|
||||
def fill_only(input_path, output_path, layout):
|
||||
with open(input_path, "rb") as f:
|
||||
data = f.read()
|
||||
|
||||
padded, pad_len = pad_to_page(data)
|
||||
|
||||
# 校验补齐后不超过 APP 最大容量
|
||||
if len(padded) > layout["app_max"]:
|
||||
limit_desc = (
|
||||
f"{layout['flash_size']//1024}KB - 8KB(BL) - 1KB(校准) - 1KB(Flag) "
|
||||
f"= {layout['app_max']//1024}KB"
|
||||
if layout["cal_reserved"] else
|
||||
f"APP 0x{layout['app_addr']:08X} ~ 0x{layout['flag_page']:08X}(Flag 页前)"
|
||||
f"= {layout['app_max']//1024}KB"
|
||||
)
|
||||
print(
|
||||
f"错误:补齐后 ({len(padded)}B / {len(padded)/1024:.1f}KB) 超过 APP 最大容量 "
|
||||
f"({layout['app_max']}B / {layout['app_max']/1024:.0f}KB)\n"
|
||||
f" 芯片 {layout['flash_size']//1024}KB: "
|
||||
f"{layout['flash_size']//1024}KB - 8KB(BL) - 1KB(校准) - 1KB(Flag) "
|
||||
f"= {layout['app_max']//1024}KB",
|
||||
f"({layout['app_max']}B / {layout['app_max']//1024:.0f}KB)\n"
|
||||
f" 芯片 {layout['flash_size']//1024}KB: {limit_desc}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
@@ -166,86 +243,126 @@ def fill_only(input_path, output_path, layout):
|
||||
print(f" 说明 : 已是整页对齐,原样输出")
|
||||
|
||||
|
||||
def run_legacy(args):
|
||||
chip = CHIP_CONFIG[args.flash_size]
|
||||
|
||||
app_addr = None
|
||||
if args.app_addr is not None:
|
||||
try:
|
||||
app_addr = int(args.app_addr, 0)
|
||||
except ValueError:
|
||||
print(f"错误:-a/--app-addr 无法解析:{args.app_addr}", file=sys.stderr)
|
||||
return 2
|
||||
if app_addr % PAGE_SIZE != 0:
|
||||
print(f"错误:APP 落位地址 0x{app_addr:08X} 未按 1KB 页对齐", file=sys.stderr)
|
||||
return 2
|
||||
addr_min = FLASH_BASE + BOOTLOADER_SIZE
|
||||
addr_max = FLASH_BASE + chip["size"] - FLAG_PAGE_SIZE
|
||||
if not (addr_min <= app_addr < addr_max):
|
||||
print(
|
||||
f"错误:APP 落位地址 0x{app_addr:08X} 超出允许范围 "
|
||||
f"[0x{addr_min:08X}, 0x{addr_max:08X})",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 2
|
||||
|
||||
layout = calc_layout(chip["size"], app_addr)
|
||||
print(f"[legacy] 芯片: {chip['label']}")
|
||||
|
||||
app = args.bins[0] if args.bins else None
|
||||
if not app or not os.path.isfile(app):
|
||||
print(f"错误:找不到 APP 文件 {app}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
if args.output is None:
|
||||
base, ext = os.path.splitext(os.path.basename(app))
|
||||
suffix = "_BL" if args.merge else "_UPDATE"
|
||||
args.output = os.path.join(os.path.dirname(os.path.abspath(app)),
|
||||
f"{base}{suffix}{ext}")
|
||||
|
||||
if args.merge:
|
||||
bl = args.bootloader or DEFAULT_BOOTLOADER
|
||||
if not os.path.isfile(bl):
|
||||
print(f"错误:找不到 Bootloader 文件 {bl}", file=sys.stderr)
|
||||
return 2
|
||||
if not merge(bl, app, args.output, layout):
|
||||
return 1
|
||||
else:
|
||||
fill_only(app, args.output, layout)
|
||||
return 0
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
# 入口
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="GD32E230 固件工具 — 整页补齐 & 合成烧录镜像",
|
||||
prog='gd32e230_firmware_tool',
|
||||
description=f"GD32E230 固件打包工具 v{TOOL_VERSION} — 双固件 profile 打包"
|
||||
"(默认)/ 旧单槽补齐·拼接(-f/-m)",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
示例:
|
||||
python %(prog)s -m app.bin # F8 合成镜像 (默认)
|
||||
python %(prog)s -m app.bin -s F6 # F6 合成镜像
|
||||
python %(prog)s -f app.bin # F8 补齐升级包 (默认)
|
||||
python %(prog)s -f app.bin -s F4 # F4 补齐升级包
|
||||
python %(prog)s -m app.bin -b my_bl.bin -o out.bin # 自定义 bootloader/输出
|
||||
epilog=f"""
|
||||
v2 双固件模式(默认, 规格: UPGRADE_PLAN_v2.md):
|
||||
python %(prog)s app_a.bin # 识别 A 包 → *_UPDATE.bin (+FULL)
|
||||
python %(prog)s app_a.bin app_b.bin -b bl.bin # 全产物: UPDATE/B/FULL/md5
|
||||
python %(prog)s --a a.bin --b b.bin # 显式指定槽位(与向量表冲突报错)
|
||||
python %(prog)s a.bin --profile f8_single_slot # 换布局(无保底槽)
|
||||
python %(prog)s a.bin --profile my_layout.json # 自定义 profile JSON
|
||||
python %(prog)s b.bin # 仅 B 底包 → *_B.bin
|
||||
|
||||
legacy 模式(旧单槽方案, -f/-m 触发):
|
||||
python %(prog)s -f app.bin # 纯 0xFF 补齐升级包(无 trailer)
|
||||
python %(prog)s -m app.bin # BL+APP+flag 拼整片
|
||||
python %(prog)s -m app.bin -s F6 # F6 芯片
|
||||
python %(prog)s -m app.bin -a 0x0800B000 -b bl.bin # 自定义落位
|
||||
""",
|
||||
)
|
||||
|
||||
# 位置参数
|
||||
parser.add_argument(
|
||||
"app", help="Application 的 .bin 文件路径"
|
||||
)
|
||||
|
||||
# 模式 (互斥)
|
||||
mode = parser.add_mutually_exclusive_group(required=True)
|
||||
mode.add_argument(
|
||||
"-m", "--merge",
|
||||
action="store_true",
|
||||
help="合成完整 Flash 镜像 (bootloader + APP + FLAG),用于整片烧录",
|
||||
)
|
||||
mode.add_argument(
|
||||
"-f", "--fill",
|
||||
action="store_true",
|
||||
help="仅补齐 APP 到整页边界,用于 Bootloader Ymodem 升级",
|
||||
)
|
||||
|
||||
# 选项
|
||||
parser.add_argument(
|
||||
"-s", "--flash-size",
|
||||
default=DEFAULT_CHIP,
|
||||
choices=list(CHIP_CONFIG.keys()),
|
||||
help=f"芯片型号: F4(16K) F6(32K) F8(64K) (默认: {DEFAULT_CHIP})",
|
||||
)
|
||||
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 或 APP名_UPDATE.bin)",
|
||||
)
|
||||
"bins", nargs="*",
|
||||
help="v2: 输入 bin(A/B 包自动识别, 可多个);legacy: APP 路径")
|
||||
mode = parser.add_mutually_exclusive_group()
|
||||
mode.add_argument("-m", "--merge", action="store_true",
|
||||
help="legacy: 合成完整 Flash 镜像 (bootloader + APP + FLAG)")
|
||||
mode.add_argument("-f", "--fill", action="store_true",
|
||||
help="legacy: 仅补齐 APP 到整页边界 (纯 0xFF, 无 trailer)")
|
||||
parser.add_argument("--legacy", action="store_true",
|
||||
help="显式声明 legacy 模式(需配合 -f/-m)")
|
||||
parser.add_argument("--profile", default="ceramixnano_mb_v51",
|
||||
help="v2 布局 profile: 内置名(%s)或 JSON 文件路径"
|
||||
% "/".join(fw_pack.BUILTIN_PROFILES))
|
||||
parser.add_argument("--a", metavar="BIN", default=None,
|
||||
help="v2: 显式指定 A 包(运行槽固件)")
|
||||
parser.add_argument("--b", metavar="BIN", default=None,
|
||||
help="v2: 显式指定 B 包(出厂保底固件)")
|
||||
parser.add_argument("-b", "--bootloader", default=None,
|
||||
help="BL bin (v2: 参与 FULL 拼接; legacy -m: 默认内置 BL)")
|
||||
parser.add_argument("-o", "--output", default=None,
|
||||
help="输出路径: v2=输出目录(自动命名); legacy=输出文件")
|
||||
parser.add_argument("--no-full", action="store_true",
|
||||
help="v2: 只出 UPDATE/B 包, 不拼 FULL")
|
||||
parser.add_argument("-s", "--flash-size", default=DEFAULT_CHIP,
|
||||
choices=list(CHIP_CONFIG.keys()),
|
||||
help="legacy 芯片型号: F4(16K) F6(32K) F8(64K)")
|
||||
parser.add_argument("-a", "--app-addr", default=None,
|
||||
help="legacy: APP 落位地址, 如 0x0800B000 (默认 0x08002000)")
|
||||
|
||||
args = parser.parse_args()
|
||||
print(f"gd32e230_firmware_tool v{TOOL_VERSION} (fw_pack {fw_pack.TOOL_VERSION})")
|
||||
|
||||
chip = CHIP_CONFIG[args.flash_size]
|
||||
layout = calc_layout(chip["size"])
|
||||
legacy = args.fill or args.merge
|
||||
if args.legacy and not legacy:
|
||||
parser.error("--legacy 需配合 -f/--fill 或 -m/--merge 使用")
|
||||
if legacy and (args.profile != "ceramixnano_mb_v51" or args.a or args.b
|
||||
or args.no_full or args.app_addr):
|
||||
parser.error("--profile/--a/--b/--no-full/-a 是 v2/legacy 专属参数, "
|
||||
"不能与 -f/-m 同用")
|
||||
if legacy and len(args.bins) != 1:
|
||||
parser.error("legacy 模式需要一个 APP 位置参数, 例: python %(prog)s -f app.bin"
|
||||
% {'prog': parser.prog})
|
||||
|
||||
print(f"芯片: {chip['label']}")
|
||||
|
||||
# 输出文件名
|
||||
if args.output is None:
|
||||
base, ext = os.path.splitext(os.path.basename(args.app))
|
||||
suffix = "_BL" if args.merge else "_UPDATE"
|
||||
args.output = f"{base}{suffix}{ext}"
|
||||
|
||||
# 输入文件检查
|
||||
if not os.path.isfile(args.app):
|
||||
print(f"错误:找不到 APP 文件 {args.app}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if args.merge:
|
||||
if not os.path.isfile(args.bootloader):
|
||||
print(f"错误:找不到 Bootloader 文件 {args.bootloader}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if not merge(args.bootloader, args.app, args.output, layout):
|
||||
sys.exit(1)
|
||||
else:
|
||||
fill_only(args.app, args.output, layout)
|
||||
rc = run_legacy(args) if legacy else run_v2(args)
|
||||
sys.exit(rc)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user