Compare commits

..

7 Commits

Author SHA1 Message Date
yuquanjun 956eeee1a5 Add RTT sys reset command and remote reboot script with CPU resume.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-31 12:31:53 +08:00
yuquanjun f6ae34892f Document verified J-Link log dump workflow and harden flash script.
Kill stale cspybat before download; note successful RTT log export sample in LOG_README.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-31 11:45:36 +08:00
yuquanjun 7fea921727 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>
2026-08-31 11:24:10 +08:00
yuquanjun b8eaf2c808 Fix mode-select focus and touch pipeline; add return-to-home gesture.
Preserve focus across redraws, improve XPT2046 sampling, and log touch coords via RTT for ongoing debug.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-31 11:13:01 +08:00
yuquanjun b996c8876e Use designer 0831 PNG bitmaps for mode-select labels.
Replace on-device Chinese glyphs with MiSans icon+text row assets so the trial format matches the designer's export.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-31 09:41:13 +08:00
yuquanjun ba31d91aef Fix mode-select touch area IDs and keep touch thread from blocking on LCD dump.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-31 08:21:58 +08:00
yuquanjun 68e3a37883 Fix mode-select layout/battery margins and enable reliable post-flash boot.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-31 08:20:24 +08:00
31 changed files with 5017 additions and 325 deletions

226
APP/app_log.c Normal file
View File

