Add RTT touch diagnostics: tp status/live commands and live event monitor.
Harden reset/recover when RTT is missing, and stream touch/UI events only on press for field debugging. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
33f48d9171
commit
42534a28b8
|
|
@ -161,6 +161,18 @@ uint8_t app_log_try_command(const char *cmd)
|
||||||
LOG_I("LOG", "level set %c", s_debug_level);
|
LOG_I("LOG", "level set %c", s_debug_level);
|
||||||
return 1U;
|
return 1U;
|
||||||
}
|
}
|
||||||
|
if (strncmp(cmd, "tp status", 9) == 0) {
|
||||||
|
TP_LogStatus();
|
||||||
|
return 1U;
|
||||||
|
}
|
||||||
|
if (strncmp(cmd, "tp live off", 11) == 0) {
|
||||||
|
TP_SetLiveMode(0);
|
||||||
|
return 1U;
|
||||||
|
}
|
||||||
|
if (strncmp(cmd, "tp live", 7) == 0) {
|
||||||
|
TP_SetLiveMode(1);
|
||||||
|
return 1U;
|
||||||
|
}
|
||||||
if (strncmp(cmd, "sys reset", 9) == 0) {
|
if (strncmp(cmd, "sys reset", 9) == 0) {
|
||||||
app_log_sys_reset();
|
app_log_sys_reset();
|
||||||
return 1U;
|
return 1U;
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,9 @@ void app_log_set_ui_page(const char *name);
|
||||||
#define LOG_TP(fmt, ...) ((void)0)
|
#define LOG_TP(fmt, ...) ((void)0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* 触摸按下:始终 INFO,便于 log dump 排查硬件/坐标 */
|
||||||
|
#define LOG_TPI(fmt, ...) LOG_I("TP", fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#define LOG_I(cat, fmt, ...) ((void)0)
|
#define LOG_I(cat, fmt, ...) ((void)0)
|
||||||
|
|
@ -60,6 +63,7 @@ void app_log_set_ui_page(const char *name);
|
||||||
#define LOG_E(cat, fmt, ...) ((void)0)
|
#define LOG_E(cat, fmt, ...) ((void)0)
|
||||||
#define LOG_D(cat, fmt, ...) ((void)0)
|
#define LOG_D(cat, fmt, ...) ((void)0)
|
||||||
#define LOG_TP(fmt, ...) ((void)0)
|
#define LOG_TP(fmt, ...) ((void)0)
|
||||||
|
#define LOG_TPI(fmt, ...) ((void)0)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,7 @@ int main(void)
|
||||||
LCD_Dump_Poll();
|
LCD_Dump_Poll();
|
||||||
#if !DEBUG_LCD_DUMP
|
#if !DEBUG_LCD_DUMP
|
||||||
app_log_poll();
|
app_log_poll();
|
||||||
|
TP_LivePoll();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
rt_thread_delay(1);
|
rt_thread_delay(1);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ from datetime import datetime
|
||||||
|
|
||||||
DEFAULT_DEVICES = ("Cortex-M4", "AT32F403AC", "AT32F403A")
|
DEFAULT_DEVICES = ("Cortex-M4", "AT32F403AC", "AT32F403A")
|
||||||
DUMP_CMD = b"log dump\n"
|
DUMP_CMD = b"log dump\n"
|
||||||
|
PROBE_CMD = b"tp status\n"
|
||||||
END_MARKERS = (b"LOG_DUMP_END",)
|
END_MARKERS = (b"LOG_DUMP_END",)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -113,6 +114,8 @@ def dump_log(out_path: str, device: str, timeout_s: float) -> None:
|
||||||
print("RTT0 stale:", text.strip()[:200])
|
print("RTT0 stale:", text.strip()[:200])
|
||||||
time.sleep(0.05)
|
time.sleep(0.05)
|
||||||
|
|
||||||
|
send_command(jlink, PROBE_CMD)
|
||||||
|
time.sleep(0.25)
|
||||||
send_command(jlink, DUMP_CMD)
|
send_command(jlink, DUMP_CMD)
|
||||||
|
|
||||||
buffer = b""
|
buffer = b""
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Recover K1 from J-Link halt / black screen: hardware reset + run (no RTT required)."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import time
|
||||||
|
|
||||||
|
DEFAULT_DEVICES = ("Cortex-M4", "AT32F403AC", "AT32F403A")
|
||||||
|
|
||||||
|
|
||||||
|
def import_deps():
|
||||||
|
try:
|
||||||
|
import pylink # noqa: F401
|
||||||
|
except ImportError as exc:
|
||||||
|
raise SystemExit("Missing dependency: pip install pylink-square") from exc
|
||||||
|
|
||||||
|
|
||||||
|
def connect_jlink(device: str):
|
||||||
|
import pylink
|
||||||
|
|
||||||
|
jlink = pylink.JLink()
|
||||||
|
jlink.open()
|
||||||
|
try:
|
||||||
|
jlink.exec_command("HideDeviceSelection = 1")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
jlink.set_tif(pylink.enums.JLinkInterfaces.SWD)
|
||||||
|
|
||||||
|
last_error = None
|
||||||
|
for candidate in (device, *DEFAULT_DEVICES):
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
jlink.exec_command(f"Device = {candidate}")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
jlink.connect(candidate)
|
||||||
|
print(f"Connected as {candidate}")
|
||||||
|
return jlink
|
||||||
|
except pylink.errors.JLinkException as exc:
|
||||||
|
last_error = exc
|
||||||
|
jlink.close()
|
||||||
|
raise SystemExit(f"Failed to connect J-Link: {last_error}")
|
||||||
|
|
||||||
|
|
||||||
|
def run_go(jlink) -> None:
|
||||||
|
try:
|
||||||
|
jlink.reset(halt=False)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
if hasattr(jlink, "restart"):
|
||||||
|
jlink.restart()
|
||||||
|
else:
|
||||||
|
jlink.go()
|
||||||
|
except Exception:
|
||||||
|
jlink.go()
|
||||||
|
|
||||||
|
|
||||||
|
def recover(device: str, wait_boot_s: float) -> None:
|
||||||
|
jlink = connect_jlink(device)
|
||||||
|
try:
|
||||||
|
halted = jlink.halted()
|
||||||
|
print(f"Target halted={halted}")
|
||||||
|
print("Reset + run...")
|
||||||
|
run_go(jlink)
|
||||||
|
if wait_boot_s > 0:
|
||||||
|
print(f"Waiting {wait_boot_s:.0f}s for auto boot...")
|
||||||
|
time.sleep(wait_boot_s)
|
||||||
|
run_go(jlink)
|
||||||
|
print("Done. Screen should show UI; if still black, run flash_app.bat")
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
run_go(jlink)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
jlink.close()
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser(description="Recover K1 from halt/black screen via J-Link")
|
||||||
|
parser.add_argument("--device", default="AT32F403AC")
|
||||||
|
parser.add_argument("--wait-boot", type=float, default=4.0)
|
||||||
|
args = parser.parse_args()
|
||||||
|
import_deps()
|
||||||
|
recover(args.device, args.wait_boot)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -49,8 +49,10 @@ def find_rtt_control_block(jlink, ram_base: int = 0x20000000, ram_size: int = 0x
|
||||||
needle = b"SEGGER RTT"
|
needle = b"SEGGER RTT"
|
||||||
chunk = 0x1000
|
chunk = 0x1000
|
||||||
was_halted = jlink.halted()
|
was_halted = jlink.halted()
|
||||||
|
did_halt = False
|
||||||
if not was_halted:
|
if not was_halted:
|
||||||
jlink.halt()
|
jlink.halt()
|
||||||
|
did_halt = True
|
||||||
try:
|
try:
|
||||||
for off in range(0, ram_size, chunk):
|
for off in range(0, ram_size, chunk):
|
||||||
data = bytes(jlink.memory_read8(ram_base + off, min(chunk, ram_size - off)))
|
data = bytes(jlink.memory_read8(ram_base + off, min(chunk, ram_size - off)))
|
||||||
|
|
@ -58,11 +60,21 @@ def find_rtt_control_block(jlink, ram_base: int = 0x20000000, ram_size: int = 0x
|
||||||
if idx >= 0:
|
if idx >= 0:
|
||||||
return ram_base + off + idx
|
return ram_base + off + idx
|
||||||
finally:
|
finally:
|
||||||
if not was_halted:
|
if did_halt:
|
||||||
resume_cpu(jlink)
|
resume_cpu(jlink)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def hw_reset_and_run(jlink, wait_s: float = 4.0) -> None:
|
||||||
|
"""J-Link 硬件复位并释放 CPU(RTT 不可用时的唯一可靠恢复手段)。"""
|
||||||
|
print("Hardware reset + run...")
|
||||||
|
resume_cpu(jlink)
|
||||||
|
if wait_s > 0:
|
||||||
|
print(f"Waiting {wait_s:.0f}s for boot...")
|
||||||
|
time.sleep(wait_s)
|
||||||
|
resume_cpu(jlink)
|
||||||
|
|
||||||
|
|
||||||
def resume_cpu(jlink) -> None:
|
def resume_cpu(jlink) -> None:
|
||||||
"""J-Link often halts the core on reset; must run() or UI stays black."""
|
"""J-Link often halts the core on reset; must run() or UI stays black."""
|
||||||
try:
|
try:
|
||||||
|
|
@ -106,7 +118,16 @@ def send_reset(device: str, timeout_s: float, wait_boot_s: float, hw_fallback: b
|
||||||
|
|
||||||
cb = find_rtt_control_block(jlink)
|
cb = find_rtt_control_block(jlink)
|
||||||
if cb is None:
|
if cb is None:
|
||||||
raise SystemExit("SEGGER RTT control block not found in SRAM")
|
print("SEGGER RTT control block not found in SRAM.")
|
||||||
|
if hw_fallback:
|
||||||
|
hw_reset_and_run(jlink, wait_boot_s)
|
||||||
|
print("Done (hardware recovery). If still black, run: flash_app.bat")
|
||||||
|
else:
|
||||||
|
raise SystemExit(
|
||||||
|
"RTT not available. Re-run without --no-hw-fallback, or flash_app.bat"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
print(f"RTT CB @ 0x{cb:08X}")
|
print(f"RTT CB @ 0x{cb:08X}")
|
||||||
|
|
||||||
resume_cpu(jlink)
|
resume_cpu(jlink)
|
||||||
|
|
@ -138,12 +159,10 @@ def send_reset(device: str, timeout_s: float, wait_boot_s: float, hw_fallback: b
|
||||||
if not ack_seen:
|
if not ack_seen:
|
||||||
print("No SYS_RESET_OK from firmware.")
|
print("No SYS_RESET_OK from firmware.")
|
||||||
if hw_fallback:
|
if hw_fallback:
|
||||||
print("Using J-Link hardware reset fallback...")
|
hw_reset_and_run(jlink, 0.0)
|
||||||
resume_cpu(jlink)
|
|
||||||
else:
|
else:
|
||||||
print("Hint: re-run with --hw-fallback or flash latest firmware.")
|
print("Hint: re-run with --hw-fallback or flash latest firmware.")
|
||||||
|
|
||||||
# Critical: release CPU after reset — otherwise screen stays black.
|
|
||||||
print("Resuming CPU after reset...")
|
print("Resuming CPU after reset...")
|
||||||
resume_cpu(jlink)
|
resume_cpu(jlink)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,207 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Live-stream K1 touch logs via J-Link RTT while you touch the screen.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python rtt_touch_live.py
|
||||||
|
python rtt_touch_live.py --all # show every RTT line
|
||||||
|
python rtt_touch_live.py --seconds 120 # auto-stop after N seconds
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
DEFAULT_DEVICES = ("Cortex-M4", "AT32F403AC", "AT32F403A")
|
||||||
|
LIVE_ON_CMD = b"tp live\n"
|
||||||
|
LIVE_OFF_CMD = b"tp live off\n"
|
||||||
|
|
||||||
|
# Default: touch / UI hit / message path
|
||||||
|
DEFAULT_KEYS = ("[TP", "[UI", "[MSG")
|
||||||
|
|
||||||
|
|
||||||
|
def import_deps():
|
||||||
|
try:
|
||||||
|
import pylink # noqa: F401
|
||||||
|
except ImportError as exc:
|
||||||
|
raise SystemExit("Missing dependency: pip install pylink-square") from exc
|
||||||
|
|
||||||
|
|
||||||
|
def connect_jlink(device: str):
|
||||||
|
import pylink
|
||||||
|
|
||||||
|
jlink = pylink.JLink()
|
||||||
|
jlink.open()
|
||||||
|
try:
|
||||||
|
jlink.exec_command("HideDeviceSelection = 1")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
jlink.set_tif(pylink.enums.JLinkInterfaces.SWD)
|
||||||
|
|
||||||
|
last_error = None
|
||||||
|
for candidate in (device, *DEFAULT_DEVICES):
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
jlink.exec_command(f"Device = {candidate}")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
jlink.connect(candidate)
|
||||||
|
print(f"Connected as {candidate}", flush=True)
|
||||||
|
return jlink
|
||||||
|
except pylink.errors.JLinkException as exc:
|
||||||
|
last_error = exc
|
||||||
|
jlink.close()
|
||||||
|
raise SystemExit(f"Failed to connect J-Link: {last_error}")
|
||||||
|
|
||||||
|
|
||||||
|
def find_rtt_control_block(jlink, ram_base: int = 0x20000000, ram_size: int = 0x18000) -> int | None:
|
||||||
|
needle = b"SEGGER RTT"
|
||||||
|
chunk = 0x1000
|
||||||
|
was_halted = jlink.halted()
|
||||||
|
if not was_halted:
|
||||||
|
jlink.halt()
|
||||||
|
try:
|
||||||
|
for off in range(0, ram_size, chunk):
|
||||||
|
data = bytes(jlink.memory_read8(ram_base + off, min(chunk, ram_size - off)))
|
||||||
|
idx = data.find(needle)
|
||||||
|
if idx >= 0:
|
||||||
|
return ram_base + off + idx
|
||||||
|
finally:
|
||||||
|
if not was_halted:
|
||||||
|
if hasattr(jlink, "restart"):
|
||||||
|
jlink.restart()
|
||||||
|
else:
|
||||||
|
jlink.go()
|
||||||
|
time.sleep(0.1)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def wait_rtt_ready(jlink, timeout_s: float = 15.0) -> None:
|
||||||
|
deadline = time.time() + timeout_s
|
||||||
|
while time.time() < deadline:
|
||||||
|
try:
|
||||||
|
if jlink.rtt_get_num_up_buffers() > 0:
|
||||||
|
return
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
time.sleep(0.2)
|
||||||
|
raise SystemExit("RTT control block found but buffers not ready")
|
||||||
|
|
||||||
|
|
||||||
|
def send_command(jlink, payload: bytes, retries: int = 40) -> None:
|
||||||
|
for _ in range(retries):
|
||||||
|
wrote = jlink.rtt_write(0, list(payload))
|
||||||
|
if wrote > 0:
|
||||||
|
print(f">> {payload.decode('ascii', errors='replace').strip()}", flush=True)
|
||||||
|
return
|
||||||
|
time.sleep(0.15)
|
||||||
|
print("WARN: failed to send RTT command", flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
def want_line(line: str, show_all: bool, keys: tuple[str, ...]) -> bool:
|
||||||
|
if show_all:
|
||||||
|
return True
|
||||||
|
return any(k in line for k in keys)
|
||||||
|
|
||||||
|
|
||||||
|
def run_live(device: str, show_all: bool, seconds: float, out_path: str | None) -> None:
|
||||||
|
jlink = connect_jlink(device)
|
||||||
|
out_fp = open(out_path, "w", encoding="utf-8", newline="\n") if out_path else None
|
||||||
|
try:
|
||||||
|
if jlink.halted():
|
||||||
|
if hasattr(jlink, "restart"):
|
||||||
|
jlink.restart()
|
||||||
|
else:
|
||||||
|
jlink.go()
|
||||||
|
time.sleep(0.3)
|
||||||
|
|
||||||
|
cb = find_rtt_control_block(jlink)
|
||||||
|
if cb is None:
|
||||||
|
raise SystemExit("SEGGER RTT control block not found — is app running?")
|
||||||
|
print(f"RTT CB @ 0x{cb:08X}", flush=True)
|
||||||
|
|
||||||
|
jlink.rtt_start(cb)
|
||||||
|
wait_rtt_ready(jlink)
|
||||||
|
|
||||||
|
# Drain backlog
|
||||||
|
for _ in range(8):
|
||||||
|
junk = jlink.rtt_read(0, 4096)
|
||||||
|
if not junk:
|
||||||
|
break
|
||||||
|
|
||||||
|
print("Waiting ~8s for UI/touch task...", flush=True)
|
||||||
|
time.sleep(8.0)
|
||||||
|
for _ in range(4):
|
||||||
|
junk = jlink.rtt_read(0, 4096)
|
||||||
|
if not junk:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Event-only: firmware no longer floods idle samples.
|
||||||
|
# Optional: tp live just tags the session; leave it off by default.
|
||||||
|
print("Event log ON — only touch events will print. Touch the screen. Ctrl+C to stop.", flush=True)
|
||||||
|
print("-" * 60, flush=True)
|
||||||
|
|
||||||
|
buf = b""
|
||||||
|
t0 = time.time()
|
||||||
|
while True:
|
||||||
|
if seconds > 0 and (time.time() - t0) >= seconds:
|
||||||
|
print("\n(time limit reached)", flush=True)
|
||||||
|
break
|
||||||
|
|
||||||
|
chunk = jlink.rtt_read(0, 4096)
|
||||||
|
if chunk:
|
||||||
|
buf += bytes(chunk)
|
||||||
|
while True:
|
||||||
|
nl = buf.find(b"\n")
|
||||||
|
if nl < 0:
|
||||||
|
break
|
||||||
|
raw = buf[:nl]
|
||||||
|
buf = buf[nl + 1 :]
|
||||||
|
line = raw.decode("utf-8", errors="replace").rstrip("\r")
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
if want_line(line, show_all, DEFAULT_KEYS):
|
||||||
|
print(line, flush=True)
|
||||||
|
if out_fp:
|
||||||
|
out_fp.write(line + "\n")
|
||||||
|
out_fp.flush()
|
||||||
|
else:
|
||||||
|
time.sleep(0.01)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nStopped by user.", flush=True)
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
jlink.rtt_stop()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
if hasattr(jlink, "restart"):
|
||||||
|
jlink.restart()
|
||||||
|
else:
|
||||||
|
jlink.go()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
jlink.close()
|
||||||
|
if out_fp:
|
||||||
|
out_fp.close()
|
||||||
|
print(f"Saved: {out_path}", flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser(description="Live K1 touch RTT monitor")
|
||||||
|
parser.add_argument("--device", default="AT32F403AC")
|
||||||
|
parser.add_argument("--all", action="store_true", help="Print all RTT lines")
|
||||||
|
parser.add_argument("--seconds", type=float, default=0, help="Auto stop after N seconds (0=forever)")
|
||||||
|
parser.add_argument(
|
||||||
|
"--out",
|
||||||
|
default="",
|
||||||
|
help="Also append filtered lines to this file",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
import_deps()
|
||||||
|
run_live(args.device, args.all, args.seconds, args.out or None)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Reference in New Issue