Add J-Link RTT logging module with RAM ring buffer and dump tool.

Log boot/power/touch/key/UI/NVM events; export via rtt_log_dump.py for field diagnostics.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yuquanjun 2026-08-31 11:24:10 +08:00
parent b8eaf2c808
commit 7fea921727
19 changed files with 662 additions and 56 deletions

207
APP/app_log.c Normal file
View File

@ -0,0 +1,207 @@
#include "includes.h"
#include "SEGGER_RTT.h"
#include <stdarg.h>
#if APP_LOG_ENABLE
static char s_slots[APP_LOG_SLOT_COUNT][APP_LOG_SLOT_SIZE];
static uint16_t s_head;
static uint16_t s_count;
static uint32_t s_overflow;
static uint32_t s_boot_count;
static char s_ui_page[16] = "boot";
static char s_debug_level = 'D';
static char s_cmd_buf[48];
static uint8_t s_cmd_len;
static uint32_t app_log_ms(void)
{
return (uint32_t)(rt_tick_get() * 1000U / RT_TICK_PER_SECOND);
}
static void app_log_slot_store(const char *line)
{
uint16_t idx = s_head;
strncpy(s_slots[idx], line, APP_LOG_SLOT_SIZE - 1U);
s_slots[idx][APP_LOG_SLOT_SIZE - 1U] = '\0';
s_head = (uint16_t)((s_head + 1U) % APP_LOG_SLOT_COUNT);
if (s_count < APP_LOG_SLOT_COUNT) {
s_count++;
} else {
s_overflow++;
}
}
void app_log_set_ui_page(const char *name)
{
if (name == NULL) {
return;
}
strncpy(s_ui_page, name, sizeof(s_ui_page) - 1U);
s_ui_page[sizeof(s_ui_page) - 1U] = '\0';
}
void app_log_write(char level, const char *cat, const char *fmt, ...)
{
char line[APP_LOG_SLOT_SIZE];
va_list ap;
int n;
if (level == 'D' && s_debug_level != 'D') {
return;
}
n = snprintf(line, sizeof(line), "[%05u][%c][%-4s] ",
(unsigned)app_log_ms(), level, cat);
if (n < 0) {
return;
}
if ((size_t)n >= sizeof(line)) {
n = (int)sizeof(line) - 1;
}
va_start(ap, fmt);
vsnprintf(line + n, sizeof(line) - (size_t)n, fmt, ap);
va_end(ap);
app_log_slot_store(line);
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line);
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "\n");
}
static void app_log_dump_ram(void)
{
uint16_t start;
uint16_t i;
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "LOG_DUMP_BEGIN\n");
{
char meta[APP_LOG_SLOT_SIZE];
snprintf(meta, sizeof(meta),
"meta boot=%u lines=%u overflow=%u ui=%s",
(unsigned)s_boot_count,
(unsigned)s_count,
(unsigned)s_overflow,
s_ui_page);
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, meta);
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "\n");
}
if (s_count == 0U) {
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "LOG_DUMP_END\n");
return;
}
start = (s_count < APP_LOG_SLOT_COUNT)
? 0U
: s_head;
for (i = 0; i < s_count; i++) {
uint16_t idx = (uint16_t)((start + i) % APP_LOG_SLOT_COUNT);
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, s_slots[idx]);
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "\n");
}
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "LOG_DUMP_END\n");
}
static void app_log_clear_ram(void)
{
s_head = 0U;
s_count = 0U;
s_overflow = 0U;
memset(s_slots, 0, sizeof(s_slots));
LOG_I("LOG", "ram cleared");
}
static void app_log_print_status(void)
{
char line[APP_LOG_SLOT_SIZE];
snprintf(line, sizeof(line),
"LOG_STATUS boot=%u lines=%u overflow=%u level=%c ui=%s tick=%u",
(unsigned)s_boot_count,
(unsigned)s_count,
(unsigned)s_overflow,
s_debug_level,
s_ui_page,
(unsigned)app_log_ms());
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line);
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "\n");
}
uint8_t app_log_try_command(const char *cmd)
{
if (cmd == NULL || cmd[0] == '\0') {
return 0U;
}
if (strncmp(cmd, "log dump", 8) == 0) {
app_log_dump_ram();
return 1U;
}
if (strncmp(cmd, "log clear", 9) == 0) {
app_log_clear_ram();
return 1U;
}
if (strncmp(cmd, "log status", 10) == 0) {
app_log_print_status();
return 1U;
}
if (strncmp(cmd, "log level ", 10) == 0 && cmd[10] != '\0') {
s_debug_level = cmd[10];
LOG_I("LOG", "level set %c", s_debug_level);
return 1U;
}
return 0U;
}
static void app_log_feed_char(char c)
{
if (c == '\r' || c == '\n' || c == '\0') {
if (s_cmd_len > 0U) {
s_cmd_buf[s_cmd_len] = '\0';
(void)app_log_try_command(s_cmd_buf);
}
s_cmd_len = 0U;
s_cmd_buf[0] = '\0';
return;
}
if (s_cmd_len + 1U >= sizeof(s_cmd_buf)) {
s_cmd_len = 0U;
}
s_cmd_buf[s_cmd_len++] = c;
s_cmd_buf[s_cmd_len] = '\0';
}
void app_log_poll(void)
{
char rx[16];
unsigned read_len;
unsigned i;
read_len = SEGGER_RTT_Read(APP_LOG_RTT_CHANNEL, rx, sizeof(rx));
if (read_len == 0U) {
return;
}
for (i = 0; i < read_len; i++) {
app_log_feed_char(rx[i]);
}
}
void app_log_init(void)
{
s_boot_count++;
LOG_I("BOOT", "app_log init boot=%u", (unsigned)s_boot_count);
}
#else
void app_log_init(void) {}
void app_log_write(char level, const char *cat, const char *fmt, ...) { (void)level; (void)cat; (void)fmt; }
void app_log_poll(void) {}
uint8_t app_log_try_command(const char *cmd) { (void)cmd; return 0U; }
void app_log_set_ui_page(const char *name) { (void)name; }
#endif /* APP_LOG_ENABLE */

