初步版本发布
This commit is contained in:
+20
@@ -0,0 +1,20 @@
|
|||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
*.egg-info/
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Binary files
|
||||||
|
*.bin
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
# GD32 Firmware Merge Tool
|
||||||
|
|
||||||
|
将 Bootloader 和 Application 的 bin 文件合成为一个完整的 Flash 镜像,用于 GD32 系列 MCU 的初始烧录。
|
||||||
|
|
||||||
|
## Flash 布局
|
||||||
|
|
||||||
|
```
|
||||||
|
地址 内容
|
||||||
|
0x08000000 ┌──────────────────┐
|
||||||
|
│ Bootloader │
|
||||||
|
0x08002000 ├──────────────────┤
|
||||||
|
│ Application │
|
||||||
|
│ ... │
|
||||||
|
│ (0xFF 填充) │
|
||||||
|
0x0800FFFC │ 0xEEEEEEEE │ ← 有效 APP 标志
|
||||||
|
0x08010000 └──────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
- 空白区域填充 `0xFF`(Flash 擦除后的默认值)
|
||||||
|
- 标志位 `0xEEEEEEEE` 告诉 Bootloader:Flash 中已存在有效的 APP,可以直接跳转执行
|
||||||
|
|
||||||
|
## 环境要求
|
||||||
|
|
||||||
|
- Python 3.x(标准安装即可,无需第三方库)
|
||||||
|
|
||||||
|
## 用法
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python merge_bin.py <bootloader.bin> <app.bin> [-o 输出文件] [--app-addr 地址] [--flag-addr 地址]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 参数说明
|
||||||
|
|
||||||
|
| 参数 | 必填 | 默认值 | 说明 |
|
||||||
|
|------|:----:|--------|------|
|
||||||
|
| `bootloader` | ✅ | — | Bootloader 的 .bin 文件路径 |
|
||||||
|
| `app` | ✅ | — | Application 的 .bin 文件路径 |
|
||||||
|
| `-o, --output` | ❌ | `merged.bin` | 输出文件路径 |
|
||||||
|
| `--app-addr` | ❌ | `0x08002000` | APP 在 Flash 中的起始地址 |
|
||||||
|
| `--flag-addr` | ❌ | `0x0800FFFC` | APP 有效标志位的地址 |
|
||||||
|
|
||||||
|
### 示例
|
||||||
|
|
||||||
|
**基本用法**(使用默认地址):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python merge_bin.py bootloader.bin app.bin -o merged.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
**指定自定义地址**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python merge_bin.py bootloader.bin app.bin --app-addr 0x08002000 --flag-addr 0x0800FFFC -o merged.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
**实际项目示例**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python merge_bin.py \
|
||||||
|
gd32e23x_V1.0.0_bootloader_2026-03-23.bin \
|
||||||
|
CeramixNano-MB_V1.0.9_APP_2026-03-23.bin \
|
||||||
|
-o CeramixNano-MB_V1.0.9_APP_BL_2026-03-23.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
### 输出示例
|
||||||
|
|
||||||
|
```
|
||||||
|
合成完成: merged.bin
|
||||||
|
Bootloader : 3272 字节 @ 0x08000000
|
||||||
|
Application: 25600 字节 @ 0x08002000
|
||||||
|
Flag : 0xEEEE @ 0x0800FFFC
|
||||||
|
总大小 : 65536 字节 (64KB)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 烧录
|
||||||
|
|
||||||
|
合成后的 bin 文件可直接用 OpenOCD 整片烧录:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openocd -f interface/cmsis-dap.cfg -f target/gd32e23x.cfg \
|
||||||
|
-c "init; reset halt; flash write_image erase merged.bin 0x08000000; reset; exit"
|
||||||
|
```
|
||||||
|
|
||||||
|
或使用 J-Link:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
JFlash -openprj project.jflash -open merged.bin,0x08000000 -auto -exit
|
||||||
|
```
|
||||||
|
|
||||||
|
## 安全校验
|
||||||
|
|
||||||
|
脚本内置以下校验,不满足时会报错退出:
|
||||||
|
|
||||||
|
- APP 地址和标志地址必须在 Flash 范围内(0x08000000 ~ 0x08010000)
|
||||||
|
- Bootloader 体积不能超过 APP 起始偏移
|
||||||
|
- APP 结束地址不能覆盖标志位地址
|
||||||
|
|
||||||
|
## 适配其他型号
|
||||||
|
|
||||||
|
修改脚本顶部常量即可适配不同 Flash 大小的 MCU:
|
||||||
|
|
||||||
|
```python
|
||||||
|
FLASH_BASE = 0x08000000
|
||||||
|
FLASH_SIZE = 64 * 1024 # 修改为目标 MCU 的 Flash 大小
|
||||||
|
```
|
||||||
|
|
||||||
|
通过 `--app-addr` 和 `--flag-addr` 参数适配不同的内存布局,无需修改代码。
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
+108
@@ -0,0 +1,108 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
|
||||||
|
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("<I", image, flag_offset, 0xEEEEEEEE)
|
||||||
|
|
||||||
|
# 输出
|
||||||
|
with open(output_path, "wb") as f:
|
||||||
|
f.write(image)
|
||||||
|
|
||||||
|
print(f"合成完成: {output_path}")
|
||||||
|
print(f" Bootloader : {len(bootloader):>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("bootloader", help="Bootloader bin 文件路径")
|
||||||
|
parser.add_argument("app", help="Application bin 文件路径")
|
||||||
|
parser.add_argument("-o", "--output", default="merged.bin", help="输出文件路径 (默认: merged.bin)")
|
||||||
|
parser.add_argument("--app-addr", default="0x08002000", help="APP 起始地址 (默认: 0x08002000)")
|
||||||
|
parser.add_argument("--flag-addr", default="0x0800FFFC", help="标志位地址 (默认: 0x0800FFFC)")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
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()
|
||||||
Reference in New Issue
Block a user