tooling: sync bench + debug console to live STM32 protocol

The mppt-testbench Python tooling had drifted from the flashed firmware
(bundled fw e7a23a3 vs live 1b85532) and could no longer communicate:

- stm32_link.py: CRC8 -> CRC-16/CCITT-FALSE; telemetry 68B -> 78B
  (btemp, cmp_outer/inner, iout_slow, vfly_ofs_applied); add PTYPE_INT16
  and commands 0x12-0x18; replace PARAMS with the current 37-param map
  (single dt_normal, no dt brackets; test_corr/phase_ofs, phase PI,
  precharge PI, duty dither; vfly_active 0-3).
- tuner.py: retire per-bracket deadtime; sweep the single dt_normal.
- cli.py: update tune-deadtime, help/examples, btemp readout;
  default ports COM11 (load) / COM4 (stm32).
- debug console TUI: sync protocol.py/app.py/status_bar/telemetry_panel
  from live (new command keys, link RX/TX/loss stats, single dead-time,
  new telemetry fields, param-write auto-retry); add duty_fft.py.
- README: rewrite parameter table, deadtime section, ports, keybindings.

Verified: protocol round-trip self-tests + live `bench stm32-read`
reading all 37 params over COM4.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
janik
2026-07-01 12:03:51 +07:00
co-authored by Claude Opus 4.8
parent 903fa78585
commit d2dfc73f9e
9 changed files with 656 additions and 266 deletions
+18 -14
View File
@@ -584,7 +584,8 @@ def cmd_stm32_read(bench: MPPTTestbench, args: argparse.Namespace) -> None:
print(f" Pout = {t.power_out_W:8.2f} W")
print(f" EFF = {t.efficiency:8.1f} %")
print(f" Vfly = {t.vfly/1000:8.2f} V")
print(f" Temp = {t.etemp:8.1f} °C")
print(f" Temp = {t.etemp:8.1f} °C (FET)")
print(f" Tbrd = {t.btemp:8.1f} °C (board)")
def cmd_stm32_write(bench: MPPTTestbench, args: argparse.Namespace) -> None:
@@ -714,7 +715,7 @@ def cmd_tune_param_vsweep(bench: MPPTTestbench, args: argparse.Namespace) -> Non
def cmd_tune_deadtime(bench: MPPTTestbench, args: argparse.Namespace) -> None:
"""Optimize deadtime for each current bracket."""
"""Optimize the single global dead-time (dt_normal)."""
from testbench.stm32_link import STM32Link
from testbench.tuner import Tuner
@@ -740,8 +741,11 @@ def cmd_tune_deadtime(bench: MPPTTestbench, args: argparse.Namespace) -> None:
settle_time=args.settle,
)
if args.output:
tuner.write_csv(results, args.output)
if args.apply:
tuner.apply_best_deadtimes(results)
tuner.apply_best_deadtime(results)
if args.output:
all_pts = []
@@ -1123,9 +1127,9 @@ examples:
%(prog)s safe-off
%(prog)s plot-sweep sweep_vi_20260312_151212.csv
%(prog)s plot-sweep sweep_vi_20260312_151212.csv --no-show -o plots/
%(prog)s stm32-read --stm32-port COM28
%(prog)s tune-param --stm32-port COM28 --param dt_10_20A --start 14 --stop 40 --step 1 --voltage 60 --current-limit 20 --load-mode CP --load-value 300
%(prog)s tune-deadtime --stm32-port COM28 --voltage 60 --current-limit 20 --load-mode CP --apply
%(prog)s stm32-read --stm32-port COM4
%(prog)s tune-param --stm32-port COM4 --param dt_normal --start 14 --stop 40 --step 1 --voltage 60 --current-limit 20 --load-mode CP --load-value 300
%(prog)s tune-deadtime --stm32-port COM4 --voltage 60 --current-limit 20 --load-mode CP --apply
""",
)
@@ -1135,8 +1139,8 @@ examples:
help="IT6500D VISA address (auto-detect if omitted)",
)
parser.add_argument(
"--load-port", default="COM1",
help="Prodigit 3366G serial port (default: COM1)",
"--load-port", default="COM11",
help="Prodigit 3366G serial port (default: COM11)",
)
parser.add_argument(
"--load-baud", type=int, default=115200,
@@ -1151,8 +1155,8 @@ examples:
help="VISA timeout in ms (default: 5000)",
)
parser.add_argument(
"--stm32-port", default="COM28",
help="STM32 debug serial port (default: COM28)",
"--stm32-port", default="COM4",
help="STM32 debug serial port (default: COM4)",
)
parser.add_argument(
"--stm32-baud", type=int, default=460800,
@@ -1263,7 +1267,7 @@ examples:
# tune-param
p_tp = sub.add_parser("tune-param", help="Sweep an STM32 parameter while measuring efficiency")
p_tp.add_argument("--param", required=True, help="Parameter name (e.g. dt_10_20A, vfly_kp)")
p_tp.add_argument("--param", required=True, help="Parameter name (e.g. dt_normal, vfly_kp)")
p_tp.add_argument("--start", type=float, required=True, help="Start value")
p_tp.add_argument("--stop", type=float, required=True, help="Stop value")
p_tp.add_argument("--step", type=float, required=True, help="Step size")
@@ -1277,7 +1281,7 @@ examples:
# tune-param-vsweep
p_tv = sub.add_parser("tune-param-vsweep", help="Sweep an STM32 parameter across a voltage range")
p_tv.add_argument("--param", required=True, help="Parameter name (e.g. dt_10_20A, vfly_kp)")
p_tv.add_argument("--param", required=True, help="Parameter name (e.g. dt_normal, vfly_kp)")
p_tv.add_argument("--start", type=float, required=True, help="Param start value")
p_tv.add_argument("--stop", type=float, required=True, help="Param stop value")
p_tv.add_argument("--step", type=float, required=True, help="Param step size")
@@ -1291,14 +1295,14 @@ examples:
p_tv.add_argument("-o", "--output", help="Output prefix for CSVs (default: param name)")
# tune-deadtime
p_td = sub.add_parser("tune-deadtime", help="Optimize deadtime for each current bracket")
p_td = sub.add_parser("tune-deadtime", help="Optimize the single global dead-time (dt_normal)")
p_td.add_argument("--dt-start", type=int, default=14, help="Min deadtime ticks (default: 14)")
p_td.add_argument("--dt-stop", type=int, default=50, help="Max deadtime ticks (default: 50)")
p_td.add_argument("--dt-step", type=int, default=1, help="Deadtime step (default: 1)")
p_td.add_argument("--voltage", type=float, default=60.0, help="Supply voltage (V)")
p_td.add_argument("--current-limit", type=float, default=20.0, help="Supply current limit (A)")
p_td.add_argument("--load-mode", choices=["CC", "CP"], default="CP", help="Load mode")
p_td.add_argument("--load-values", help="Comma-separated load values per bracket (e.g. 20,50,100,250,400,600)")
p_td.add_argument("--load-values", help="Comma-separated load values to test (e.g. 100,300,500)")
p_td.add_argument("--settle", type=float, default=3.0, help="Settle time per step (s)")
p_td.add_argument("--apply", action="store_true", help="Apply best deadtimes after sweep")
p_td.add_argument("-o", "--output", help="CSV output file")