66
APP/app_log.h Normal file
View File

@ -0,0 +1,66 @@
#ifndef __APP_LOG_H__
#define __APP_LOG_H__
#include <stdint.h>
#ifndef APP_LOG_ENABLE
#define APP_LOG_ENABLE 1
#endif
#ifndef APP_LOG_DEBUG
#define APP_LOG_DEBUG 1
#endif
#ifndef APP_LOG_RAM_SIZE
#define APP_LOG_RAM_SIZE (4U * 1024U)
#endif
#ifndef APP_LOG_SLOT_SIZE
#define APP_LOG_SLOT_SIZE 128U
#endif
#ifndef APP_LOG_RTT_CHANNEL
#define APP_LOG_RTT_CHANNEL 0
#endif
#ifndef APP_LOG_TP_VERBOSE
#define APP_LOG_TP_VERBOSE 1
#endif
#define APP_LOG_SLOT_COUNT (APP_LOG_RAM_SIZE / APP_LOG_SLOT_SIZE)
void app_log_init(void);
void app_log_write(char level, const char *cat, const char *fmt, ...);
void app_log_poll(void);
uint8_t app_log_try_command(const char *cmd);
void app_log_set_ui_page(const char *name);
#if APP_LOG_ENABLE
#define LOG_I(cat, fmt, ...) app_log_write('I', (cat), (fmt), ##__VA_ARGS__)
#define LOG_W(cat, fmt, ...) app_log_write('W', (cat), (fmt), ##__VA_ARGS__)
#define LOG_E(cat, fmt, ...) app_log_write('E', (cat), (fmt), ##__VA_ARGS__)
#if APP_LOG_DEBUG
#define LOG_D(cat, fmt, ...) app_log_write('D', (cat), (fmt), ##__VA_ARGS__)
#else
#define LOG_D(cat, fmt, ...) ((void)0)
#endif
#if APP_LOG_TP_VERBOSE
#define LOG_TP(fmt, ...) LOG_D("TP", fmt, ##__VA_ARGS__)
#else
#define LOG_TP(fmt, ...) ((void)0)
#endif
#else
#define LOG_I(cat, fmt, ...) ((void)0)
#define LOG_W(cat, fmt, ...) ((void)0)
#define LOG_E(cat, fmt, ...) ((void)0)
#define LOG_D(cat, fmt, ...) ((void)0)
#define LOG_TP(fmt, ...) ((void)0)
#endif
#endif /* __APP_LOG_H__ */

