Compare commits
7 Commits
27cf30cbe4
...
956eeee1a5
| Author | SHA1 | Date |
|---|---|---|
|
|
956eeee1a5 | |
|
|
f6ae34892f | |
|
|
7fea921727 | |
|
|
b8eaf2c808 | |
|
|
b996c8876e | |
|
|
ba31d91aef | |
|
|
68e3a37883 |
|
|
@ -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 */
|
||||
|
|
@ -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__ */
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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; /* 业务消息 ID(MSG_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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
138
UI/UI_Idle.c
138
UI/UI_Idle.c
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 &&
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
113
task/task_init.c
113
task/task_init.c
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 ****************************/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
```
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Send sys reset via J-Link RTT to reboot K1 MCU (software NVIC reset)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import time
|
||||
|
||||
DEFAULT_DEVICES = ("Cortex-M4", "AT32F403AC", "AT32F403A")
|
||||
RESET_CMD = b"sys reset\n"
|
||||
ACK = b"SYS_RESET_OK"
|
||||
|
||||
|
||||
def import_deps():
|
||||
try:
|
||||
import pylink # noqa: F401
|
||||
except ImportError as exc:
|
||||
raise SystemExit("Missing dependency: pip install pylink-square") from exc
|
||||
|
||||
|
||||
def connect_jlink(device: str):
|
||||
import pylink
|
||||
|
||||
jlink = pylink.JLink()
|
||||
jlink.open()
|
||||
try:
|
||||
jlink.exec_command("HideDeviceSelection = 1")
|
||||
except Exception:
|
||||
pass
|
||||
jlink.set_tif(pylink.enums.JLinkInterfaces.SWD)
|
||||
|
||||
last_error = None
|
||||
for candidate in (device, *DEFAULT_DEVICES):
|
||||
try:
|
||||
try:
|
||||
jlink.exec_command(f"Device = {candidate}")
|
||||
except Exception:
|
||||
pass
|
||||
jlink.connect(candidate)
|
||||
print(f"Connected as {candidate}")
|
||||
return jlink
|
||||
except pylink.errors.JLinkException as exc:
|
||||
last_error = exc
|
||||
jlink.close()
|
||||
raise SystemExit(f"Failed to connect J-Link: {last_error}")
|
||||
|
||||
|
||||
def find_rtt_control_block(jlink, ram_base: int = 0x20000000, ram_size: int = 0x18000) -> int | None:
|
||||
needle = b"SEGGER RTT"
|
||||
chunk = 0x1000
|
||||
was_halted = jlink.halted()
|
||||
if not was_halted:
|
||||
jlink.halt()
|
||||
try:
|
||||
for off in range(0, ram_size, chunk):
|
||||
data = bytes(jlink.memory_read8(ram_base + off, min(chunk, ram_size - off)))
|
||||
idx = data.find(needle)
|
||||
if idx >= 0:
|
||||
return ram_base + off + idx
|
||||
finally:
|
||||
if not was_halted:
|
||||
resume_cpu(jlink)
|
||||
return None
|
||||
|
||||
|
||||
def resume_cpu(jlink) -> None:
|
||||
"""J-Link often halts the core on reset; must run() or UI stays black."""
|
||||
try:
|
||||
jlink.rtt_stop()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
jlink.reset(halt=False)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
if hasattr(jlink, "restart"):
|
||||
jlink.restart()
|
||||
else:
|
||||
jlink.go()
|
||||
except Exception:
|
||||
jlink.go()
|
||||
time.sleep(0.05)
|
||||
|
||||
|
||||
def wait_rtt_ready(jlink, timeout_s: float = 15.0) -> None:
|
||||
deadline = time.time() + timeout_s
|
||||
while time.time() < deadline:
|
||||
try:
|
||||
if jlink.rtt_get_num_up_buffers() > 0:
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(0.2)
|
||||
raise SystemExit("RTT control block found but buffers not ready")
|
||||
|
||||
|
||||
def send_reset(device: str, timeout_s: float, wait_boot_s: float, hw_fallback: bool) -> None:
|
||||
jlink = connect_jlink(device)
|
||||
ack_seen = False
|
||||
try:
|
||||
if jlink.halted():
|
||||
print("Target was halted, resuming before RTT...")
|
||||
resume_cpu(jlink)
|
||||
time.sleep(0.2)
|
||||
|
||||
cb = find_rtt_control_block(jlink)
|
||||
if cb is None:
|
||||
raise SystemExit("SEGGER RTT control block not found in SRAM")
|
||||
print(f"RTT CB @ 0x{cb:08X}")
|
||||
|
||||
resume_cpu(jlink)
|
||||
time.sleep(0.15)
|
||||
jlink.rtt_start(cb)
|
||||
wait_rtt_ready(jlink)
|
||||
|
||||
wrote = 0
|
||||
for _ in range(30):
|
||||
wrote = jlink.rtt_write(0, list(RESET_CMD))
|
||||
if wrote > 0:
|
||||
break
|
||||
time.sleep(0.2)
|
||||
if wrote <= 0:
|
||||
raise SystemExit("Failed to send sys reset on RTT down channel 0")
|
||||
print(f"Sent sys reset ({wrote} bytes)")
|
||||
|
||||
deadline = time.time() + timeout_s
|
||||
while time.time() < deadline:
|
||||
chunk = jlink.rtt_read(0, 1024)
|
||||
if chunk:
|
||||
text = bytes(chunk)
|
||||
if ACK in text:
|
||||
ack_seen = True
|
||||
print("SYS_RESET_OK (firmware handled reset)")
|
||||
break
|
||||
time.sleep(0.05)
|
||||
|
||||
if not ack_seen:
|
||||
print("No SYS_RESET_OK from firmware.")
|
||||
if hw_fallback:
|
||||
print("Using J-Link hardware reset fallback...")
|
||||
resume_cpu(jlink)
|
||||
else:
|
||||
print("Hint: re-run with --hw-fallback or flash latest firmware.")
|
||||
|
||||
# Critical: release CPU after reset — otherwise screen stays black.
|
||||
print("Resuming CPU after reset...")
|
||||
resume_cpu(jlink)
|
||||
|
||||
if wait_boot_s > 0:
|
||||
print(f"Waiting {wait_boot_s:.0f}s for auto boot...")
|
||||
time.sleep(wait_boot_s)
|
||||
resume_cpu(jlink)
|
||||
print("Done. Device should be back on mode-select screen.")
|
||||
finally:
|
||||
try:
|
||||
resume_cpu(jlink)
|
||||
except Exception:
|
||||
pass
|
||||
jlink.close()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Reboot K1 via RTT sys reset command")
|
||||
parser.add_argument("--device", default="AT32F403AC", help="J-Link device name")
|
||||
parser.add_argument("--timeout", type=float, default=3.0, help="Seconds to wait for ACK")
|
||||
parser.add_argument(
|
||||
"--wait-boot",
|
||||
type=float,
|
||||
default=4.0,
|
||||
help="Seconds to wait after reset for auto power-on (0 to skip)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-hw-fallback",
|
||||
action="store_true",
|
||||
help="Do not use J-Link hardware reset when firmware ACK is missing",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
import_deps()
|
||||
send_reset(args.device, args.timeout, args.wait_boot, not args.no_hw_fallback)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue