Add RTT sys reset command and remote reboot script with CPU resume.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
f6ae34892f
commit
956eeee1a5
|
|
@ -129,6 +129,15 @@ static void app_log_print_status(void)
|
||||||
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "\n");
|
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void app_log_sys_reset(void)
|
||||||
|
{
|
||||||
|
LOG_I("SYS", "reset via RTT");
|
||||||
|
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "SYS_RESET_OK\n");
|
||||||
|
rt_thread_mdelay(20);
|
||||||
|
__disable_irq();
|
||||||
|
nvic_system_reset();
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t app_log_try_command(const char *cmd)
|
uint8_t app_log_try_command(const char *cmd)
|
||||||
{
|
{
|
||||||
if (cmd == NULL || cmd[0] == '\0') {
|
if (cmd == NULL || cmd[0] == '\0') {
|
||||||
|
|
@ -152,6 +161,10 @@ 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, "sys reset", 9) == 0) {
|
||||||
|
app_log_sys_reset();
|
||||||
|
return 1U;
|
||||||
|
}
|
||||||
return 0U;
|
return 0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,6 +185,12 @@ static void app_log_feed_char(char c)
|
||||||
}
|
}
|
||||||
s_cmd_buf[s_cmd_len++] = c;
|
s_cmd_buf[s_cmd_len++] = c;
|
||||||
s_cmd_buf[s_cmd_len] = '\0';
|
s_cmd_buf[s_cmd_len] = '\0';
|
||||||
|
|
||||||
|
if (strstr(s_cmd_buf, "sys reset") != NULL) {
|
||||||
|
s_cmd_len = 0U;
|
||||||
|
s_cmd_buf[0] = '\0';
|
||||||
|
(void)app_log_try_command("sys reset");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void app_log_poll(void)
|
void app_log_poll(void)
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,10 @@
|
||||||
#define LCD_DUMP_RTT_DOWN_CH 0
|
#define LCD_DUMP_RTT_DOWN_CH 0
|
||||||
#define LCD_DUMP_CMD "DUMP"
|
#define LCD_DUMP_CMD "DUMP"
|
||||||
#define LCD_DUMP_TAP_CMD "TAP"
|
#define LCD_DUMP_TAP_CMD "TAP"
|
||||||
|
#define LCD_DUMP_RESET_CMD "sys reset"
|
||||||
#define LCD_DUMP_MAGIC "SCRN"
|
#define LCD_DUMP_MAGIC "SCRN"
|
||||||
#define LCD_DUMP_FMT_RGB565 1
|
#define LCD_DUMP_FMT_RGB565 1
|
||||||
#define LCD_DUMP_CMD_BUF_SIZE 16
|
#define LCD_DUMP_CMD_BUF_SIZE 32
|
||||||
|
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -226,6 +227,13 @@ static uint8_t LCD_Dump_CommandPending(void)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strstr(s_cmd_buf, LCD_DUMP_RESET_CMD) != NULL) {
|
||||||
|
s_cmd_len = 0U;
|
||||||
|
s_cmd_buf[0] = '\0';
|
||||||
|
(void)app_log_try_command(LCD_DUMP_RESET_CMD);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (c == 0x01 || strstr(s_cmd_buf, LCD_DUMP_CMD) != NULL) {
|
if (c == 0x01 || strstr(s_cmd_buf, LCD_DUMP_CMD) != NULL) {
|
||||||
s_cmd_len = 0U;
|
s_cmd_len = 0U;
|
||||||
s_cmd_buf[0] = '\0';
|
s_cmd_buf[0] = '\0';
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,15 @@ python rtt_log_dump.py --out problem.log
|
||||||
| `log clear` | 清空 RAM 日志 |
|
| `log clear` | 清空 RAM 日志 |
|
||||||
| `log status` | 查看 boot 次数、条数、溢出、当前 UI 页 |
|
| `log status` | 查看 boot 次数、条数、溢出、当前 UI 页 |
|
||||||
| `log level D` | 开启 Debug 级(含触摸细节);`I` 仅 Info 以上 |
|
| `log level D` | 开启 Debug 级(含触摸细节);`I` 仅 Info 以上 |
|
||||||
|
| `sys reset` | MCU 软件复位(自动重新开机,**RAM 日志会丢失**) |
|
||||||
|
|
||||||
|
或通过脚本(**复位后脚本会自动 `go()` 释放 CPU,避免 J-Link 停核黑屏**):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python rtt_reset.py
|
||||||
|
```
|
||||||
|
|
||||||
|
若固件较旧无 `sys reset`,脚本会自动用 J-Link 硬件复位兜底。
|
||||||
|
|
||||||
## 日志格式
|
## 日志格式
|
||||||
|
|
||||||
|
|
@ -44,6 +53,7 @@ python rtt_log_dump.py --out problem.log
|
||||||
|
|
||||||
- 屏幕截图:`python rtt_lcd_capture.py --out screen.png`
|
- 屏幕截图:`python rtt_lcd_capture.py --out screen.png`
|
||||||
- 日志导出:`python rtt_log_dump.py --out problem.log`
|
- 日志导出:`python rtt_log_dump.py --out problem.log`
|
||||||
|
- 远程复位:`python rtt_reset.py`
|
||||||
|
|
||||||
## 触摸问题排查要点
|
## 触摸问题排查要点
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,185 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Send sys reset via J-Link RTT to reboot K1 MCU (software NVIC reset)."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import time
|
||||||
|
|
||||||
|
DEFAULT_DEVICES = ("Cortex-M4", "AT32F403AC", "AT32F403A")
|
||||||
|
RESET_CMD = b"sys reset\n"
|
||||||
|
ACK = b"SYS_RESET_OK"
|
||||||
|
|
||||||
|
|
||||||
|
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 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:
|
||||||
|
resume_cpu(jlink)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def resume_cpu(jlink) -> None:
|
||||||
|
"""J-Link often halts the core on reset; must run() or UI stays black."""
|
||||||
|
try:
|
||||||
|
jlink.rtt_stop()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
jlink.reset(halt=False)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
if hasattr(jlink, "restart"):
|
||||||
|
jlink.restart()
|
||||||
|
else:
|
||||||
|
jlink.go()
|
||||||
|
except Exception:
|
||||||
|
jlink.go()
|
||||||
|
time.sleep(0.05)
|
||||||
|
|
||||||
|
|
||||||
|
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_reset(device: str, timeout_s: float, wait_boot_s: float, hw_fallback: bool) -> None:
|
||||||
|
jlink = connect_jlink(device)
|
||||||
|
ack_seen = False
|
||||||
|
try:
|
||||||
|
if jlink.halted():
|
||||||
|
print("Target was halted, resuming before RTT...")
|
||||||
|
resume_cpu(jlink)
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
|
cb = find_rtt_control_block(jlink)
|
||||||
|
if cb is None:
|
||||||
|
raise SystemExit("SEGGER RTT control block not found in SRAM")
|
||||||
|
print(f"RTT CB @ 0x{cb:08X}")
|
||||||
|
|
||||||
|
resume_cpu(jlink)
|
||||||
|
time.sleep(0.15)
|
||||||
|
jlink.rtt_start(cb)
|
||||||
|
wait_rtt_ready(jlink)
|
||||||
|
|
||||||
|
wrote = 0
|
||||||
|
for _ in range(30):
|
||||||
|
wrote = jlink.rtt_write(0, list(RESET_CMD))
|
||||||
|
if wrote > 0:
|
||||||
|
break
|
||||||
|
time.sleep(0.2)
|
||||||
|
if wrote <= 0:
|
||||||
|
raise SystemExit("Failed to send sys reset on RTT down channel 0")
|
||||||
|
print(f"Sent sys reset ({wrote} bytes)")
|
||||||
|
|
||||||
|
deadline = time.time() + timeout_s
|
||||||
|
while time.time() < deadline:
|
||||||
|
chunk = jlink.rtt_read(0, 1024)
|
||||||
|
if chunk:
|
||||||
|
text = bytes(chunk)
|
||||||
|
if ACK in text:
|
||||||
|
ack_seen = True
|
||||||
|
print("SYS_RESET_OK (firmware handled reset)")
|
||||||
|
break
|
||||||
|
time.sleep(0.05)
|
||||||
|
|
||||||
|
if not ack_seen:
|
||||||
|
print("No SYS_RESET_OK from firmware.")
|
||||||
|
if hw_fallback:
|
||||||
|
print("Using J-Link hardware reset fallback...")
|
||||||
|
resume_cpu(jlink)
|
||||||
|
else:
|
||||||
|
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...")
|
||||||
|
resume_cpu(jlink)
|
||||||
|
|
||||||
|
if wait_boot_s > 0:
|
||||||
|
print(f"Waiting {wait_boot_s:.0f}s for auto boot...")
|
||||||
|
time.sleep(wait_boot_s)
|
||||||
|
resume_cpu(jlink)
|
||||||
|
print("Done. Device should be back on mode-select screen.")
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
resume_cpu(jlink)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
jlink.close()
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser(description="Reboot K1 via RTT sys reset command")
|
||||||
|
parser.add_argument("--device", default="AT32F403AC", help="J-Link device name")
|
||||||
|
parser.add_argument("--timeout", type=float, default=3.0, help="Seconds to wait for ACK")
|
||||||
|
parser.add_argument(
|
||||||
|
"--wait-boot",
|
||||||
|
type=float,
|
||||||
|
default=4.0,
|
||||||
|
help="Seconds to wait after reset for auto power-on (0 to skip)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-hw-fallback",
|
||||||
|
action="store_true",
|
||||||
|
help="Do not use J-Link hardware reset when firmware ACK is missing",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
import_deps()
|
||||||
|
send_reset(args.device, args.timeout, args.wait_boot, not args.no_hw_fallback)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Reference in New Issue