View File

@ -78,6 +78,7 @@ uint8_t app_tm1617_scan_key(void)
if (key != key_last)
{
key_last = key;
LOG_I("KEY", "tm1617 key=%u", (unsigned)key);
TM1617_Handle(key);
return key;
}

View File

@ -31,6 +31,7 @@ uint8_t app_tm1629_Scan_Key(void)
if(key != key_last)
{
key_last = key;
LOG_I("KEY", "tm1629 key=%u", (unsigned)key);
TM1629_Handle(key);
return key;
}

View File

@ -1,6 +1,6 @@
#include "includes.h"
/* 按下即发点击;抬起仅补发未发出的短按 */
/* ?????????????????????????????? */
typedef enum {
TOUCH_IDLE,
@ -58,6 +58,7 @@ app_touch_result_t app_touch_process(uint16_t x, uint16_t y, uint8_t pressed)
r.gesture = GESTURE_RELEASE;
r.x = down_x;
r.y = down_y;
LOG_D("TP", "release x=%u y=%u", (unsigned)down_x, (unsigned)down_y);
MainTask_Sendmsg(MSG_ID_TOUCH, 1, down_x, down_y);
}
touch_state = TOUCH_IDLE;
@ -79,8 +80,9 @@ app_touch_result_t app_touch_process(uint16_t x, uint16_t y, uint8_t pressed)
click_sent = 0;
repeat_tick = 0;
touch_state = TOUCH_PRESS_WAIT_STABLE;
/* 第一帧就发点击,模式选择等页跟手 */
/* ??????????????????????? */
click_sent = 1;
LOG_D("TP", "press down x=%u y=%u", (unsigned)down_x, (unsigned)down_y);
MainTask_Sendmsg(MSG_ID_TOUCH, 1, down_x, down_y);
break;
@ -101,6 +103,7 @@ app_touch_result_t app_touch_process(uint16_t x, uint16_t y, uint8_t pressed)
long_press_fired = 1;
repeat_tick = now;
r.gesture = GESTURE_LONG_PRESS;
LOG_D("TP", "long_press x=%u y=%u", (unsigned)down_x, (unsigned)down_y);
MainTask_Sendmsg(MSG_ID_TOUCH_LONG_REPEAT, 1, down_x, down_y);
return r;
}

View File

