feat(flash-gui): cfgs 自定义 cfg 机制 + 全操作 SRST 连接序列 + 整包 0.11 工具链
This commit is contained in:
@@ -43,15 +43,54 @@ def _app_base():
|
||||
return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
def _find_scripts(root):
|
||||
"""在工具链根目录下找 scripts 目录, 兼容两种布局:
|
||||
xpack 0.11(脚本在根: scripts/) 与 0.12(脚本在 openocd/scripts/)"""
|
||||
for sub in ("openocd/scripts", "scripts"):
|
||||
p = os.path.join(root, sub)
|
||||
if os.path.isdir(p):
|
||||
return p
|
||||
return None
|
||||
|
||||
|
||||
def bundled_toolchain():
|
||||
"""内置工具链路径, 不存在返回 None"""
|
||||
exe = os.path.join(_app_base(), "toolchain", "openocd", "bin", "openocd.exe")
|
||||
root = os.path.join(_app_base(), "toolchain", "openocd")
|
||||
exe = os.path.join(root, "bin", "openocd.exe")
|
||||
if os.path.isfile(exe):
|
||||
scripts = os.path.join(_app_base(), "toolchain", "openocd", "openocd", "scripts")
|
||||
return exe, scripts if os.path.isdir(scripts) else DEFAULT_SCRIPTS
|
||||
scripts = _find_scripts(root)
|
||||
return exe, (scripts or DEFAULT_SCRIPTS)
|
||||
return None
|
||||
|
||||
|
||||
def custom_cfg_dir():
|
||||
"""用户自定义 cfg 目录: 程序目录旁 cfgs/(exe 模式在 exe 旁, 源码模式在 FlashTool/)"""
|
||||
return os.path.join(_app_base(), "cfgs")
|
||||
|
||||
|
||||
def list_custom_cfgs():
|
||||
"""cfgs/ 下的 *.cfg 文件名列表(供界面下拉选择), 目录不存在返回空表"""
|
||||
d = custom_cfg_dir()
|
||||
if not os.path.isdir(d):
|
||||
return []
|
||||
return sorted(f for f in os.listdir(d)
|
||||
if f.lower().endswith(".cfg") and os.path.isfile(os.path.join(d, f)))
|
||||
|
||||
|
||||
def scan_cfgs(scripts_dir, kind):
|
||||
"""扫描 scripts 目录下 kind(target/interface) 子目录里的 cfg 文件名。
|
||||
|
||||
返回 ['target/gd32e23x.cfg', ...]; scripts 无效/子目录不存在返回空表。
|
||||
"""
|
||||
if not scripts_dir or not os.path.isdir(scripts_dir):
|
||||
return []
|
||||
d = os.path.join(scripts_dir, kind)
|
||||
if not os.path.isdir(d):
|
||||
return []
|
||||
return sorted(kind + "/" + f for f in os.listdir(d)
|
||||
if f.lower().endswith(".cfg") and os.path.isfile(os.path.join(d, f)))
|
||||
|
||||
|
||||
def locate_toolchain():
|
||||
"""返回 (openocd_exe, scripts_dir, source),source ∈ 内置/本机/PATH"""
|
||||
bundled = bundled_toolchain()
|
||||
@@ -83,6 +122,20 @@ def ensure_cfg_path(value, folder):
|
||||
return folder + "/" + value
|
||||
|
||||
|
||||
def resolve_cfg(value, folder):
|
||||
"""界面里的 cfg 引用 → OpenOCD -f 参数。
|
||||
|
||||
cfgs/<name>.cfg 是自定义 cfg, 转成绝对路径(不依赖 cwd);
|
||||
其余(裸名/带路径/绝对路径)按原逻辑处理。
|
||||
"""
|
||||
value = value.strip().replace("\\", "/")
|
||||
if value.startswith("cfgs/"):
|
||||
p = os.path.join(custom_cfg_dir(), os.path.basename(value))
|
||||
if os.path.isfile(p):
|
||||
return p
|
||||
return ensure_cfg_path(value, folder)
|
||||
|
||||
|
||||
def version(exe):
|
||||
"""openocd --version 首行(如 Open On-Chip Debugger 0.12.0), 失败返回 None"""
|
||||
try:
|
||||
@@ -99,12 +152,14 @@ def version(exe):
|
||||
# 烧录脚本构建(GUI 与 CLI 共用)
|
||||
# ============================================================
|
||||
def build_cmd(openocd, scripts, interface, target, speed="", serial="", extra="", script=""):
|
||||
"""拼 OpenOCD 命令行: -s 搜索目录 + interface/target cfg + -c 选项"""
|
||||
"""拼 OpenOCD 命令行: -s 搜索目录 + interface/target cfg + -c 选项。
|
||||
interface 留空时跳过(配合自带 adapter driver 的自定义 target cfg 使用)"""
|
||||
cmd = [openocd.strip()]
|
||||
if scripts and scripts.strip():
|
||||
cmd += ["-s", scripts.strip()]
|
||||
cmd += ["-f", ensure_cfg_path(interface, "interface"),
|
||||
"-f", ensure_cfg_path(target, "target")]
|
||||
if interface and interface.strip():
|
||||
cmd += ["-f", resolve_cfg(interface, "interface")]
|
||||
cmd += ["-f", resolve_cfg(target, "target")]
|
||||
if speed and speed.strip():
|
||||
cmd += ["-c", "adapter speed " + speed.strip()]
|
||||
if serial and serial.strip() and "cmsis-dap" in interface.lower():
|
||||
@@ -117,21 +172,38 @@ def build_cmd(openocd, scripts, interface, target, speed="", serial="", extra=""
|
||||
|
||||
|
||||
def script_detect():
|
||||
return "init; reset halt; flash list; exit"
|
||||
return "init; %s; flash list; exit" % CONNECT_SEQ
|
||||
|
||||
|
||||
def script_erase(flash_size_text):
|
||||
size = parse_int(flash_size_text)
|
||||
if size is None or size <= 0:
|
||||
raise ValueError("Flash 大小无效: %s" % flash_size_text)
|
||||
return ("init; reset halt; flash erase_address 0x%08X 0x%X; reset halt; exit"
|
||||
% (FLASH_BASE_DEFAULT, size))
|
||||
return ("init; %s; flash erase_address 0x%08X 0x%X; reset halt; exit"
|
||||
% (CONNECT_SEQ, FLASH_BASE_DEFAULT, size))
|
||||
|
||||
|
||||
def script_flash(firmware, base_text="", erase=True, verify=True, reset=True):
|
||||
"""HEX/ELF 用文件内地址(base_text 留空), BIN 必须给基地址"""
|
||||
# 复刻 VSCode tasks.json 的复位序列(0.11 语法, 与内置 xpack 0.11.0-3 匹配):
|
||||
# init 连接(connect_assert_srst 由 cfg 声明)后显式释放 SRST 并等复位完成再 halt。
|
||||
# 注意: 0.11 只认 "adapter deassert srst"; 0.12 的 "adapter srst deassert"
|
||||
# 在 0.11 上是无效命令, 包 catch 会静默吞掉 → SRST 一直按住 → cannot read IDR。
|
||||
# 全部操作(检测/烧录/擦除/读取/复位)都必须带本序列: 本目标必须按住复位连接,
|
||||
# 否则 exam 失败(PARTNO 0x0), 后续任何访问都会 "Target not examined" 失败。
|
||||
CONNECT_SEQ = "adapter deassert srst; sleep 200; catch {reset halt}; reset halt"
|
||||
POST_RESET_PREFIX = "adapter deassert srst; sleep 200; "
|
||||
|
||||
|
||||
def script_flash(firmware, base_text="", erase=True, verify=True, reset=True,
|
||||
pre_reset=True, post_reset=True):
|
||||
"""HEX/ELF 用文件内地址(base_text 留空), BIN 必须给基地址。
|
||||
|
||||
pre_reset - 烧录前硬件复位序列(SRST 释放 + halt), 复刻 VSCode 任务链
|
||||
post_reset - 烧录后复位前先释放 SRST(硬件复位), 否则仅软件 reset
|
||||
reset - 复位后运行(reset run); 不勾则复位后保持暂停(reset halt)
|
||||
"""
|
||||
ext = os.path.splitext(firmware)[1].lower()
|
||||
script = "init; reset halt; "
|
||||
script = "init; "
|
||||
script += (CONNECT_SEQ if pre_reset else "reset halt") + "; "
|
||||
addr = ""
|
||||
if ext == ".bin":
|
||||
base = parse_int(base_text)
|
||||
@@ -143,7 +215,8 @@ def script_flash(firmware, base_text="", erase=True, verify=True, reset=True):
|
||||
else "flash write_image %s%s; ") % (quoted, addr)
|
||||
if verify:
|
||||
script += "verify_image %s%s; " % (quoted, addr)
|
||||
script += ("reset run" if reset else "reset halt") + "; exit"
|
||||
tail = "reset run" if reset else "reset halt"
|
||||
script += (POST_RESET_PREFIX + tail if post_reset else tail) + "; exit"
|
||||
return script
|
||||
|
||||
|
||||
@@ -151,12 +224,13 @@ def script_read(out_path, flash_size_text):
|
||||
size = parse_int(flash_size_text)
|
||||
if size is None or size <= 0:
|
||||
raise ValueError("Flash 大小无效: %s" % flash_size_text)
|
||||
return "init; reset halt; dump_image %s 0x%08X 0x%X; exit" % (
|
||||
tcl_quote(out_path), FLASH_BASE_DEFAULT, size)
|
||||
return "init; %s; dump_image %s 0x%08X 0x%X; exit" % (
|
||||
CONNECT_SEQ, tcl_quote(out_path), FLASH_BASE_DEFAULT, size)
|
||||
|
||||
|
||||
def script_reset():
|
||||
return "init; reset run; exit"
|
||||
"""对齐 VSCode Reset 任务: 释放 SRST 后复位运行"""
|
||||
return "init; adapter deassert srst; sleep 200; reset; exit"
|
||||
|
||||
|
||||
# ============================================================
|
||||
|
||||
Reference in New Issue
Block a user