@ -0,0 +1,226 @@
#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");
}
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, "sys reset", 9) == 0) {
app_log_sys_reset();
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)
{
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,64 +1,46 @@
#include "includes.h"
/* ============================================================================
* app_touch
*
* Touch_Process
* IDLE PRESS_WAIT_STABLE PRESS_STABLE SLIDING
*
*
* 20ms (TOUCH_STABLE_TIME)
* 10px (TOUCH_SLIDE_THRESH)
* 5px (TOUCH_SLIDE_STEP)
* 1000ms (TOUCH_LONG_PRESS_TIME)
* 120ms (TOUCH_REPEAT_INTERVAL)
* ==========================================================================*/
/* ?????????????????????????????? */
/* ==================== 状态机枚举 ==================== */
typedef enum {
TOUCH_IDLE, /* 空闲 */
TOUCH_PRESS_WAIT_STABLE, /* 按下,等待稳定判定 */
TOUCH_PRESS_STABLE, /* 稳定按下(可长按) */
TOUCH_SLIDING /* 滑动中 */
TOUCH_IDLE,
TOUCH_PRESS_WAIT_STABLE,
TOUCH_PRESS_STABLE,
TOUCH_SLIDING
} touch_state_t;
/* ==================== 时序参数 ==================== */
#define TOUCH_STABLE_TIME 20 /* 按下稳定判定时间 ms */
#define TOUCH_SLIDE_THRESH 10 /* 进入滑动的最小位移 px */
#define TOUCH_SLIDE_STEP 5 /* 滑动事件最小步进 px */
#define TOUCH_LONG_PRESS_TIME 1000 /* 长按触发 ms */
#define TOUCH_REPEAT_INTERVAL 120 /* 长按重复间隔 ms */
#define TOUCH_STABLE_TIME 8
#define TOUCH_SLIDE_THRESH 12
#define TOUCH_SLIDE_STEP 5
#define TOUCH_LONG_PRESS_TIME 800
#define TOUCH_REPEAT_INTERVAL 120
/* ==================== 状态变量 ==================== */
static touch_state_t touch_state = TOUCH_IDLE;
static uint16_t down_x = 0;
static uint16_t down_y = 0;
static uint32_t touch_tick = 0;
static uint32_t repeat_tick = 0;
static uint8_t long_press_fired = 0;
static uint8_t click_sent = 0;
static uint16_t last_slide_x = 0;
static uint16_t last_slide_y = 0;
/* 取绝对值(避免引入 math.h */
static inline uint16_t abs_diff(int16_t a, int16_t b)
{
int16_t d = a - b;
return (d < 0) ? (uint16_t)(-d) : (uint16_t)d;
}
/* ==================== 重置 ==================== */
void app_touch_reset(void)
{
touch_state = TOUCH_IDLE;
down_x = down_y = 0;
touch_tick = repeat_tick = 0;
long_press_fired = 0;
click_sent = 0;
last_slide_x = last_slide_y = 0;
}
/* ==================== 手势识别 ==================== */
app_touch_result_t app_touch_process(uint16_t x, uint16_t y, uint8_t pressed)
{
app_touch_result_t r;
@ -68,22 +50,26 @@ app_touch_result_t app_touch_process(uint16_t x, uint16_t y, uint8_t pressed)
uint32_t now = rt_tick_get();
/* ---------- 抬起处理 ---------- */
if (!pressed) {
/* 若曾稳定按下且未触发长按 → 释放(可当作 TAP */
if (touch_state == TOUCH_PRESS_STABLE && !long_press_fired) {
if (!long_press_fired && !click_sent &&
(touch_state == TOUCH_PRESS_WAIT_STABLE ||
touch_state == TOUCH_PRESS_STABLE))
{
r.gesture = GESTURE_RELEASE;
r.x = down_x; r.y = down_y; /* 用按下点作为释放坐标 */
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;
down_x = down_y = 0;
last_slide_x = last_slide_y = 0;
touch_tick = repeat_tick = 0;
long_press_fired = 0;
click_sent = 0;
return r;
}
/* ---------- 按下处理 ---------- */
switch (touch_state)
{
case TOUCH_IDLE:
@ -91,44 +77,42 @@ app_touch_result_t app_touch_process(uint16_t x, uint16_t y, uint8_t pressed)
down_y = y;
touch_tick = now;
long_press_fired = 0;
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;
case TOUCH_PRESS_WAIT_STABLE:
if ((now - touch_tick) >= TOUCH_STABLE_TIME) {
/* 位移小 → 进入稳定 */
if (abs_diff(x, down_x) < 8 && abs_diff(y, down_y) < 8) {
if (abs_diff(x, down_x) < 24 && abs_diff(y, down_y) < 24)
touch_state = TOUCH_PRESS_STABLE;
last_slide_x = down_x;
last_slide_y = down_y;
else {
down_x = x;
down_y = y;
touch_tick = now;
MainTask_Sendmsg(MSG_ID_TOUCH, 1, down_x, down_y);
} else {
/* 位移大 → 视为误触,回到空闲 */
touch_state = TOUCH_IDLE;
}
}
break;
case TOUCH_PRESS_STABLE:
{
/* ----- 长按检测 ----- */
if (!long_press_fired && (now - touch_tick) >= TOUCH_LONG_PRESS_TIME) {
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;
}
/* ----- 长按重复 ----- */
if (long_press_fired && (now - repeat_tick) >= TOUCH_REPEAT_INTERVAL) {
repeat_tick = now;
r.gesture = GESTURE_REPEAT;
MainTask_Sendmsg(MSG_ID_TOUCH_LONG_REPEAT, 1, down_x, down_y);
return r;
}
/* ----- 位移进入滑动 ----- */
if (abs_diff(x, down_x) >= TOUCH_SLIDE_THRESH ||
abs_diff(y, down_y) >= TOUCH_SLIDE_THRESH) {
touch_state = TOUCH_SLIDING;
@ -136,15 +120,12 @@ app_touch_result_t app_touch_process(uint16_t x, uint16_t y, uint8_t pressed)
last_slide_y = y;
}
break;
}
case TOUCH_SLIDING:
{
/* 位移超过步进 → 产生拖动事件 */
if (abs_diff(x, last_slide_x) >= TOUCH_SLIDE_STEP ||
abs_diff(y, last_slide_y) >= TOUCH_SLIDE_STEP) {
r.gesture = GESTURE_DRAG;
r.down_x = last_slide_x; /* 记录上次位置,便于消费端算增量 */
r.down_x = last_slide_x;
r.down_y = last_slide_y;
MainTask_Sendmsg(MSG_ID_TOUCH, 1, x, y);
last_slide_x = x;
@ -152,12 +133,11 @@ app_touch_result_t app_touch_process(uint16_t x, uint16_t y, uint8_t pressed)
return r;
}
break;
}
default:
touch_state = TOUCH_IDLE;
break;
}
return r; /* 无手势 */
return r;
}

View File

@ -130,13 +130,17 @@ int main(void)
TM1617_GPIO_Init();
wk_delay_ms(1000);
/* 短暂等待总线稳定;默认跳转 App。
* KEY_MAIN_A USB
* KEY_NULL App Boot */
wk_delay_ms(50);
TM1617KeyValue = TM1617_ScanFkey();
if(TM1617KeyValue == 4)
if (TM1617KeyValue != KEY_MAIN_A)
{
JumpToApplication();
/* 若 App 向量无效则继续走升级模式 */
}
/* init spi1 function. */
BSP_MainPowerEnable(1);
wk_spi1_init();

View File

@ -52,37 +52,33 @@ Fader_t faders[4] = {
const Touch_AreaTypeDef Touch_Areas[] =
{
// Xmin, Xmax, Ymin, Ymax, flag,value ID, action
{ 0, 60, 25, 90, -1, 0, GUI_TRANSPOSE, NULL },
{ 60, 120, 25, 90, 1, 0, GUI_TRANSPOSE, NULL },
/* Xmin,Xmax,Ymin,Ymax, Flag, Value, ID, action — 坐标相对 240x320 */
{ 0, 60, 25, 95, -1, 0, GUI_TRANSPOSE, NULL },
{ 60, 120, 25, 95, 1, 0, GUI_TRANSPOSE, NULL },
{ 125, 180, 25, 90, -1, 0, GUI_SPEED, NULL },
{ 180, 240, 25, 90, 1, 0, GUI_SPEED, NULL },
{ 125, 180, 25, 95, -1, 0, GUI_SPEED, NULL },
{ 180, 239, 25, 95, 1, 0, GUI_SPEED, NULL },
{ 0, 80, 90, 120, 0, 0, GUI_AUTOBAND_SW, NULL },
{ 90, 170, 90, 120, 0, 1, GUI_AUTOBAND_SW, NULL },
{ 80, 140, 0, 30, 0, 0, GUI_BL_SW, NULL },
{ 0, 85, 95, 130, 0, 0, GUI_AUTOBAND_SW, NULL },
{ 90, 175, 95, 130, 0, 1, GUI_AUTOBAND_SW, NULL },
{ 0, 120, 120, 150, -1, 0, GUI_MODE_PARAM, NULL },
{120, 240, 120, 150, 1, 0, GUI_MODE_PARAM, NULL },
{ 0, 120, 130, 165, -1, 0, GUI_MODE_PARAM, NULL },
{ 120, 239, 130, 165, 1, 0, GUI_MODE_PARAM, NULL },
{ 10, 120, 125, 150, -1, 0, GUI_MODE_PARAM, NULL },
{ 80, 140, 0, 30, 0, 0, GUI_MODE_PARAM, NULL },
{ 0, 117, 155, 200, -1, 0, GUI_TIMBRE_SELECT, NULL },
{ 120, 240, 155, 200, 1, 0, GUI_TIMBRE_SELECT, NULL },
{ 10, 50, 275, 340, 0,2, GUI_TAB_INDEX, NULL },
{ 70, 130, 275, 340, 0,0, GUI_TAB_INDEX, NULL },
{ 130, 170, 275, 340, 0,1, GUI_TAB_INDEX, NULL },
{ 190, 230, 275, 340, 0,3, GUI_TAB_INDEX, NULL },
{ 10, 60, 240, 320, 0,0, GUI_PLAY_SECTION, NULL },
{ 60, 110, 240, 320, 0,1, GUI_PLAY_SECTION, NULL },
{ 110, 170, 240, 320, 0,2, GUI_PLAY_SECTION, NULL },
{ 170, 220, 240, 320, 0,3, GUI_PLAY_SECTION, NULL },
{ 0, 120, 165, 215, -1, 0, GUI_TIMBRE_SELECT, NULL },
{ 120, 239, 165, 215, 1, 0, GUI_TIMBRE_SELECT, NULL },
/* 演奏段落 1~4 */
{ 10, 60, 220, 270, 0, 0, GUI_PLAY_SECTION, NULL },
{ 60, 110, 220, 270, 0, 1, GUI_PLAY_SECTION, NULL },
{ 110, 165, 220, 270, 0, 2, GUI_PLAY_SECTION, NULL },
{ 165, 220, 220, 270, 0, 3, GUI_PLAY_SECTION, NULL },
/* 底栏 TAB万能/普通/专业/设置Value=目标 TAB */
{ 0, 60, 275, 319, 0, 0, GUI_TAB_INDEX, NULL },
{ 60, 120, 275, 319, 0, 2, GUI_TAB_INDEX, NULL },
{ 120, 180, 275, 319, 0, 1, GUI_TAB_INDEX, NULL },
{ 180, 239, 275, 319, 0, 3, GUI_TAB_INDEX, NULL },
};
int16_t MIC_VOL_MAP[10]= {-9000,-7000,-5000,-3000,-1000,1000,3000,5000,7000,9000};
@ -100,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);
}
}
@ -155,8 +161,9 @@ static uint32_t pwr_press_tick = 0;
bool powon = false;
/* ==================== 开机/关机动作 ==================== */
static void PowerOn(void)
void System_PowerOn(void)
{
LOG_I("PWR", "power_on begin");
/* 控制电源硬件 */
BSP_MainPowerEnable(1);
BSP_DreamCorePowerEnable(1);
@ -169,16 +176,26 @@ static void 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);
}
static void PowerOn(void)
{
System_PowerOn();
}
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();
@ -191,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);
@ -202,6 +220,20 @@ void Power_Key_Scan()
uint32_t now = rt_tick_get();
uint32_t holdtick = 0;
/* 复位/烧录后自动开机一次,避免黑屏干等长按;正常关机后下电再上电仍走此路径 */
if (powon == false && pwr_key_state == KEY_STATE_IDLE && pwr_press_tick == 0)
{
static uint8_t s_auto_poweron_pending = 1;
if (s_auto_poweron_pending)
{
s_auto_poweron_pending = 0;
LOG_I("PWR", "auto power_on");
PowerOn();
pwr_key_state = KEY_STATE_NONE; /* 忽略开机瞬间可能残留的按键电平 */
return;
}
}
switch(pwr_key_state)
{
case KEY_STATE_IDLE:
@ -224,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

@ -164,6 +164,8 @@ extern rt_timer_t OutTime_Stop_timer;
extern uint8_t KEY_ID_1629;
extern bool powon;
void Power_Key_Scan(void);
void System_PowerOn(void);
extern uint8_t Led;
extern bool PressFlag;
@ -194,8 +196,6 @@ extern void (*CurrUIProcress)(DisplayTaskMessage_Type xEvent);
void Stop_AutoBand();
void Power_Key_Scan();
static void ShowPowerOn();
void GET_ParamNum_Str(int16_t NUM, uint8_t ValidLen, char* Str, uint8_t hasSign);

View File

@ -19,23 +19,40 @@ void RGB_Y_TO_G()
#define UI_MODE_SCREEN_WIDTH 240
#define UI_MODE_SCREEN_HEIGHT 320
#define UI_MODE_BG_COLOR 0x1925
#define UI_MODE_LINE_COLOR DARKBLUE
#define UI_MODE_TEXT_COLOR 0xFFFF
#define UI_MODE_FONT_SIZE 16
/* 模式选择页焦点0=万能 1=普通 2=专业,默认选中万能(与规范一致) */
static uint8_t s_mode_select_focus = 0;
static void UI_DrawModeSelectButton(uint16_t y, const uint8_t *text,
const uint8_t *icon, uint8_t focused)
extern bool InModeFlag;
static void UI_ModeSelect_DrawButtons(void);
static void UI_ModeSelect_SetFocus(uint8_t idx)
{
if (idx > 2 || idx == s_mode_select_focus)
return;
s_mode_select_focus = idx;
UI_ModeSelect_DrawButtons();
}
void UI_ReturnToModeSelect(void)
{
StartFlag = 0;
InModeFlag = false;
AutoBandTop1_Stop();
s_mode_select_focus = 0;
CallUI_Idle();
UI_DrawModeSelectScreen();
}
/* 0831 美工试用图:图标+MiSans 文字整行位图 */
static void UI_DrawModeSelectButton(uint16_t y, const uint8_t *row, uint8_t focused)
{
const uint16_t btn_w = 220;
const uint16_t btn_h = 87;
const uint16_t btn_r = 10;
const uint16_t btn_x = (240 - btn_w) / 2;
uint16_t text_y = y + (btn_h - UI_MODE_FONT_SIZE) / 2;
uint16_t text_x;
const uint16_t row_x = (uint16_t)(btn_x + (btn_w - UI_MODE0831_ROW_W) / 2);
const uint16_t row_y = (uint16_t)(y + (btn_h - UI_MODE0831_ROW_H) / 2);
if (focused)
{
@ -47,47 +64,37 @@ static void UI_DrawModeSelectButton(uint16_t y, const uint8_t *text,
LCD_FillRoundRect(btn_x, y, btn_w, btn_h, btn_r, COLOR_NORMAL_BG);
}
/* 叠加绘制文字,避免在渐变底上打出实心色块 */
text_x = LCD_ShowChinese_AutoAlign(
btn_x, btn_x + btn_w,
text_y,
text,
LCD_ALIGN_CENTER,
UI_MODE_TEXT_COLOR,
BLACK,
1,
UI_MODE_FONT_SIZE
);
LCD_WR_PIC_Trans(text_x - 45, text_y - 5, 40, 30, icon, BLACK);
LCD_WR_PIC_Trans(row_x, row_y, UI_MODE0831_ROW_W, UI_MODE0831_ROW_H, row, BLACK);
}
static void UI_ModeSelect_DrawButtons(void)
{
UI_DrawModeSelectButton(40,
gImage_Mode0831_Universal_Row_180_52,
s_mode_select_focus == 0);
UI_DrawModeSelectButton(132,
gImage_Mode0831_Normal_Row_180_52,
s_mode_select_focus == 1);
UI_DrawModeSelectButton(224,
gImage_Mode0831_Expert_Row_180_52,
s_mode_select_focus == 2);
}
void UI_DrawModeSelectScreen(void)
{
UI_ClearFocus();
s_mode_select_focus = 0;
pCurrentTouch_Area = Touch_ModeSelect_Areas;
LCD_FillByColor(0, 0, 240, 320, BLACK);
label_top();
UI_DrawModeSelectButton(40,
(const uint8_t *)"\xCD\xF2\xC4\xDC\xC4\xA3\xCA\xBD",
gImage_FreeMode_Icon_40_30,
s_mode_select_focus == 0);
UI_DrawModeSelectButton(132,
(const uint8_t *)"\xC6\xD5\xCD\xA8\xC4\xA3\xCA\xBD",
gImage_NormalMode_Icon_40_30,
s_mode_select_focus == 1);
UI_DrawModeSelectButton(224,
(const uint8_t *)"\xD7\xA8\xD2\xB5\xC4\xA3\xCA\xBD",
gImage_ExpressMode_Icon_40_30,
s_mode_select_focus == 2);
UI_ModeSelect_DrawButtons();
}
const Touch_AreaTypeDef Touch_ModeSelect_Areas[] = {
// Xmin, Xmax, Ymin, Ymax, flag, ID, action
{ 10, 229, 40, 126, 0, 0, GUI_MODE_SELECT3, NULL }, //<2F><><EFBFBD><EFBFBD>
{ 10, 229, 132, 218, 1, 0, GUI_MODE_SELECT1, NULL }, //????/<2F><><EFBFBD><EFBFBD>
{ 10, 229, 224, 310, 2, 0, GUI_MODE_SELECT2, NULL }, //רҵ/ר???
/* Flag=按钮序号(0万能/1普通/2专业)ID=进入 TAB */
{ 0, 239, 38, 127, 0, 0, 0, NULL },
{ 0, 239, 128, 219, 1, 0, 2, NULL },
{ 0, 239, 220, 311, 2, 0, 1, NULL },
};
#define TOUCH_AREA_MODE_SELECT_NUM (sizeof(Touch_ModeSelect_Areas)/sizeof(Touch_AreaTypeDef))
@ -130,44 +137,36 @@ void LoadConfig()
void Touch_Mode_Select_Action(int8_t FLAG, uint8_t ID, uint8_t areaIndex)
{
(void)areaIndex;
StartTask();
s_mode_select_focus = (uint8_t)FLAG;
/* ID 为 TAB 索引0=万能(Song) 1=专业(Expert) 2=普通(Free) */
mGuiData[GUI_TAB_INDEX].Current = ID;
InModeFlag = true;
// <20>ٽ<EFBFBD><D9BD><EFBFBD><EFBFBD>ٽ<EFBFBD><D9BD><EFBFBD>
switch (ID)
{
case 0:
case 0: /* 万能 */
if (mGuiData[GUI_AUTOBAND_SW].Current)
{
ADDRESS = 0x0009EB5F;
}
else
{
ADDRESS = 0x0001B8F0;
}
MenuPage_SongMode_Proc();
break;
case 1:
case 1: /* 专业 */
if (mGuiData[GUI_AUTOBAND_SW].Current)
{
ADDRESS = 0x0009EB5F;
}
else
{
// ADDRESS = 0x0001B8F0;
ADDRESS = 0x00000000;
}
MenuPage_ExpertMode_Proc();
break;
case 2:
case 2: /* 普通 */
ADDRESS = 0x0009598F;
MenuPage_FreeMode_Proc();
break;
default:
break;
}
}
extern uint8_t bat_init_done;
@ -181,7 +180,8 @@ void IdleProcess(DisplayTaskMessage_Type msg)
LCD_FillByColor(0, 0, 240, 320, WHITE);
LCD_WR_PIC_FROM_FLASH(40, 80, 168, 155, 0x00000000);
BSP_SelectAdcChannel(6);
wk_delay_ms(1000);
/* 让出 CPU避免忙等卡住触摸/主循环线程 */
rt_thread_mdelay(1000);
LoadConfig();
vol = ADC_ReadChannel(ADC_CHANNEL_1)>>5;
if (vol <= 5) level = 0;
@ -192,6 +192,7 @@ void IdleProcess(DisplayTaskMessage_Type msg)
else level = 5;
Send_volume(vol);
s_mode_select_focus = 0;
UI_DrawModeSelectScreen();
TM1629D_AllLedOn(LED_COLOR_G);
TM1629D_UpdateDisplay(0);
@ -210,15 +211,34 @@ 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 >= 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 )
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 )
{
Touch_Mode_Select_Action(pCurrentTouch_Area[k].Flag,pCurrentTouch_Area[k].ID,k);
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
UI_ModeSelect_SetFocus(row);
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:
/* 模式页暂无长按动作 */
break;
case MSG_ID_BATTERY_VALUE:

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:
@ -417,6 +428,12 @@ void UI_SongMode_Process(DisplayTaskMessage_Type msg)
break;
case MSG_ID_TOUCH_LONG_REPEAT:
{
/* 长按顶栏y<28返回三键模式选择页 */
if (msg.LoByte < 28)
{
UI_ReturnToModeSelect();
break;
}
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 &&

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;
@ -156,7 +172,7 @@ void label_top()
const uint16_t btn_color = /*DARKLGRAY*/0x2988; // 深灰<E6B7B1>?????
//LCD_FillByColor(0,0,240,25,BLACK);
//LCD_WR_PIC(5,0,12,20,gImage_bar);
Draw_Volume_Bar(5, 5, level, 1, WHITE, GRAY);
Draw_Volume_Bar(8, 5, level, 1, WHITE, GRAY);
LCD_DrawLine(0, 26, 240, 26, btn_color);
Show_BL_Icon(mGuiData[GUI_BL_SW].Current);
Show_Battery_Icon(usb_charging_state);

View File

@ -28,9 +28,11 @@ void CallUI_RestoreSelect(void);
#define UI_STATUS_BAR_H 26
#define UI_STATUS_BATTERY_X 180
/* 电量组在 Draw_Battery_Icon 内按右对齐重算;此值为允许的最左起点 */
#define UI_STATUS_BATTERY_X 150
#define UI_STATUS_BATTERY_H 11
#define UI_STATUS_BATTERY_Y ((UI_STATUS_BAR_H - UI_STATUS_BATTERY_H) / 2)
#define UI_STATUS_BATTERY_RIGHT_MARGIN 8
#define UI_FOCUS_NONE 0xFF
void UI_ClearFocus(void);

View File

@ -9,7 +9,7 @@ typedef enum {
extern const Touch_AreaTypeDef Touch_ModeSelect_Areas[];
extern void UI_DrawModeSelectScreen(void);
extern void UI_ReturnToModeSelect(void);
extern void IdleProcess(DisplayTaskMessage_Type xEvent);

View File

@ -150,14 +150,13 @@ void LCD_WR_PIC(uint16_t x, uint16_t y, uint16_t length, uint16_t width, const u
}
}
/* 跳过 key_color 像素,用于图标透明底(常见 key=BLACK */
/* 跳过近黑色像素(图标导出底色常为 0x0000/0x0001/0x0022非纯 BLACK */
void LCD_WR_PIC_Trans(uint16_t x, uint16_t y, uint16_t length, uint16_t width,
const uint8_t data[], uint16_t key_color)
{
uint32_t total_pixels = (uint32_t)length * width;
uint8_t key_h = (uint8_t)(key_color >> 8);
uint8_t key_l = (uint8_t)(key_color & 0xFF);
uint32_t i;
(void)key_color;
if (total_pixels == 0)
return;
@ -166,11 +165,18 @@ void LCD_WR_PIC_Trans(uint16_t x, uint16_t y, uint16_t length, uint16_t width,
{
uint8_t hi = data[i * 2];
uint8_t lo = data[i * 2 + 1];
if (hi == key_h && lo == key_l)
uint16_t c = (uint16_t)((hi << 8) | lo);
uint8_t r5 = (uint8_t)((c >> 11) & 0x1F);
uint8_t g6 = (uint8_t)((c >> 5) & 0x3F);
uint8_t b5 = (uint8_t)(c & 0x1F);
/* 近黑视为透明,避免图标黑底方块 */
if (r5 <= 1 && g6 <= 2 && b5 <= 2)
continue;
LCD_DrawPoint((uint16_t)(x + (i % length)),
(uint16_t)(y + (i / length)),
(uint16_t)((hi << 8) | lo));
c);
}
}
@ -1794,11 +1800,14 @@ void Draw_Battery_Icon(uint16_t x, uint16_t y, uint8_t battery_percent,uint8_t s
uint8_t round_r = 2 * scale;
uint8_t stroke = 1 * scale;
uint16_t fill_gap = 2 * scale;
uint8_t text_size = 16 * scale; /* 16px ASCII 字体渲染可靠13px 位图与驱动不匹配会乱码 */
/* PDF 标注 7.5,但 8px ASCII 位图乱码;用 16px 并整组右对齐,避免左右被裁 */
uint8_t text_size = 16 * scale;
uint8_t status_bar_h = 26 * scale;
uint8_t Info[40];
uint16_t pole_w = 2 * scale;
uint16_t pole_h = 5 * scale;
uint16_t gap = 6 * scale;
uint16_t right_margin = 14 * scale;
uint16_t pole_x;
uint16_t pole_y;
uint16_t inner_w;
@ -1806,6 +1815,9 @@ void Draw_Battery_Icon(uint16_t x, uint16_t y, uint8_t battery_percent,uint8_t s
uint16_t fill_w;
uint16_t text_x;
uint16_t text_y;
uint16_t text_w;
uint16_t group_w;
uint16_t bat_x = x;
uint16_t clear_h;
if(battery_percent > 100)
@ -1813,18 +1825,26 @@ void Draw_Battery_Icon(uint16_t x, uint16_t y, uint8_t battery_percent,uint8_t s
battery_percent = 100;
}
sprintf((char*)Info, "%d%%", battery_percent);
text_w = (uint16_t)(strlen((char*)Info) * (text_size / 2));
group_w = (uint16_t)(bat_w + pole_w + gap + text_w);
if (group_w + right_margin < 240)
bat_x = (uint16_t)(240 - right_margin - group_w);
if (bat_x < x)
bat_x = x;
clear_h = (bat_h > text_size) ? bat_h : text_size;
if(status_bar_h > clear_h)
{
clear_h = status_bar_h;
}
LCD_FillByColor(x, 0, 240, clear_h, BLACK);
LCD_FillByColor(bat_x > 4 ? (uint16_t)(bat_x - 4) : 0, 0, 240, clear_h, BLACK);
/* 圆角外壳:外框 + 内挖空 */
LCD_FillRoundRect(x, y, bat_w, bat_h, round_r, border_color);
LCD_FillRoundRect(bat_x, y, bat_w, bat_h, round_r, border_color);
if(bat_w > 2 * stroke && bat_h > 2 * stroke)
{
LCD_FillRoundRect(x + stroke, y + stroke,
LCD_FillRoundRect(bat_x + stroke, y + stroke,
bat_w - 2 * stroke, bat_h - 2 * stroke,
(round_r > stroke) ? (round_r - stroke) : 0,
bg_color);
@ -1834,26 +1854,25 @@ void Draw_Battery_Icon(uint16_t x, uint16_t y, uint8_t battery_percent,uint8_t s
inner_h = bat_h - 2 * fill_gap;
fill_w = inner_w * battery_percent / 100;
/* 从左向右实心填充,宽度与百分比一致 */
if(battery_percent > 0 && fill_w > 0)
{
if(fill_w > inner_w)
{
fill_w = inner_w;
}
LCD_FillRoundRect(x + fill_gap, y + fill_gap,
LCD_FillRoundRect(bat_x + fill_gap, y + fill_gap,
fill_w, inner_h,
1 * scale, fill_color);
}
/* 右侧正极小凸起 */
pole_x = x + bat_w;
pole_x = bat_x + bat_w;
pole_y = y + (bat_h - pole_h) / 2;
LCD_FillRoundRect(pole_x, pole_y, pole_w, pole_h, 1 * scale, border_color);
sprintf((char*)Info, "%d%%", battery_percent);
text_x = pole_x + pole_w + 2 * scale;
text_x = (uint16_t)(pole_x + pole_w + gap);
text_y = (status_bar_h - text_size) / 2;
if ((int16_t)text_y < 0)
text_y = 0;
LCD_ShowString(text_x, text_y, Info, WHITE, BLACK, text_size, 0);
}

View File

@ -7,9 +7,11 @@
#define LCD_DUMP_RTT_UP_CH 1
#define LCD_DUMP_RTT_DOWN_CH 0
#define LCD_DUMP_CMD "DUMP"
#define LCD_DUMP_TAP_CMD "TAP"
#define LCD_DUMP_RESET_CMD "sys reset"
#define LCD_DUMP_MAGIC "SCRN"
#define LCD_DUMP_FMT_RGB565 1
#define LCD_DUMP_CMD_BUF_SIZE 16
#define LCD_DUMP_CMD_BUF_SIZE 32
#pragma pack(1)
typedef struct {
@ -197,6 +199,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;
@ -208,6 +218,22 @@ static uint8_t LCD_Dump_CommandPending(void)
s_cmd_buf[s_cmd_len++] = c;
s_cmd_buf[s_cmd_len] = '\0';
/* 合成点击:验证 UI 命中路径(点万能按钮中心) */
if (strstr(s_cmd_buf, LCD_DUMP_TAP_CMD) != NULL) {
s_cmd_len = 0U;
s_cmd_buf[0] = '\0';
MainTask_Sendmsg(MSG_ID_TOUCH, 1, 120, 80);
SEGGER_RTT_WriteString(0, "TAP inject 120,80\n");
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) {
s_cmd_len = 0U;
s_cmd_buf[0] = '\0';

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
#ifndef __DRV_ILI9341_LCD_IMAGE_MODESELECT0831_H
#define __DRV_ILI9341_LCD_IMAGE_MODESELECT0831_H
#include "stdint.h"
#define UI_MODE0831_ROW_W 180
#define UI_MODE0831_ROW_H 52
extern const unsigned char gImage_Mode0831_Universal_Row_180_52[];
extern const unsigned char gImage_Mode0831_Normal_Row_180_52[];
extern const unsigned char gImage_Mode0831_Expert_Row_180_52[];
#endif

View File

@ -97,110 +97,156 @@ uint16_t TP_Read_AD(uint8_t cmd)
uint8_t i;
uint16_t adc_val = 0;
TP_CS_Clr(); // 片选拉低选中XPT2046
Delay_us(2); // 片选稳定时间
TP_Write_Byte(cmd); // MCU通过DIN发送命令XPT2046的DIN输入接收
// 等待XPT2046完成AD转换约6us转换期间OUT无有效数据
TP_CS_Clr();
Delay_us(2);
TP_Write_Byte(cmd);
Delay_us(6);
// 读取16位数据XPT2046通过自身OUT输出数据MCU通过OUT输入读取
for (i = 0; i < 16; i++)
{
adc_val <<= 1; // 左移,准备接收下一位
// 时钟上升沿XPT2046更新OUT引脚输出下降沿MCU读取OUT输入
adc_val <<= 1;
TP_CLK_Set();
Delay_us(1);
TP_CLK_Clr();
Delay_us(1);
// MCU读取OUT引脚的输入数据高12位有效低4位丢弃
if (TP_OUT_Read())
{
adc_val |= 0x01;
}
TP_CS_Set();
return adc_val >> 4;
}
TP_CS_Set(); // 释放片选
return adc_val >> 4; // 取高12位有效数据
}
// 简单去抖动读取连续n次相同状态视为有效
uint8_t TP_CheckPressed(uint8_t n)
{
uint8_t count = 0;
uint8_t i;
for (uint8_t i = 0; i < n; i++)
for (i = 0; i < n; i++)
{
if (TP_IRQ_Read() == 0)
{ // IRQ低电平表示按下
count++;
}
Delay_us(100);
}
return (count >= n/2) ? 1 : 0;
return (count >= (n / 2)) ? 1 : 0;
}
static uint16_t TP_SampleAxis(uint8_t cmd)
{
(void)TP_Read_AD(cmd);
Delay_us(15);
return TP_Read_AD(cmd);
}
// 读取原始坐标(未校准)
TP_Point TP_ReadRaw(void)
{
TP_Point p = {0, 0, 0};
if (!TP_CheckPressed(3))
{ // 检查触摸状态
return p;
}
// 多次读取取平均值,减少噪声
uint32_t x_sum = 0, y_sum = 0;
uint8_t i;
static uint8_t s_raw_logged;
for (uint8_t i = 0; i < 5; i++)
{
x_sum += TP_Read_AD(XPT2046_READ_X);
y_sum += TP_Read_AD(XPT2046_READ_Y);
}
p.x = x_sum / 5;
p.y = y_sum / 5;
p.pressed = 1;
if (!TP_CheckPressed(3)) {
s_raw_logged = 0U;
return p;
}
// 校准参数需实际触摸四个角后修改示例为240x320屏
#define CALIB_X_MIN 200 // 最小X原始值
#define CALIB_X_MAX 3800 // 最大X原始值
#define CALIB_Y_MIN 200 // 最小Y原始值
#define CALIB_Y_MAX 3800 // 最大Y原始值
#define LCD_WIDTH 240 // LCD宽度
#define LCD_HEIGHT 320 // LCD高度
for (i = 0; i < 5; i++)
{
x_sum += TP_SampleAxis(XPT2046_READ_X);
Delay_us(15);
y_sum += TP_SampleAxis(XPT2046_READ_Y);
Delay_us(15);
}
p.x = (uint16_t)(x_sum / 5);
p.y = (uint16_t)(y_sum / 5);
/* Y 通道常为 0 时,交换 X/Y 命令再采一次 */
if (p.y < 80u && p.x > 80u)
{
uint32_t sx = 0, sy = 0;
for (i = 0; i < 3; i++)
{
sx += TP_SampleAxis(XPT2046_READ_Y);
Delay_us(15);
sy += TP_SampleAxis(XPT2046_READ_X);
Delay_us(15);
}
if (sy / 3u > p.y)
{
p.x = (uint16_t)(sx / 3);
p.y = (uint16_t)(sy / 3);
}
}
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;
}
#define CALIB_X_MIN 200
#define CALIB_X_MAX 3800
#define CALIB_Y_MIN 200
#define CALIB_Y_MAX 3800
/* 竖屏:多数 ILI9341+XPT2046 板级需 XY 对调X 镜像与历史 240-tp.x 一致 */
#define TP_SWAP_XY 1
#define TP_MIRROR_X 1
#define TP_MIRROR_Y 0
static uint16_t TP_MapAxis(uint16_t adc, uint16_t adc_min, uint16_t adc_max, uint16_t px_max)
{
int32_t v = (int32_t)adc;
int32_t a0 = (int32_t)adc_min;
int32_t a1 = (int32_t)adc_max;
int32_t span = a1 - a0;
int32_t px;
if (span <= 0)
return 0;
if (v < a0)
v = a0;
if (v > a1)
v = a1;
px = (v - a0) * (int32_t)px_max / span;
if (px < 0)
px = 0;
if (px > (int32_t)px_max)
px = (int32_t)px_max;
return (uint16_t)px;
}
// 校准后获取LCD坐标
TP_Point TP_Read(void)
{
// char Info[40];
TP_Point raw = TP_ReadRaw();
// sprintf (Info, "(x=%03d y=%03d) ", raw.x, raw.y);
// LCD_ShowString(2, 176+16, (const uint8_t*)Info, RED, WHITE, 16, 0);
TP_Point calib = {0, 0, raw.pressed};
uint16_t ax, ay;
if (!raw.pressed) {
if (!raw.pressed)
return calib;
}
// 线性映射将原始ADC值转换为LCD像素坐标
calib.x = (raw.x - CALIB_X_MIN) * LCD_WIDTH / (CALIB_X_MAX - CALIB_X_MIN);
calib.y = (raw.y - CALIB_Y_MIN) * LCD_HEIGHT / (CALIB_Y_MAX - CALIB_Y_MIN);
#if TP_SWAP_XY
ax = TP_MapAxis(raw.y, CALIB_Y_MIN, CALIB_Y_MAX, (uint16_t)(LCD_W - 1));
ay = TP_MapAxis(raw.x, CALIB_X_MIN, CALIB_X_MAX, (uint16_t)(LCD_H - 1));
#else
ax = TP_MapAxis(raw.x, CALIB_X_MIN, CALIB_X_MAX, (uint16_t)(LCD_W - 1));
ay = TP_MapAxis(raw.y, CALIB_Y_MIN, CALIB_Y_MAX, (uint16_t)(LCD_H - 1));
#endif
// 边界限制
if (calib.x > LCD_WIDTH - 1) calib.x = LCD_WIDTH - 1;
if (calib.x == 0) calib.x = 0;
if (calib.y > LCD_HEIGHT - 1) calib.y = LCD_HEIGHT - 1;
if (calib.y == 0) calib.y = 0;
#if TP_MIRROR_X
ax = (uint16_t)(LCD_W - 1 - ax);
#endif
#if TP_MIRROR_Y
ay = (uint16_t)(LCD_H - 1 - ay);
#endif
calib.x = ax;
calib.y = ay;
return calib;
}

View File

@ -33,11 +33,11 @@
#define TP_DIN_Clr() gpio_bits_reset(TP_DIN_PORT, TP_DIN_PIN) // DIN输出低MCU→XPT2046
#define TP_DIN_Set() gpio_bits_set(TP_DIN_PORT, TP_DIN_PIN) // DIN输出高MCU→XPT2046
// XPT2046命令定义
#define XPT2046_READ_X 0xD0 // 读X坐标命令MCU通过DIN发送给XPT2046
#define XPT2046_READ_Y 0x90 // 读Y坐标命令
#define XPT2046_READ_Z1 0xB0 // 读压力Z1
#define XPT2046_READ_Z2 0xC0 // 读压力Z2
// XPT2046命令(与 BOOT 工程一致)
#define XPT2046_READ_X 0xD0
#define XPT2046_READ_Y 0x90
#define XPT2046_READ_Z1 0xB0
#define XPT2046_READ_Z2 0xC0
// 触摸状态结构体
typedef struct

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>
@ -2078,6 +2084,12 @@
<file>
<name>$PROJ_DIR$\..\..\device\LCD_ILI9341\Drv_ILI9341_Lcd_Image.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\..\device\LCD_ILI9341\Drv_ILI9341_Lcd_Image_ModeSelect0831.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\..\device\LCD_ILI9341\Drv_ILI9341_Lcd_Image_ModeSelect0831.h</name>
</file>
<file>
<name>$PROJ_DIR$\..\..\device\LCD_ILI9341\Drv_ILI9341_Lcd_Image.h</name>
</file>

View File

@ -35,6 +35,7 @@
#include "Drv_ILI9341_Lcd_Init.h"
#include "Drv_ILI9341_Lcd_App.h"
#include "Drv_ILI9341_Lcd_Image.h"
#include "Drv_ILI9341_Lcd_Image_ModeSelect0831.h"
#include "Drv_ILI9341_Lcd_Dump.h"
#include "Drv_XPT2046_Touch.h"
@ -75,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

@ -44,13 +44,18 @@ void TaskUIThread_entry(void* parameter)
void TaskTouchThread_entry(void* parameter)
{
static uint8_t s_last_pressed = 0;
while(1)
{
TP_Point tp = TP_Read(); /* 读 XPT2046 触摸坐标 */
app_touch_process(240-tp.x, tp.y, tp.pressed);
LCD_Dump_Poll();
TP_Point tp = TP_Read(); /* 已含轴交换/镜像,直接进手势 */
if (tp.pressed && !s_last_pressed)
{
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);
Read_Cur_Measure();
rt_thread_mdelay(10);
rt_thread_mdelay(8);
}
}
@ -146,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,
@ -220,37 +225,121 @@ rt_err_t TaskInit(void)
void StartTask(void)
{
rt_thread_startup(&TaskScanThread);
/* 扫描任务可能已由 StartScanTask 拉起;重复 startup 会被 RT-Thread 忽略 */
StartScanTask();
if ((TaskBTHandleThread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_CLOSE)
{
rt_thread_init(&TaskBTHandleThread,
"TaskBTHandleThread",
TaskBTHandleThread_entry,
RT_NULL,
&TaskBTTHandlehread_Stack,
sizeof(TaskBTTHandlehread_Stack),
5,
20);
}
if ((TaskBTHandleThread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT)
rt_thread_startup(&TaskBTHandleThread);
if ((TaskBTRecvThread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_CLOSE)
{
rt_thread_init(&TaskBTRecvThread,
"TaskBTRecvThread",
TaskBTRecvThread_entry,
RT_NULL,
&TaskBTRecvThread_Stack,
sizeof(TaskBTRecvThread_Stack),
5,
20);
}
if ((TaskBTRecvThread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT)
rt_thread_startup(&TaskBTRecvThread);
if ((TaskAutobandThread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_CLOSE)
{
rt_thread_init(&TaskAutobandThread,
"TaskAutobandThread",
TaskAutobandThread_entry,
RT_NULL,
&TaskAutobandThread_Stack,
sizeof(TaskAutobandThread_Stack),
4,
20);
}
if ((TaskAutobandThread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT)
rt_thread_startup(&TaskAutobandThread);
}
void StartTouchTask(void)
{
rt_uint8_t st = (rt_uint8_t)(TaskTouchThread.stat & RT_THREAD_STAT_MASK);
if (st == RT_THREAD_CLOSE)
{
rt_thread_init(&TaskTouchThread,
"TaskTouchThread",
TaskTouchThread_entry,
RT_NULL,
&TaskTouchThread_Stack,
sizeof(TaskTouchThread_Stack),
5,
20);
st = RT_THREAD_INIT;
}
if (st == RT_THREAD_INIT)
rt_thread_startup(&TaskTouchThread);
}
void StartScanTask(void)
{
rt_uint8_t st = (rt_uint8_t)(TaskScanThread.stat & RT_THREAD_STAT_MASK);
if (st == RT_THREAD_CLOSE)
{
rt_thread_init(&TaskScanThread,
"TaskScanThread",
TaskScanThread_entry,
RT_NULL,
&TaskScanThread_Stack,
sizeof(TaskScanThread_Stack),
5,
20);
st = RT_THREAD_INIT;
}
if (st == RT_THREAD_INIT)
rt_thread_startup(&TaskScanThread);
}
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");
}
static void OutTime_Stop_timer_callback(void *parameter)

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 ****************************/

79
tools/LOG_README.md Normal file
View File

@ -0,0 +1,79 @@
# 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 以上 |
| `sys reset` | MCU 软件复位(自动重新开机,**RAM 日志会丢失** |
或通过脚本(**复位后脚本会自动 `go()` 释放 CPU避免 J-Link 停核黑屏**
```bash
python rtt_reset.py
```
若固件较旧无 `sys reset`,脚本会自动用 J-Link 硬件复位兜底。
## 日志格式
```
[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`
- 远程复位:`python rtt_reset.py`
## 触摸问题排查要点
导出后重点 grep
- `[TP]` — raw/cal 坐标、irq 状态
- `[MSG] id=12` — 触摸消息是否发出
- `[UI] mode_select miss` — UI 命中测试是否失败
## 已验证2026-08-31
烧录:`cspybat` + `C:\Temp\k1flash\YNGJ-GT1-M.out` 下载J-Link reset/run 后约 4s 开机。
日志导出示例(`python rtt_log_dump.py --out test.log`
```
[00185][I][BOOT] app_log init boot=1
[00224][I][NVM ] init ok ver=2
[00224][I][BOOT] main init done ui=Idle
[00224][I][PWR ] auto power_on
[00401][I][PWR ] periph ready send POWER_ON
[00500][I][MSG ] id=0 hi=0 lo=0
```

View File

@ -1,7 +1,5 @@
@echo off
REM Flash YNGJ-GT1-M app via IAR cspybat (AT32 flash loader).
REM Do NOT pass Commander-style .jlink files to --jlink_script_file.
REM Flash YNGJ-GT1-M app via IAR cspybat, then reset+run and wait for UI boot.
setlocal
set ROOT=%~dp0..
set OUT=%ROOT%\project\IAR_V7.4\YNGJ-GT1-M\Exe\YNGJ-GT1-M.out
@ -9,17 +7,29 @@ set GEN=%ROOT%\project\IAR_V7.4\settings\YNGJ-GT1-M.YNGJ-GT1-M.general.xcl
set DRV=%ROOT%\project\IAR_V7.4\settings\YNGJ-GT1-M.YNGJ-GT1-M.driver.xcl
set CSPY="C:\Program Files (x86)\IAR Systems\Embedded Workbench 7.3\common\bin\cspybat"
set STAGE=C:\Temp\k1flash
set JLINK="C:\Program Files\SEGGER\JLink_V818\JLink.exe"
if not exist "%OUT%" (
echo ERROR: missing %OUT% - build first
exit /b 1
)
REM Avoid stale debugger locking SWD (cspybat can hang otherwise)
taskkill /F /IM cspybat.exe >nul 2>&1
mkdir "%STAGE%" 2>nul
copy /Y "%OUT%" "%STAGE%\YNGJ-GT1-M.out" >nul
echo [1/3] Downloading (may take ~60s)...
%CSPY% -f "%GEN%" "--debug_file=%STAGE%\YNGJ-GT1-M.out" --download_only --backend -f "%DRV%"
if errorlevel 1 exit /b 1
"C:\Program Files\SEGGER\JLink_V818\JLink.exe" -CommandFile "%~dp0reset_run.jlink"
exit /b %ERRORLEVEL%
echo [2/3] Reset and run...
%JLINK% -CommandFile "%~dp0reset_run.jlink"
if errorlevel 1 exit /b 1
echo [3/3] Wait for auto PowerOn + logo (about 4s)...
timeout /t 4 /nobreak >nul
echo Done. UI should be on mode-select screen.
echo Log dump: python rtt_log_dump.py --out problem.log
exit /b 0

View File

@ -0,0 +1,129 @@
#!/usr/bin/env python3
"""Convert Doc/UI 0831 trial PNG slices to RGB565 C arrays for mode-select."""
from PIL import Image, ImageDraw
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SRC = ROOT / "tools" / "assets_0831"
OUT_C = ROOT / "device" / "LCD_ILI9341" / "Drv_ILI9341_Lcd_Image_ModeSelect0831.c"
OUT_H = ROOT / "device" / "LCD_ILI9341" / "Drv_ILI9341_Lcd_Image_ModeSelect0831.h"
PREV = SRC / "preview_mode_select.png"
# Only emit composite rows (icon+MiSans text) to save Flash.
ROW_W, ROW_H = 180, 52
NAMES = [
("Universal", "mode_row_0.png"), # 万能
("Normal", "mode_row_1.png"), # 普通
("Expert", "mode_row_2.png"), # 专业
]
def rgba_to_rgb565_bytes(im: Image.Image) -> bytes:
im = im.convert("RGBA")
px = im.load()
w, h = im.size
out = bytearray(w * h * 2)
i = 0
for y in range(h):
for x in range(w):
r, g, b, a = px[x, y]
if a < 40 or (r < 12 and g < 12 and b < 12):
out[i] = 0
out[i + 1] = 0
else:
r = (r * a) // 255
g = (g * a) // 255
b = (b * a) // 255
c = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3)
out[i] = (c >> 8) & 0xFF
out[i + 1] = c & 0xFF
i += 2
return bytes(out)
def fit_on_canvas(src_im: Image.Image, cw: int, ch: int) -> Image.Image:
im = src_im.convert("RGBA")
bbox = im.getbbox()
if bbox:
im = im.crop(bbox)
sw, sh = im.size
scale = min(cw / sw, ch / sh)
nw = max(1, int(round(sw * scale)))
nh = max(1, int(round(sh * scale)))
im = im.resize((nw, nh), Image.Resampling.LANCZOS)
canvas = Image.new("RGBA", (cw, ch), (0, 0, 0, 255))
ox = (cw - nw) // 2
oy = (ch - nh) // 2
canvas.paste(im, (ox, oy), im)
return canvas
def emit_array(name: str, data: bytes, w: int, h: int, lines: list) -> None:
lines.append(f"const unsigned char {name}[{len(data)}] = {{ /* {w}x{h} RGB565 BE */")
for i in range(0, len(data), 16):
chunk = data[i : i + 16]
hexes = ",".join(f"0x{b:02X}" for b in chunk)
lines.append(hexes + ",")
lines.append("};")
lines.append("")
def main() -> None:
c_lines = [
'#include "Drv_ILI9341_Lcd_Image_ModeSelect0831.h"',
"",
"/* Auto-generated from Doc/UI 0831 trial PNG. Do not hand-edit. */",
"",
]
h_lines = [
"#ifndef __DRV_ILI9341_LCD_IMAGE_MODESELECT0831_H",
"#define __DRV_ILI9341_LCD_IMAGE_MODESELECT0831_H",
"",
'#include "stdint.h"',
"",
f"#define UI_MODE0831_ROW_W {ROW_W}",
f"#define UI_MODE0831_ROW_H {ROW_H}",
"",
]
screen = Image.new("RGBA", (240, 320), (0, 0, 0, 255))
d = ImageDraw.Draw(screen)
d.rectangle([0, 0, 239, 27], fill=(20, 28, 40, 255))
btn_ys = [40, 132, 224]
btn_w, btn_h, btn_r = 220, 87, 10
total = 0
for idx, (tag, row_n) in enumerate(NAMES):
row = fit_on_canvas(Image.open(SRC / row_n), ROW_W, ROW_H)
row.save(SRC / f"gen_{tag.lower()}_row.png")
rdata = rgba_to_rgb565_bytes(row)
total += len(rdata)
rname = f"gImage_Mode0831_{tag}_Row_{ROW_W}_{ROW_H}"
emit_array(rname, rdata, ROW_W, ROW_H, c_lines)
h_lines.append(f"extern const unsigned char {rname}[];")
h_lines.append("")
by = btn_ys[idx]
bx = (240 - btn_w) // 2
fill = (47, 54, 71, 255) if idx == 0 else (25, 32, 45, 255)
d.rounded_rectangle([bx, by, bx + btn_w - 1, by + btn_h - 1], radius=btn_r, fill=fill)
rx = bx + (btn_w - ROW_W) // 2
ry = by + (btn_h - ROW_H) // 2
screen.paste(row, (rx, ry), row)
h_lines += ["#endif", ""]
OUT_H.write_text("\n".join(h_lines), encoding="utf-8")
OUT_C.write_text("\n".join(c_lines), encoding="utf-8")
screen.convert("RGB").save(PREV)
print(f"wrote {OUT_C} ({OUT_C.stat().st_size} bytes)")
print(f"wrote {OUT_H}")
print(f"flash payload ~{total} bytes")
print(f"preview {PREV}")
if __name__ == "__main__":
main()

View File

@ -138,17 +138,26 @@ def capture_rtt_png(out_path: str, device: str, timeout_s: float, do_reset: bool
buffer = b""
deadline = time.time() + timeout_s
header_off = -1
while time.time() < deadline:
term = jlink.rtt_read(0, 1024)
if term:
print("RTT0:", bytes(term).decode("utf-8", errors="replace").strip())
chunk = jlink.rtt_read(1, 8192)
chunk = jlink.rtt_read(1, 16384)
if chunk:
buffer += bytes(chunk)
if len(buffer) >= HEADER_SIZE and buffer[:4] == MAGIC:
# Drain stale bytes before SCRN (previous incomplete dumps).
header_off = buffer.find(MAGIC)
if header_off >= 0 and len(buffer) >= header_off + HEADER_SIZE:
if header_off > 0:
print(f"Discarded {header_off} stale RTT1 bytes before SCRN")
buffer = buffer[header_off:]
break
time.sleep(0.01)
# Cap memory if SCRN never appears
if len(buffer) > 256 * 1024:
buffer = buffer[-65536:]
time.sleep(0.001)
if len(buffer) < HEADER_SIZE or buffer[:4] != MAGIC:
raise SystemExit("Timeout waiting for SCRN header on RTT channel 1")

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()

185
tools/rtt_reset.py Normal file
View File

@ -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()