@ -96,12 +96,22 @@ bool AutoCloseFlag = true;
void MainTask_Sendmsg(uint16_t ID, uint16_t ID2, uint16_t HiByte, uint16_t LoByte)
{
DisplayTaskMessage_Type msg;
rt_err_t ret;
msg.MessageType = ID; /* 业务消息 IDMSG_ID_xxx */
msg.ID = ID2;
msg.HiByte = HiByte;
msg.LoByte = LoByte;
rt_mq_send(MainTask_msg, &msg, sizeof(DisplayTaskMessage_Type));
ret = rt_mq_send(MainTask_msg, &msg, sizeof(DisplayTaskMessage_Type));
if (ret != RT_EOK) {
LOG_E("MSG", "send fail id=%u ret=%d", (unsigned)ID, (int)ret);
} else if (ID == MSG_ID_TOUCH || ID == MSG_ID_TOUCH_LONG_REPEAT ||
ID == MSG_ID_POWER_ON || ID == MSG_ID_POWER_OFF ||
ID == MSG_ID_BP_KEY || ID == MSG_ID_TAP_SPEED ||
ID == MSG_ID_BP_TRANSPOSE) {
LOG_I("MSG", "id=%u hi=%u lo=%u", (unsigned)ID, (unsigned)HiByte, (unsigned)LoByte);
}
}
@ -153,6 +163,7 @@ bool powon = false;
/* ==================== 开机/关机动作 ==================== */
void System_PowerOn(void)
{
LOG_I("PWR", "power_on begin");
/* 控制电源硬件 */
BSP_MainPowerEnable(1);
BSP_DreamCorePowerEnable(1);
@ -165,6 +176,7 @@ void System_PowerOn(void)
app_tm1617_init();
XPT2046_Init();
App_Auto_Init();
LOG_I("PWR", "periph ready send POWER_ON");
/* 通知 UI 层画开机界面 */
MainTask_Sendmsg(MSG_ID_POWER_ON,0,0,0);
}
@ -176,10 +188,14 @@ static void PowerOn(void)
static void PowerOff(void)
{
uint8_t nvm_ret;
LOG_I("PWR", "power_off begin");
powon = false; /* 更新关机标志 */
StopFullTask();
drv_nvm_save_to_flash();
nvm_ret = drv_nvm_save_to_flash();
LOG_I("PWR", "nvm_save=%u", (unsigned)nvm_ret);
LCD_FillByColor(0, 0, 240, 320, BLACK);
LCD_BLK_Clr();
@ -192,6 +208,7 @@ static void PowerOff(void)
BSP_DreamCorePowerEnable(0);
BSP_HT7178PowerEnable(0);
BSP_BlueToothPowerEnable(0);
LOG_I("PWR", "reset");
__disable_irq();
nvic_system_reset();
//MainTask_Sendmsg(MSG_ID_POWER_OFF,0,0,0);
@ -210,6 +227,7 @@ void Power_Key_Scan()
if (s_auto_poweron_pending)
{
s_auto_poweron_pending = 0;
LOG_I("PWR", "auto power_on");
PowerOn();
pwr_key_state = KEY_STATE_NONE; /* 忽略开机瞬间可能残留的按键电平 */
return;
@ -238,11 +256,13 @@ void Power_Key_Scan()
/* 用 powon 区分:关机长按 3s 开机,开机长按 2s 关机 */
if(powon == false && holdtick >= PWR_ON_HOLD_MS)
{
LOG_I("PWR", "key power_on hold=%u", (unsigned)holdtick);
PowerOn();
pwr_key_state = KEY_STATE_NONE;
}
else if(powon == true && holdtick >= PWR_OFF_HOLD_MS)
{
LOG_I("PWR", "key power_off hold=%u", (unsigned)holdtick);
PowerOff();
pwr_key_state = KEY_STATE_NONE;
}

View File

@ -211,12 +211,18 @@ void IdleProcess(DisplayTaskMessage_Type msg)
break;
case MSG_ID_TOUCH:
{
uint8_t hit = 0U;
for(uint8_t k=0; k<TOUCH_AREA_MODE_SELECT_NUM; k++)
{
if( msg.HiByte >= Touch_ModeSelect_Areas[k].x_min && msg.HiByte <= Touch_ModeSelect_Areas[k].x_max &&
msg.LoByte >= Touch_ModeSelect_Areas[k].y_min && msg.LoByte <= Touch_ModeSelect_Areas[k].y_max )
{
hit = 1U;
uint8_t row = (uint8_t)Touch_ModeSelect_Areas[k].Flag;
LOG_I("UI", "mode_select hit area=%u row=%u x=%u y=%u",
(unsigned)k, (unsigned)row,
(unsigned)msg.HiByte, (unsigned)msg.LoByte);
if (row == s_mode_select_focus)
Touch_Mode_Select_Action(Touch_ModeSelect_Areas[k].Flag, Touch_ModeSelect_Areas[k].ID, k);
else
@ -224,6 +230,11 @@ void IdleProcess(DisplayTaskMessage_Type msg)
break;
}
}
if (!hit) {
LOG_I("UI", "mode_select miss x=%u y=%u",
(unsigned)msg.HiByte, (unsigned)msg.LoByte);
}
}
break;
case MSG_ID_TOUCH_LONG_REPEAT:

View File

@ -377,16 +377,27 @@ void UI_SongMode_Process(DisplayTaskMessage_Type msg)
switch(msg.MessageType)
{
case MSG_ID_TOUCH:
{
uint8_t hit = 0U;
for(uint8_t k=0; k<TOUCH_AREA_NUM; k++)
{
if( msg.HiByte >= pCurrentTouch_Area[k].x_min && msg.HiByte <= pCurrentTouch_Area[k].x_max &&
msg.LoByte >= pCurrentTouch_Area[k].y_min && msg.LoByte <= pCurrentTouch_Area[k].y_max )
{
hit = 1U;
LOG_I("UI", "song hit area=%u id=%u x=%u y=%u",
(unsigned)k, (unsigned)pCurrentTouch_Area[k].ID,
(unsigned)msg.HiByte, (unsigned)msg.LoByte);
Touch_Action(pCurrentTouch_Area[k].Flag,pCurrentTouch_Area[k].ID,k,pCurrentTouch_Area[k].Value);
ResetAutoPowerCount();
break;
}
}
if (!hit) {
LOG_I("UI", "song miss x=%u y=%u",
(unsigned)msg.HiByte, (unsigned)msg.LoByte);
}
}
break;
case MSG_ID_BATTERY_VALUE:

View File

@ -33,43 +33,59 @@ void InitMenuUI(void) {
// ==============================================
void CallUI_Idle(void) {
CurrUIProcress = IdleProcess;
app_log_set_ui_page("Idle");
LOG_I("UI", "page Idle");
}
void CallUI_SongMode(void) {
UI_SongMode_Init();
CurrUIProcress = UI_SongMode_Process;
app_log_set_ui_page("Song");
LOG_I("UI", "page SongMode tab=%u", (unsigned)mGuiData[GUI_TAB_INDEX].Current);
}
void CallUI_ExpertMode(void) {
UI_ExpertMode_Init();
// CurrUIProcress = UI_ExpertMode_Process;
CurrUIProcress = UI_SongMode_Process;
app_log_set_ui_page("Expert");
LOG_I("UI", "page ExpertMode");
}
void CallUI_FreeMode(void) {
UI_FreeMode_Init();
// CurrUIProcress = UI_FreeMode_Process;
CurrUIProcress = UI_SongMode_Process;
app_log_set_ui_page("Free");
LOG_I("UI", "page FreeMode");
}
void CallUI_Setting(void) {
CurrUIProcress = UI_Setting_Process;
UI_Setting_Init();
app_log_set_ui_page("Setting");
LOG_I("UI", "page Setting");
}
void CallUI_Mixer(void) {
CurrUIProcress = UI_Mixer_Process;
UI_Mixer_Init();
app_log_set_ui_page("Mixer");
LOG_I("UI", "page Mixer");
}
void CallUI_SystemSetting(void) {
CurrUIProcress = UI_SystemSet_Process;
UI_SystemSet_Init();
app_log_set_ui_page("SysSet");
LOG_I("UI", "page SystemSetting");
}
void CallUI_RestoreSelect(void) {
CurrUIProcress = UI_Restore_Select_Process;
UI_Restore_Select_Init();
app_log_set_ui_page("Restore");
LOG_I("UI", "page Restore");
}
static uint8_t s_ui_focus_id = UI_FOCUS_NONE;

View File

@ -198,6 +198,14 @@ static uint8_t LCD_Dump_CommandPending(void)
char c = rx[i];
if (c == '\r' || c == '\n' || c == '\0') {
if (s_cmd_len > 0U) {
s_cmd_buf[s_cmd_len] = '\0';
if (app_log_try_command(s_cmd_buf)) {
s_cmd_len = 0U;
s_cmd_buf[0] = '\0';
continue;
}
}
s_cmd_len = 0U;
s_cmd_buf[0] = '\0';
continue;

View File

@ -143,9 +143,12 @@ TP_Point TP_ReadRaw(void)
TP_Point p = {0, 0, 0};
uint32_t x_sum = 0, y_sum = 0;
uint8_t i;
static uint8_t s_raw_logged;
if (!TP_CheckPressed(3))
if (!TP_CheckPressed(3)) {
s_raw_logged = 0U;
return p;
}
for (i = 0; i < 5; i++)
{
@ -178,6 +181,11 @@ TP_Point TP_ReadRaw(void)
}
p.pressed = 1;
if (!s_raw_logged) {
LOG_TP("raw x=%u y=%u irq=%u", (unsigned)p.x, (unsigned)p.y,
(unsigned)(TP_IRQ_Read() == 0U));
s_raw_logged = 1U;
}
return p;
}

View File

@ -79,7 +79,7 @@ void drv_nvm_init(void)
dataSum1 = ~dataSum0;
if ((dataSum0 != mNvmData.sum[0]) || (dataSum1 != mNvmData.sum[1]) || (mNvmData.data.nvm_version != NVM_VERSION)) {
// load default value;
LOG_W("NVM", "checksum fail load default ver=%u", (unsigned)mNvmData.data.nvm_version);
drv_nvm_load_default_value(&mNvmData.data);
drv_nvm_save_to_flash();
}else
@ -88,6 +88,7 @@ void drv_nvm_init(void)
drv_nvm_save_to_flash();
}
memcpy(&mNvmDataShadow, &mNvmData, sizeof(NvmData_Type));
LOG_I("NVM", "init ok ver=%u", (unsigned)mNvmData.data.nvm_version);
}
NvmParam_Type * drv_nvm_param_ptr(void)
@ -146,8 +147,9 @@ uint8_t drv_nvm_save_to_flash(void)
uint32_t addr = INNER_FLASH_SAVE_ADDRESS;
NvmData_Type * NvmDataToSave;
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// 无变化不保存
if (memcmp(&mNvmDataShadow, &mNvmData, sizeof(NvmData_Type)) == 0) {
LOG_D("NVM", "save skip unchanged");
return 0;
}
@ -172,6 +174,7 @@ uint8_t drv_nvm_save_to_flash(void)
flash_write_nocheck(addr,pData16,len);
flash_lock();
LOG_I("NVM", "saved to flash");
return 1;
}

View File

@ -2001,6 +2001,12 @@
<file>
<name>$PROJ_DIR$\..\..\APP\app_touch.h</name>
</file>
<file>
<name>$PROJ_DIR$\..\..\APP\app_log.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\..\APP\app_log.h</name>
</file>
</group>
<group>
<name>AutoBand</name>

View File

@ -76,5 +76,6 @@
#include "app_tm1617.h"
#include "app_touch.h"
#include "app_adc.h"
#include "app_log.h"
#endif

View File

@ -83,6 +83,7 @@ int main(void)
bsp_init();
LCD_Init();
LCD_Dump_Init();
app_log_init();
drv_nvm_init();
TaskInit();
timer_init();
@ -90,6 +91,7 @@ int main(void)
BSP_HT7178PowerEnable (1);
BSP_ChargerEnable(1);
CurrUIProcress = IdleProcess;
LOG_I("BOOT", "main init done ui=Idle");
/* add user code end 2 */
while(1)
@ -102,6 +104,9 @@ int main(void)
/* add user code begin 3 */
Power_Key_Scan();
LCD_Dump_Poll();
#if !DEBUG_LCD_DUMP
app_log_poll();
#endif
rt_thread_delay(1);
/* add user code end 3 */

View File

@ -1,5 +1,4 @@
#include "includes.h"
#include "SEGGER_RTT.h"
static struct rt_thread TaskScanThread;
static struct rt_thread TaskUIThread;
@ -51,9 +50,7 @@ void TaskTouchThread_entry(void* parameter)
TP_Point tp = TP_Read(); /* 已含轴交换/镜像,直接进手势 */
if (tp.pressed && !s_last_pressed)
{
char buf[40];
sprintf(buf, "TP %u,%u\n", (unsigned)tp.x, (unsigned)tp.y);
SEGGER_RTT_WriteString(0, buf);
LOG_TP("down cal x=%u y=%u", (unsigned)tp.x, (unsigned)tp.y);
}
s_last_pressed = tp.pressed;
app_touch_process(tp.x, tp.y, tp.pressed);
@ -154,7 +151,7 @@ rt_err_t TaskInit(void)
BLTask_msg = rt_mq_create("BLTask msgQ",sizeof(DisplayTaskMessage_Type),128,RT_IPC_FLAG_FIFO);
if(MainTask_msg == RT_NULL || UITask_msg == RT_NULL)
{
__NOP();
LOG_E("BOOT", "mq create fail main=%p ui=%p", MainTask_msg, UITask_msg);
}
ret = rt_thread_init(&TaskScanThread,
@ -316,18 +313,32 @@ void StartScanTask(void)
void StopFullTask(void)
{
if((TaskScanThread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE)
LOG_I("TASK", "StopFullTask begin");
if((TaskScanThread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE) {
LOG_I("TASK", "stop ScanThread");
rt_thread_detach(&TaskScanThread);
if((TaskUIThread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE)
}
if((TaskUIThread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE) {
LOG_I("TASK", "stop UIThread");
rt_thread_detach(&TaskUIThread);
if((TaskTouchThread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE)
}
if((TaskTouchThread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE) {
LOG_I("TASK", "stop TouchThread");
rt_thread_detach(&TaskTouchThread);
if((TaskMainThread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE)
}
if((TaskMainThread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE) {
LOG_I("TASK", "stop MainThread");
rt_thread_detach(&TaskMainThread);
if((TaskBTHandleThread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE)
}
if((TaskBTHandleThread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE) {
LOG_I("TASK", "stop BTHandleThread");
rt_thread_detach(&TaskBTHandleThread);
if((TaskBTRecvThread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE)
}
if((TaskBTRecvThread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE) {
LOG_I("TASK", "stop BTRecvThread");
rt_thread_detach(&TaskBTRecvThread);
}
LOG_I("TASK", "StopFullTask done");
}

View File

@ -3,30 +3,20 @@
* The Embedded Experts *
* www.segger.com *
**********************************************************************
* *
* SEGGER RTT * Real Time Transfer for embedded targets *
* https://github.com/SEGGERMicro/RTT *
* *
**********************************************************************
---------------------------END-OF-HEADER------------------------------
Purpose : User configuration file for RTT.
For available configuration,
refer to SEGGER_RTT_ConfDefaults.h.
----------------------------------------------------------------------
*/
#ifndef SEGGER_RTT_CONF_H
#define SEGGER_RTT_CONF_H
/*********************************************************************
*
* Defines, configurable
*
**********************************************************************
*/
#define SEGGER_RTT_MAX_NUM_UP_BUFFERS (3)
#define SEGGER_RTT_MAX_NUM_DOWN_BUFFERS (3)
#define BUFFER_SIZE_UP (2048)
#define BUFFER_SIZE_DOWN (256)
#define SEGGER_RTT_PRINTF_BUFFER_SIZE (256u)
#endif
/*************************** End of file ****************************/

54
tools/LOG_README.md Normal file
View File

@ -0,0 +1,54 @@
# K1 设备日志导出J-Link RTT
## 用途
固件运行时将启动/关机、触摸、按键、UI 切换、异常等事件写入 RAM 环形缓冲(约 4KB / 32 条),并通过 SEGGER RTT 实时输出。现场复现问题后,用 J-Link 一键导出日志文件发回分析。
## 前置条件
- J-Link 调试器 + SWD 连接设备
- Python 3.8+
- 依赖:`pip install pylink-square`
- 已烧录包含 `app_log` 模块的固件
## 操作步骤
1. 设备上电,复现问题(例如模式选择页触摸无响应)。
2. PC 连接 J-Link**不要**随意 Reset会丢失 RAM 日志;若必须复位,请先导出或复现后再导出)。
3. 在项目 `tools` 目录执行:
```bash
python rtt_log_dump.py --out problem.log
```
4. 将生成的 `problem.log` 发送给研发分析。
## 其他 RTT 命令RTT Viewer 终端输入)
| 命令 | 说明 |
|------|------|
| `log dump` | 导出 RAM 中全部历史日志 |
| `log clear` | 清空 RAM 日志 |
| `log status` | 查看 boot 次数、条数、溢出、当前 UI 页 |
| `log level D` | 开启 Debug 级(含触摸细节);`I` 仅 Info 以上 |
## 日志格式
```
[tick_ms][LVL][CAT] message key=val ...
```
类别 CAT`BOOT` `PWR` `UI` `TP` `KEY` `MSG` `NVM` `TASK` `ERR` `ADC`
## 配套工具
- 屏幕截图:`python rtt_lcd_capture.py --out screen.png`
- 日志导出:`python rtt_log_dump.py --out problem.log`
## 触摸问题排查要点
导出后重点 grep
- `[TP]` — raw/cal 坐标、irq 状态
- `[MSG] id=12` — 触摸消息是否发出
- `[UI] mode_select miss` — UI 命中测试是否失败

184
tools/rtt_log_dump.py Normal file
View File

@ -0,0 +1,184 @@
#!/usr/bin/env python3
"""Pull K1 device log ring buffer via J-Link RTT (log dump command)."""
from __future__ import annotations
import argparse
import os
import sys
import time
from datetime import datetime
DEFAULT_DEVICES = ("Cortex-M4", "AT32F403AC", "AT32F403A")
DUMP_CMD = b"log dump\n"
END_MARKERS = (b"LOG_DUMP_END",)
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:
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 = 30) -> None:
for _ in range(retries):
wrote = jlink.rtt_write(0, list(payload))
if wrote > 0:
print(f"Sent command ({wrote} bytes): {payload.decode('ascii', errors='replace').strip()}")
return
time.sleep(0.2)
raise SystemExit("Failed to send RTT down command on channel 0")
def dump_log(out_path: str, device: str, timeout_s: float) -> None:
jlink = connect_jlink(device)
lines: list[str] = []
try:
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}")
jlink.rtt_start(cb)
wait_rtt_ready(jlink)
# Drain stale RTT output.
for _ in range(5):
stale = jlink.rtt_read(0, 4096)
if stale:
text = bytes(stale).decode("utf-8", errors="replace")
if text.strip():
print("RTT0 stale:", text.strip()[:200])
time.sleep(0.05)
send_command(jlink, DUMP_CMD)
buffer = b""
deadline = time.time() + timeout_s
started = False
while time.time() < deadline:
chunk = jlink.rtt_read(0, 4096)
if chunk:
buffer += bytes(chunk)
if not started and b"LOG_DUMP_BEGIN" in buffer:
started = True
if b"LOG_DUMP_END" in buffer:
break
else:
time.sleep(0.01)
if b"LOG_DUMP_END" not in buffer:
raise SystemExit("Timeout waiting for LOG_DUMP_END (is firmware with app_log flashed?)")
text = buffer.decode("utf-8", errors="replace")
capture = False
for line in text.splitlines():
if line.strip() == "LOG_DUMP_BEGIN":
capture = True
continue
if line.strip() == "LOG_DUMP_END":
break
if capture:
lines.append(line)
header = (
f"# K1 log dump {datetime.now().isoformat(timespec='seconds')}\n"
f"# device={device}\n"
)
body = "\n".join(lines) + ("\n" if lines else "")
with open(out_path, "w", encoding="utf-8", newline="\n") as f:
f.write(header)
f.write(body)
print(f"Saved {len(lines)} log lines to {out_path}")
finally:
try:
jlink.rtt_stop()
except Exception:
pass
jlink.close()
def main() -> None:
parser = argparse.ArgumentParser(description="Dump K1 RAM log ring via J-Link RTT")
parser.add_argument(
"--out",
default=f"k1_log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log",
help="Output log file path",
)
parser.add_argument("--device", default="AT32F403AC", help="J-Link device name")
parser.add_argument("--timeout", type=float, default=20.0, help="Seconds to wait for dump")
args = parser.parse_args()
import_deps()
dump_log(args.out, args.device, args.timeout)
if os.name == "nt" and os.path.isfile(args.out):
print(f"Full path: {os.path.abspath(args.out)}")
if __name__ == "__main__":
main()