#include "includes.h" #include "SEGGER_RTT.h" #include #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; /* RTT 二进制烧录:写到 W25Q128(ui0902 或本地曲目) */ static uint8_t s_flash_mode; static uint32_t s_flash_base; static uint32_t s_flash_addr; static uint32_t s_flash_remain; static uint32_t s_flash_last_sector; static uint16_t s_flash_page_len; static uint8_t s_flash_page[256]; static char s_flash_ok_tag[24]; uint8_t app_log_flash_busy(void) { return s_flash_mode; } static uint32_t app_log_ms(void) { return (uint32_t)(rt_tick_get() * 1000U / RT_TICK_PER_SECOND); } static void app_log_flash_flush_page(void) { uint32_t sector; char ack[40]; if (s_flash_page_len == 0U) { return; } sector = s_flash_addr & ~0xFFFU; if (sector != s_flash_last_sector) { W25Q128_Erase_Sector(sector); s_flash_last_sector = sector; } W25Q128_Write(s_flash_page, s_flash_addr, s_flash_page_len); s_flash_addr += s_flash_page_len; s_flash_remain -= s_flash_page_len; s_flash_page_len = 0U; /* 每页 ACK,避免主机打爆 RTT 下行缓冲 */ snprintf(ack, sizeof(ack), "FLASH_ACK %lu\n", (unsigned long)s_flash_addr); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, ack); } static void app_log_flash_feed(const uint8_t *data, unsigned len) { unsigned i; for (i = 0; i < len && s_flash_remain > 0U; i++) { if (s_flash_page_len == 0U && s_flash_addr == s_flash_base && i == 0U) { SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "FLASH_RX\n"); } s_flash_page[s_flash_page_len++] = data[i]; if (s_flash_page_len >= sizeof(s_flash_page) || s_flash_page_len >= s_flash_remain) { app_log_flash_flush_page(); } } if (s_flash_remain == 0U && s_flash_mode) { s_flash_mode = 0U; SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, s_flash_ok_tag); UI_DrawModeSelectScreen(); } } static void app_log_flash_start_at(uint32_t base, uint32_t size, const char *go_tag, const char *ok_tag) { char msg[80]; if (size == 0U || size > (2U * 1024U * 1024U)) { SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "FLASH_ERR size\n"); return; } s_flash_mode = 1U; s_flash_base = base; s_flash_addr = base; s_flash_remain = size; s_flash_last_sector = 0xFFFFFFFFUL; s_flash_page_len = 0U; s_cmd_len = 0U; s_cmd_buf[0] = '\0'; strncpy(s_flash_ok_tag, ok_tag, sizeof(s_flash_ok_tag) - 1U); s_flash_ok_tag[sizeof(s_flash_ok_tag) - 1U] = '\0'; snprintf(msg, sizeof(msg), "%s base=0x%08lX size=%lu\n", go_tag, (unsigned long)base, (unsigned long)size); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, msg); } static void app_log_flash_start(uint32_t size) { app_log_flash_start_at(UI0902_RES_BASE, size, "FLASH_UI0902_GO", "FLASH_UI0902_OK\n"); } 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"); } 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) { 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; } 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) { app_log_sys_reset(); return 1U; } if (strncmp(cmd, "flash ui0902 ", 13) == 0) { uint32_t size = (uint32_t)strtoul(cmd + 13, NULL, 10); app_log_flash_start(size); return 1U; } if (strncmp(cmd, "flash haitian ", 14) == 0) { uint32_t size = (uint32_t)strtoul(cmd + 14, NULL, 10); app_log_flash_start_at(FLASH_ADDR_SONG_HAITIAN, size, "FLASH_HAITIAN_GO", "FLASH_HAITIAN_OK\n"); return 1U; } if (strncmp(cmd, "flash bin3 ", 11) == 0) { uint32_t size = (uint32_t)strtoul(cmd + 11, NULL, 10); app_log_flash_start_at(EXTFLASH_BIN3_UNIVERSAL_ADDR, size, "FLASH_BIN3_GO", "FLASH_BIN3_OK\n"); return 1U; } if (strncmp(cmd, "tone status", 11) == 0) { uint8_t hdr[16]; uint8_t name[40]; uint8_t cur_hdr[16]; char *pname; char line[192]; const char *map; W25Q128_Read(hdr, FLASH_ADDR_SONG_HAITIAN, 16); W25Q128_Read(name, FLASH_ADDR_SONG_HAITIAN + 1280 + 4, 32); name[32] = 0; W25Q128_Read(cur_hdr, (uint32_t)ADDRESS, 16); pname = AutoBandTop1_GetPresetName(); if (ADDRESS == EXTFLASH_BIN1_RHYTHM_ADDR) map = "BIN1_RHYTHM"; else if (ADDRESS == EXTFLASH_BIN2_SONG_HAITIAN_ADDR) map = "BIN2_SONG_HAITIAN"; else if (ADDRESS == EXTFLASH_BIN3_UNIVERSAL_ADDR) map = "BIN3_UNIVERSAL"; else map = "UNKNOWN"; snprintf(line, sizeof(line), "TONE tab=%u sw=%u addr=0x%08lX map=%s songidx=%u rhyt=%u\n", (unsigned)mGuiData[GUI_TAB_INDEX].Current, (unsigned)mGuiData[GUI_AUTOBAND_SW].Current, (unsigned long)ADDRESS, map, (unsigned)ParamGuiData[EXPRESS_MODE_PARAM].Current, (unsigned)ParamGuiData[SONG_MODE_PARAM].Current); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line); snprintf(line, sizeof(line), "TONE map BIN1=0x%08lX BIN2=0x%08lX BIN3=0x%08lX\n", (unsigned long)EXTFLASH_BIN1_RHYTHM_ADDR, (unsigned long)EXTFLASH_BIN2_SONG_HAITIAN_ADDR, (unsigned long)EXTFLASH_BIN3_UNIVERSAL_ADDR); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line); snprintf(line, sizeof(line), "TONE @ADDRESS magic=%02X%02X%02X%02X cnt=%lu\n", cur_hdr[0], cur_hdr[1], cur_hdr[2], cur_hdr[3], (unsigned long)(cur_hdr[12] | (cur_hdr[13] << 8) | (cur_hdr[14] << 16) | (cur_hdr[15] << 24))); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line); { uint8_t b3[16]; W25Q128_Read(b3, EXTFLASH_BIN3_UNIVERSAL_ADDR, 16); snprintf(line, sizeof(line), "TONE @BIN3 magic=%02X%02X%02X%02X cnt=%lu\n", b3[0], b3[1], b3[2], b3[3], (unsigned long)(b3[12] | (b3[13] << 8) | (b3[14] << 16) | (b3[15] << 24))); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line); } snprintf(line, sizeof(line), "TONE @BIN2 magic=%02X%02X%02X%02X max=%lu cnt=%lu name=%.16s\n", hdr[0], hdr[1], hdr[2], hdr[3], (unsigned long)(hdr[8] | (hdr[9] << 8) | (hdr[10] << 16) | (hdr[11] << 24)), (unsigned long)(hdr[12] | (hdr[13] << 8) | (hdr[14] << 16) | (hdr[15] << 24)), (char *)name); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line); snprintf(line, sizeof(line), "TONE loaded name=%s count=%d\n", pname ? pname : "(null)", AutoBandTop1_GetPresetItemCount()); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line); return 1U; } if (strncmp(cmd, "tone reload", 11) == 0) { char line[96]; UI_ReloadTonePreset(); snprintf(line, sizeof(line), "TONE_RELOAD addr=0x%08lX name=%s\n", (unsigned long)ADDRESS, AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "(null)"); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line); return 1U; } if (strncmp(cmd, "tone local", 10) == 0) { char line[128]; int ret; mGuiData[GUI_TAB_INDEX].Current = 1; /* 专业 */ mGuiData[GUI_AUTOBAND_SW].Current = 1; /* 本地曲目 */ ParamGuiData[EXPRESS_MODE_PARAM].Current = 0; UI_ApplyToneAddress(); AutoBandTop1_Stop(); StartFlag = 0; ret = AutoBandTop1_LoadPresetItemFromFlash(0); snprintf(line, sizeof(line), "TONE_LOCAL ret=%d addr=0x%08lX map=BIN2@0x%08lX name=%s count=%d %s\n", ret, (unsigned long)ADDRESS, (unsigned long)EXTFLASH_BIN2_SONG_HAITIAN_ADDR, AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "(null)", AutoBandTop1_GetPresetItemCount(), (ADDRESS == EXTFLASH_BIN2_SONG_HAITIAN_ADDR) ? "OK" : "MISMATCH"); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line); return 1U; } if (strncmp(cmd, "tone pick", 9) == 0) { /* 模拟专业+本地曲目拨片(无和弦板) */ mGuiData[GUI_TAB_INDEX].Current = 1; mGuiData[GUI_AUTOBAND_SW].Current = 1; KEY_ID_1629 = 0; /* 走默认和弦兜底 */ StartFlag = 0; Pick_Handle(); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "TONE_PICK_DONE\n"); return 1U; } if (strncmp(cmd, "flash erase chip", 16) == 0) { SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "FLASH_ERASE_BEGIN\n"); LOG_I("FLASH", "W25Q128 chip erase start"); W25Q128_Erase_Chip(); LOG_I("FLASH", "W25Q128 chip erase done"); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "FLASH_ERASE_OK\n"); return 1U; } if (strncmp(cmd, "ui boot", 7) == 0) { LCD_BLK_Set(); LCD_WR_PIC_FROM_FLASH(0, 0, UI0902_BOOT_LOGO_W, UI0902_BOOT_LOGO_H, UI0902_BOOT_LOGO_ADDR); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "UI_BOOT_OK\n"); return 1U; } if (strncmp(cmd, "ui charge", 9) == 0) { uint8_t peek[4]; LCD_BLK_Set(); LCD_FillByColor(0, 0, 240, 320, UI0902_BG_COLOR); W25Q128_Read(peek, UI0902_CHARGE_SCREEN_ADDR, 4); if (peek[0] != 0xFF || peek[1] != 0xFF) { LCD_WR_PIC_FROM_FLASH(0, 0, UI0902_CHARGE_SCREEN_W, UI0902_CHARGE_SCREEN_H, UI0902_CHARGE_SCREEN_ADDR); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "UI_CHARGE_OK\n"); } else { Draw_Charging_Icon(120, 150, 2, 0x3D7F); SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "UI_CHARGE_EMPTY\n"); } { const char *s = "50"; uint16_t tw = g_ChargePctPercent.w; uint16_t x, y = 266; uint8_t i; for (i = 0; s[i]; i++) tw = (uint16_t)(tw + g_ChargePctDigits[s[i] - '0'].w); LCD_FillByColor(40, 262, 200, 290, UI0902_BG_COLOR); x = (uint16_t)((240 - tw) / 2); for (i = 0; s[i]; i++) { const ChargePctGlyph *g = &g_ChargePctDigits[s[i] - '0']; LCD_WR_PIC_Trans(x, y, g->w, g->h, g->data, BLACK); x = (uint16_t)(x + g->w); } LCD_WR_PIC_Trans(x, y, g_ChargePctPercent.w, g_ChargePctPercent.h, g_ChargePctPercent.data, BLACK); } 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'; 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) { uint8_t rx[64]; unsigned read_len; unsigned i; read_len = SEGGER_RTT_Read(APP_LOG_RTT_CHANNEL, rx, sizeof(rx)); if (read_len == 0U) { return; } if (s_flash_mode) { app_log_flash_feed(rx, read_len); return; } for (i = 0; i < read_len; i++) { app_log_feed_char((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; } uint8_t app_log_flash_busy(void) { return 0U; } #endif /* APP_LOG_ENABLE */