feat(pack): FACTORY 工厂镜像 + 内置 profile 改名 CeramixNano_MB_Dual_FW

This commit is contained in:
2026-10-22 05:14:58 +08:00
parent 0ffa671656
commit 0a4095150b
10 changed files with 350 additions and 114 deletions
+8 -7
View File
@@ -1,8 +1,8 @@
# DAPLink Flash — GD32 固件烧录 · 双固件打包 · 补齐 · 拼接
> GD32E230 系列的桌面烧录/固件处理工具:**OpenOCD 烧录**(DAPLink / GD-Link /
> ST-Link / J-Link)+ **打包 v2**(双固件 profile 布局 → UPDATE / B / FULL 镜像
> + MD5 清单,与命令行工具共用 `fw_pack.py` 公共库)+ **补齐 Pad**(Y-Modem
> ST-Link / J-Link)+ **打包 v2**(双固件 profile 布局 → UPDATE / FULL / FACTORY
> 镜像 + MD5 清单,与命令行工具共用 `fw_pack.py` 公共库)+ **补齐 Pad**(Y-Modem
> 页对齐)+ **拼接 Merge**(Bootloader + APP + 有效标志 → 整片镜像)。
>
> - **只想用工具**:到 [Gitea Releases](https://gitea.hulk.wang/hulk/gd32e230f8_firmware_merge_tool/releases)
@@ -21,7 +21,7 @@
| 固件格式 | BIN / HEX / ELF——HEX/ELF 自动用文件内地址,仅 BIN 需填基地址(完整镜像 `0x08000000`,单独 APP `0x08002000`) |
| 芯片型号 | GD32E230F4 / F6 / F8 / C8,自动带出 Flash 大小与 target 配置 |
| 烧录器 | DAPLink / CMSIS-DAP、GD-Link、ST-Link、J-Link、CMSIS-DAP(TCP)、Custom;支持适配器速度与探针序列号 |
| 打包 v2 | 双固件 profile 布局(默认 `ceramixnano_mb_v51`):A/B 包向量表自动识别、补页+trailer/满槽+记录、CRC-16/XMODEM、FULL 整片镜像、MD5 清单、写盘回读复核 |
| 打包 v2 | 双固件 profile 布局(默认 `CeramixNano_MB_Dual_FW`):A/B 包向量表自动识别、补页+trailer/满槽+记录、CRC-16/XMODEM、FULL 整片镜像、FACTORY 工厂镜像(BL+B+flag,上电跳 B)、MD5 清单、写盘回读复核 |
| 补齐 Pad | BIN 尾部补 `0xFF` 到 1K/2K/4K 页边界;bootloader 的 Y-Modem 只按 1K 整包写 Flash,最后一包不满必须先补齐 |
| 拼接 Merge | Bootloader + APP + 有效标志(`0xEEEEEEEE` @`0x0800FFFC`) → 整片 Flash 镜像,逻辑与 `merge_bin.py` 一致 |
| 内置工具链 | 发布包自带 OpenOCD(`toolchain/openocd/`),GUI 自动定位,免安装免配置 |
@@ -79,13 +79,14 @@ DAPLinkFlash/
4. **点"检测设备"**确认链路通 → **点"烧录固件"**;输出实时显示在右下日志区;
5. **固件工具**(顶栏按钮弹出的独立窗口,非模态):
- 打包 v2:选 profile(内置下拉或浏览 JSON)+ BL/A 包/B 包 bin(至少一个)+
输出目录,点"执行打包"→ `*_UPDATE.bin` / `*_B.bin` / `*_FULL.bin` + MD5 清单;
输出目录,点"执行打包"→ `*_UPDATE.bin`(A 包)/ `*_FACTORY.bin`(工厂镜像
BL+B+flag,给了 B 包才出)/ `*_FULL.bin`(整片镜像,需 A 包)+ MD5 清单;
A/B 槽按向量表自动校验,逐项字节校验与回读复核结果流进主日志;
- 补齐:选输入 BIN 后输出名自动带出(`*_update.bin`,Y-Modem 升级包),对齐默认 1K,点"执行补齐";
- 拼接:选 Bootloader 与 APP 后输出名自动带出(`*_BL.bin`,整片镜像),地址按典型布局
预填,点"执行拼接"。
v2 双固件布局(默认 profile `ceramixnano_mb_v51`)与打包规则详见仓库根
v2 双固件布局(默认 profile `CeramixNano_MB_Dual_FW`)与打包规则详见仓库根
[README.md](../README.md);legacy 单槽布局:`0x08000000` Bootloader 8KB |
`0x08002000` APP | `0x0800FFFC` 有效标志 `0xEEEEEEEE`。
@@ -109,10 +110,10 @@ python src/flash_gui.py --reset 复位运行
python src/flash_gui.py --pad app.bin 补齐到 1K → app_pad.bin
python src/flash_gui.py --merge bl.bin app.bin 拼接 → app_BL.bin
python src/flash_gui.py --pack a.bin b.bin --pack-bl bl.bin -o outdir
v2 双固件打包 → UPDATE/B/FULL+MD5
v2 双固件打包 → UPDATE/FULL/FACTORY+MD5
--align N / --pad-byte 0xFF / -o OUT pad 参数(-o 对 pack 为目录)
--app-addr / --flag-addr / --flash-size / --flag-value merge 参数
--pack-profile ceramixnano_mb_v51|JSON / --pack-no-full pack 参数
--pack-profile 名字|JSON / --pack-no-full / --pack-no-factory pack 参数
--speed 4000 / --serial SN / --interface cfg / --target cfg
--openocd PATH / --scripts DIR 手动指定工具链(默认自动定位)
```
+6 -5
View File
@@ -317,7 +317,7 @@ class App(ctk.CTk):
box = ctk.CTkFrame(dlg, fg_color='transparent')
box.pack(fill='both', expand=True, padx=18, pady=(14, 12))
ctk.CTkLabel(box, text='打包 (v2 双固件)', font=BOLD, anchor='w').pack(fill='x')
ctk.CTkLabel(box, text='按 profile 布局自动识别 A/B 槽, 产出 UPDATE / B / FULL + MD5 清单',
ctk.CTkLabel(box, text='按 profile 布局自动识别 A/B 槽, 产出 UPDATE / FULL / FACTORY + MD5 清单',
text_color='gray60', anchor='w').pack(fill='x', pady=(0, 6))
row = ctk.CTkFrame(box, fg_color='transparent')
row.pack(fill='x', pady=(4, 0))
@@ -339,8 +339,8 @@ class App(ctk.CTk):
command=self._on_pack).pack(side='right')
ctk.CTkLabel(box, textvariable=self.v_pack_result, text_color=OK_BLUE,
justify='left', anchor='w').pack(fill='x', pady=(8, 0))
ctk.CTkLabel(box, text='A 包→*_UPDATE.bin(OA/Ymodem) B 包→*_B.bin(满槽+记录) '
'FULL→整片镜像(@0x08000000)+*.md5.txt',
ctk.CTkLabel(box, text='A 包→*_UPDATE.bin(OTA/Ymodem) B 包→*_FACTORY.bin(工厂'
' BL+B+flag, 上电跳 B) FULL→整片镜像(@0x08000000)',
text_color='gray50', anchor='w').pack(fill='x', pady=(2, 0))
self._center_dialog(dlg, 720, 400)
@@ -739,7 +739,8 @@ class App(ctk.CTk):
messagebox.showerror('打包失败', str(exc))
self._log('打包失败: %s' % exc)
return
names = {'update': 'UPDATE', 'b': 'B 包', 'full': 'FULL', 'md5': 'MD5 清单'}
names = {'update': 'UPDATE', 'full': 'FULL', 'factory': 'FACTORY',
'md5': 'MD5 清单'}
self.v_pack_result.set('完成: ' + ', '.join(
f"{names[k]} {os.path.basename(p)}" for k, p in outcome.paths.items()))
self._log('打包完成: %s' % ', '.join(outcome.paths.values()))
@@ -847,7 +848,7 @@ class App(ctk.CTk):
ctk.CTkLabel(box, text=f'v{APP_VERSION} — GD32E230 固件烧录与打包工具',
text_color='gray60', anchor='w').pack(anchor='w', pady=(0, 12))
for line in ('OpenOCD 烧录: 检测 / 烧录 / 擦除 / 读取 / 复位 (DAPLink·GD-Link·ST-Link·J-Link)',
'打包 v2: 双固件 profile 布局 → UPDATE / B / FULL 镜像 + MD5 清单',
'打包 v2: 双固件 profile 布局 → UPDATE / FACTORY / FULL 镜像 + MD5 清单',
'补齐 Pad: BIN 尾部补 0xFF 对齐页边界, 适配 Bootloader Y-Modem',
'拼接 Merge: Bootloader + APP + 有效标志 → 整片 Flash 镜像',
'发布包内置 OpenOCD 工具链, 免安装解压即用',
+7 -1
View File
@@ -57,7 +57,7 @@ DEFAULTS = {
"merge_flash": "0x10000",
"flag_value": "0xEEEEEEEE",
# 打包 (v2 双固件, fw_pack)
"pack_profile": "ceramixnano_mb_v51",
"pack_profile": "CeramixNano_MB_Dual_FW",
"pack_bl": "",
"pack_a": "",
"pack_b": "",
@@ -80,6 +80,10 @@ def _legacy_settings():
return path
# 旧内置 profile 名 → 现名 (改名前保存的配置一次性迁移, 旧名不再受支持)
_RENAMED_PROFILE = {"ceramixnano_mb_v51": "CeramixNano_MB_Dual_FW"}
def load_settings():
"""读配置, 带默认值兜底; 新配置不存在时尝试从旧版 per-host 配置迁移"""
data = dict(DEFAULTS)
@@ -97,6 +101,8 @@ def load_settings():
for key in DEFAULTS:
if key in stored:
data[key] = stored[key]
if data.get("pack_profile") in _RENAMED_PROFILE:
data["pack_profile"] = _RENAMED_PROFILE[data["pack_profile"]]
return data
+10 -5
View File
@@ -27,7 +27,7 @@ DAPLink Flash — GD32 固件烧录 · 补齐 · 镜像拼接 — 入口与命
python flash_gui.py --pad app.bin 补齐到 1K 边界(app_pad.bin)
python flash_gui.py --merge bl.bin app.bin 拼接整片镜像(*_BL.bin)
python flash_gui.py --pack a.bin b.bin --pack-bl bl.bin
v2 双固件打包(UPDATE/B/FULL+MD5)
v2 双固件打包(UPDATE/FULL/FACTORY+MD5)
python flash_gui.py --version 打印 OpenOCD 版本
"""
@@ -153,10 +153,13 @@ def run_pack(args):
a, b = found.get('a'), found.get('b')
if not a and not b:
sys.exit('未提供可识别的固件输入 (A 包或 B 包)')
if args.output and os.path.splitext(args.output)[1].lower() == '.bin':
sys.exit('--pack 的 -o 是输出目录(产物自动命名), 不能是 .bin 文件路径')
try:
fw_pack.pack_all(args.pack_profile, a, b_path=b,
bl_path=args.pack_bl or None, out_dir=args.output,
make_full=not args.pack_no_full)
make_full=not args.pack_no_full,
make_factory=not args.pack_no_factory)
except fw_pack.FwPackError as exc:
sys.exit(f'打包失败: {exc}')
return 0
@@ -187,13 +190,15 @@ def main():
op.add_argument('--merge', nargs=2, metavar=('BL', 'APP'),
help='拼接 Bootloader + APP → 整片镜像')
op.add_argument('--pack', nargs='+', metavar='BIN',
help='v2 双固件打包: A/B 包自动识别 → UPDATE/B/FULL+MD5')
ap.add_argument('--pack-profile', default='ceramixnano_mb_v51',
help='v2 双固件打包: A/B 包自动识别 → UPDATE/FULL/FACTORY+MD5')
ap.add_argument('--pack-profile', default='CeramixNano_MB_Dual_FW',
help='--pack 布局 profile(内置名或 JSON 路径)')
ap.add_argument('--pack-bl', default=None, metavar='BIN',
help='--pack 使用的 Bootloader bin')
ap.add_argument('--pack-no-full', action='store_true',
help='--pack 只出 UPDATE/B 包, 不拼 FULL')
help='--pack 不拼 FULL 整片镜像')
ap.add_argument('--pack-no-factory', action='store_true',
help='--pack 不出 FACTORY 工厂镜像')
ap.add_argument('--align', default='1024', help='--pad 对齐字节(默认 1024)')
ap.add_argument('--pad-byte', default='0xFF', help='--pad 填充字节(默认 0xFF)')
ap.add_argument('-o', '--output',