添加普冉 PY32F040 OTA 双工程代码生成模板
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
合并 bootloader.hex + app.hex 为出厂烧录用的 merged_single.hex。
|
||||
用法:
|
||||
python tools/merge_hex.py [--out OUT] [--bl bootloader.hex] [--app app.hex]
|
||||
默认输入/输出均位于 PY32F040开发框架/Output/ 下。
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
# 默认相对本脚本: ../Output (脚本位于 框架/tools/)
|
||||
_DEF_OUT_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
"Output")
|
||||
|
||||
|
||||
def parse_hex(path):
|
||||
"""解析 Intel HEX -> dict[abs_addr] = byte。完整支持 0x04 扩展线性地址记录,
|
||||
得到 32 位绝对地址。本工程基址 0x08000000 即由 0x04 记录(:020000040800F2)给出。"""
|
||||
mem = {}
|
||||
upper = 0 # 0x04 记录的扩展线性地址(左移到高 16 位)
|
||||
with open(path, "r") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line.startswith(":"):
|
||||
continue
|
||||
n = int(line[1:3], 16)
|
||||
addr = int(line[3:7], 16)
|
||||
rec = int(line[7:9], 16)
|
||||
if rec == 0x00:
|
||||
data = bytes.fromhex(line[9:9 + n * 2])
|
||||
base = (upper << 16) | addr
|
||||
for i, b in enumerate(data):
|
||||
mem[base + i] = b
|
||||
elif rec == 0x01: # EOF
|
||||
break
|
||||
elif rec == 0x04: # 扩展线性地址
|
||||
upper = int(line[9:13], 16)
|
||||
# 0x02/0x03/0x05 等本工程不涉及,忽略
|
||||
return mem
|
||||
|
||||
|
||||
def _emit_ela(upper):
|
||||
"""0x04 扩展线性地址记录。"""
|
||||
body = "02000004%04X" % upper
|
||||
cs = 0
|
||||
for i in range(0, len(body), 2):
|
||||
cs += int(body[i:i + 2], 16)
|
||||
cs = (0x100 - (cs & 0xFF)) & 0xFF
|
||||
return ":%s%02X" % (body, cs)
|
||||
|
||||
|
||||
def _emit(buf, addr):
|
||||
body = "%02X%04X00" % (len(buf), addr) + "".join("%02X" % b for b in buf)
|
||||
cs = 0
|
||||
for i in range(0, len(body), 2):
|
||||
cs += int(body[i:i + 2], 16)
|
||||
cs = (0x100 - (cs & 0xFF)) & 0xFF
|
||||
return ":%s%02X" % (body, cs)
|
||||
|
||||
|
||||
def write_hex(path, mem):
|
||||
"""按 32 位绝对地址写出;每当高 16 位(upper)变化时插入 0x04 扩展记录,
|
||||
保证 0x08000000 / 0x08004800 等基址被烧录器正确识别。"""
|
||||
addrs = sorted(mem)
|
||||
lines = []
|
||||
buf = []
|
||||
cur_upper = None
|
||||
cur = None
|
||||
next_addr = None
|
||||
|
||||
def flush():
|
||||
if buf:
|
||||
lines.append(_emit(buf, cur))
|
||||
buf.clear()
|
||||
|
||||
for a in addrs:
|
||||
up = (a >> 16) & 0xFFFF
|
||||
off = a & 0xFFFF
|
||||
if cur_upper is None:
|
||||
cur_upper = up
|
||||
lines.append(_emit_ela(up))
|
||||
cur = off
|
||||
next_addr = a + 1
|
||||
elif up != cur_upper:
|
||||
flush()
|
||||
cur_upper = up
|
||||
lines.append(_emit_ela(up))
|
||||
cur = off
|
||||
next_addr = a + 1
|
||||
elif a != next_addr:
|
||||
flush()
|
||||
cur = off
|
||||
next_addr = a + 1
|
||||
buf.append(mem[a])
|
||||
next_addr = a + 1
|
||||
if len(buf) == 16:
|
||||
flush()
|
||||
cur = next_addr & 0xFFFF
|
||||
flush()
|
||||
lines.append(":00000001FF")
|
||||
with open(path, "w") as f:
|
||||
f.write("\n".join(lines) + "\n")
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--bl", default=os.path.join(_DEF_OUT_DIR, "bootloader.hex"))
|
||||
ap.add_argument("--app", default=os.path.join(_DEF_OUT_DIR, "app.hex"))
|
||||
ap.add_argument("--out", default=os.path.join(_DEF_OUT_DIR, "merged_single.hex"))
|
||||
args = ap.parse_args()
|
||||
|
||||
for p in (args.bl, args.app):
|
||||
if not os.path.isfile(p):
|
||||
sys.exit("ERROR: 找不到 %s" % p)
|
||||
|
||||
bl = parse_hex(args.bl)
|
||||
app = parse_hex(args.app)
|
||||
print("bootloader: %d bytes, range 0x%X-0x%X" % (len(bl), min(bl), max(bl)))
|
||||
print("app: %d bytes, range 0x%X-0x%X" % (len(app), min(app), max(app)))
|
||||
|
||||
overlap = set(bl) & set(app)
|
||||
if overlap:
|
||||
sys.exit("ERROR: 地址重叠 %d 处 (例如 0x%X)" % (len(overlap), min(overlap)))
|
||||
|
||||
merged = dict(bl)
|
||||
merged.update(app)
|
||||
write_hex(args.out, merged)
|
||||
nlines = sum(1 for _ in open(args.out))
|
||||
print("merged: %d bytes -> %s (%d 行)" % (len(merged), args.out, nlines))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user