feat: collapsible flash sub-steps UI + erase_all checkbox

Rename erase_before_program → erase_all (semantics fix):
- erase_all: true  → -erase_all -erase_only (entire flash chip)
- erase_all: false → -erase_only (sectors matching BIN size)
Both options always erase; the checkbox controls scope, not yes/no.

UI additions:
- SubStepFrame widget: collapsible Erase/Program/Verify sub-steps
  with status dots (○/◉/●) and colored labels
- Automatically expands on Step 2 start, collapses on completion
- Phase and progress driven by real-time program_flash output parsing
- Checkbox label: '整片Flash擦除 (Erase entire flash)'

subprocess_utils.py phase detection:
- Parses 'Performing Erase/Program/Verify Operation' for phase transitions
- Emits 'phase' callbacks (erase/program/verify/done)
- Progress % tracked within current phase
This commit is contained in:
Jeremy Shen
2026-06-10 13:45:57 +08:00
parent ea3e3b8ac7
commit 95c04d94ae
6 changed files with 190 additions and 37 deletions
+16 -20
View File
@@ -98,11 +98,11 @@ def wipe_flash(
config: Config,
callback: ProgressCallback = None,
) -> FlashResult:
"""Erase QSPI flash sectors matching the bootloader image size.
"""Erase QSPI flash before programming.
Uses -erase_only to erase only the sectors covered by the BIN file.
Avoids -erase_all which fails on some flash chips (e.g. s25fl256s1)
that don't report memory density information.
Uses config.erase_all to determine erase scope:
- True: -erase_all -erase_only (erase entire chip, may fail on some)
- False: -erase_only (erase only sectors matching the BIN file, safer)
Args:
config: Application configuration (needs fsbl_elf_path).
@@ -131,7 +131,10 @@ def wipe_flash(
callback("start", "Erasing QSPI flash...")
cmd = _build_program_flash_cmd_base(flash_tool, config)
cmd += ["-erase_only"] # Erase sectors matching the BIN size (safer than -erase_all)
if getattr(config, 'erase_all', False):
cmd += ["-erase_all", "-erase_only"] # Full chip erase
else:
cmd += ["-erase_only"] # Erase sectors matching BIN size
try:
result = _run_command(cmd, timeout=config.step_timeouts.get("flash_erase", 120), callback=callback)
@@ -279,11 +282,11 @@ def full_flash_program(
bin_path: str | Path,
callback: ProgressCallback = None,
) -> list[FlashResult]:
"""Execute full flash workflow: (optional wipe) → program → verify.
"""Execute full flash workflow: wipe → program → verify.
If config.erase_before_program is True, erases flash sectors before
programming. Otherwise, program_flash handles sector erase during
programming (per-sector, not full chip).
Erase scope is controlled by config.erase_all:
- True: entire flash chip (uses -erase_all)
- False: sectors matching BIN file size (uses -erase_only)
Args:
config: Application configuration.
@@ -295,17 +298,10 @@ def full_flash_program(
"""
results: list[FlashResult] = []
# Step 1: Erase (optional — controlled by config.erase_before_program)
if getattr(config, 'erase_before_program', True):
results.append(wipe_flash(config, callback))
if not results[-1].success:
return results
else:
results.append(FlashResult(
step="wipe",
success=True,
message="Erase skipped (handled by program_flash)",
))
# Step 1: Erase (scope determined by config.erase_all)
results.append(wipe_flash(config, callback))
if not results[-1].success:
return results
# Step 2: Program and verify (program_flash -verify)
results.append(program_flash_bin(config, bin_path, callback))