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>
This commit is contained in:
yuquanjun 2026-08-31 11:13:01 +08:00
parent b996c8876e
commit b8eaf2c808
9 changed files with 259 additions and 190 deletions

View File

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

View File

@ -52,37 +52,33 @@ Fader_t faders[4] = {
const Touch_AreaTypeDef Touch_Areas[] = const Touch_AreaTypeDef Touch_Areas[] =
{ {
// Xmin, Xmax, Ymin, Ymax, flag,value ID, action /* Xmin,Xmax,Ymin,Ymax, Flag, Value, ID, action — 坐标相对 240x320 */
{ 0, 60, 25, 90, -1, 0, GUI_TRANSPOSE, NULL }, { 0, 60, 25, 95, -1, 0, GUI_TRANSPOSE, NULL },
{ 60, 120, 25, 90, 1, 0, GUI_TRANSPOSE, NULL }, { 60, 120, 25, 95, 1, 0, GUI_TRANSPOSE, NULL },
{ 125, 180, 25, 90, -1, 0, GUI_SPEED, NULL }, { 125, 180, 25, 95, -1, 0, GUI_SPEED, NULL },
{ 180, 240, 25, 90, 1, 0, GUI_SPEED, NULL }, { 180, 239, 25, 95, 1, 0, GUI_SPEED, NULL },
{ 0, 80, 90, 120, 0, 0, GUI_AUTOBAND_SW, NULL }, { 0, 85, 95, 130, 0, 0, GUI_AUTOBAND_SW, NULL },
{ 90, 170, 90, 120, 0, 1, GUI_AUTOBAND_SW, NULL }, { 90, 175, 95, 130, 0, 1, GUI_AUTOBAND_SW, NULL },
{ 80, 140, 0, 30, 0, 0, GUI_BL_SW, NULL },
{ 0, 120, 120, 150, -1, 0, GUI_MODE_PARAM, NULL }, { 0, 120, 130, 165, -1, 0, GUI_MODE_PARAM, NULL },
{120, 240, 120, 150, 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 }, { 0, 120, 165, 215, -1, 0, GUI_TIMBRE_SELECT, NULL },
{ 80, 140, 0, 30, 0, 0, GUI_MODE_PARAM, NULL }, { 120, 239, 165, 215, 1, 0, GUI_TIMBRE_SELECT, 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 },
/* 演奏段落 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}; int16_t MIC_VOL_MAP[10]= {-9000,-7000,-5000,-3000,-1000,1000,3000,5000,7000,9000};

View File

@ -22,6 +22,28 @@ void RGB_Y_TO_G()
/* 模式选择页焦点0=万能 1=普通 2=专业,默认选中万能(与规范一致) */ /* 模式选择页焦点0=万能 1=普通 2=专业,默认选中万能(与规范一致) */
static uint8_t s_mode_select_focus = 0; static uint8_t s_mode_select_focus = 0;
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 文字整行位图 */ /* 0831 美工试用图:图标+MiSans 文字整行位图 */
static void UI_DrawModeSelectButton(uint16_t y, const uint8_t *row, uint8_t focused) static void UI_DrawModeSelectButton(uint16_t y, const uint8_t *row, uint8_t focused)
{ {
@ -45,15 +67,8 @@ static void UI_DrawModeSelectButton(uint16_t y, const uint8_t *row, uint8_t focu
LCD_WR_PIC_Trans(row_x, row_y, UI_MODE0831_ROW_W, UI_MODE0831_ROW_H, row, BLACK); LCD_WR_PIC_Trans(row_x, row_y, UI_MODE0831_ROW_W, UI_MODE0831_ROW_H, row, BLACK);
} }
void UI_DrawModeSelectScreen(void) static void UI_ModeSelect_DrawButtons(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, UI_DrawModeSelectButton(40,
gImage_Mode0831_Universal_Row_180_52, gImage_Mode0831_Universal_Row_180_52,
s_mode_select_focus == 0); s_mode_select_focus == 0);
@ -65,12 +80,21 @@ void UI_DrawModeSelectScreen(void)
s_mode_select_focus == 2); s_mode_select_focus == 2);
} }
void UI_DrawModeSelectScreen(void)
{
UI_ClearFocus();
pCurrentTouch_Area = Touch_ModeSelect_Areas;
LCD_FillByColor(0, 0, 240, 320, BLACK);
label_top();
UI_ModeSelect_DrawButtons();
}
const Touch_AreaTypeDef Touch_ModeSelect_Areas[] = { const Touch_AreaTypeDef Touch_ModeSelect_Areas[] = {
/* Xmin,Xmax,Ymin,Ymax, Flag, Value, ID, action /* Flag=按钮序号(0万能/1普通/2专业)ID=进入 TAB */
* Flag=ID= TAB0Song / 1Expert / 2Free */ { 0, 239, 38, 127, 0, 0, 0, NULL },
{ 10, 229, 40, 126, 0, 0, 0, NULL }, /* 万能模式 → SongMode */ { 0, 239, 128, 219, 1, 0, 2, NULL },
{ 10, 229, 132, 218, 1, 0, 2, NULL }, /* 普通模式 → FreeMode */ { 0, 239, 220, 311, 2, 0, 1, NULL },
{ 10, 229, 224, 310, 2, 0, 1, NULL }, /* 专业模式 → ExpertMode */
}; };
#define TOUCH_AREA_MODE_SELECT_NUM (sizeof(Touch_ModeSelect_Areas)/sizeof(Touch_AreaTypeDef)) #define TOUCH_AREA_MODE_SELECT_NUM (sizeof(Touch_ModeSelect_Areas)/sizeof(Touch_AreaTypeDef))
@ -156,7 +180,8 @@ void IdleProcess(DisplayTaskMessage_Type msg)
LCD_FillByColor(0, 0, 240, 320, WHITE); LCD_FillByColor(0, 0, 240, 320, WHITE);
LCD_WR_PIC_FROM_FLASH(40, 80, 168, 155, 0x00000000); LCD_WR_PIC_FROM_FLASH(40, 80, 168, 155, 0x00000000);
BSP_SelectAdcChannel(6); BSP_SelectAdcChannel(6);
wk_delay_ms(1000); /* 让出 CPU避免忙等卡住触摸/主循环线程 */
rt_thread_mdelay(1000);
LoadConfig(); LoadConfig();
vol = ADC_ReadChannel(ADC_CHANNEL_1)>>5; vol = ADC_ReadChannel(ADC_CHANNEL_1)>>5;
if (vol <= 5) level = 0; if (vol <= 5) level = 0;
@ -167,6 +192,7 @@ void IdleProcess(DisplayTaskMessage_Type msg)
else level = 5; else level = 5;
Send_volume(vol); Send_volume(vol);
s_mode_select_focus = 0;
UI_DrawModeSelectScreen(); UI_DrawModeSelectScreen();
TM1629D_AllLedOn(LED_COLOR_G); TM1629D_AllLedOn(LED_COLOR_G);
TM1629D_UpdateDisplay(0); TM1629D_UpdateDisplay(0);
@ -190,12 +216,20 @@ void IdleProcess(DisplayTaskMessage_Type msg)
if( msg.HiByte >= Touch_ModeSelect_Areas[k].x_min && msg.HiByte <= Touch_ModeSelect_Areas[k].x_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 ) msg.LoByte >= Touch_ModeSelect_Areas[k].y_min && msg.LoByte <= Touch_ModeSelect_Areas[k].y_max )
{ {
Touch_Mode_Select_Action(Touch_ModeSelect_Areas[k].Flag,Touch_ModeSelect_Areas[k].ID,k); uint8_t row = (uint8_t)Touch_ModeSelect_Areas[k].Flag;
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; break;
} }
} }
break; break;
case MSG_ID_TOUCH_LONG_REPEAT:
/* 模式页暂无长按动作 */
break;
case MSG_ID_BATTERY_VALUE: case MSG_ID_BATTERY_VALUE:
Draw_Status_Battery_Icon(msg.ID); Draw_Status_Battery_Icon(msg.ID);
break; break;

View File

@ -417,6 +417,12 @@ void UI_SongMode_Process(DisplayTaskMessage_Type msg)
break; break;
case MSG_ID_TOUCH_LONG_REPEAT: 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++) 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 && if( msg.HiByte >= pCurrentTouch_Area[k].x_min && msg.HiByte <= pCurrentTouch_Area[k].x_max &&

View File

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

View File

@ -7,6 +7,7 @@
#define LCD_DUMP_RTT_UP_CH 1 #define LCD_DUMP_RTT_UP_CH 1
#define LCD_DUMP_RTT_DOWN_CH 0 #define LCD_DUMP_RTT_DOWN_CH 0
#define LCD_DUMP_CMD "DUMP" #define LCD_DUMP_CMD "DUMP"
#define LCD_DUMP_TAP_CMD "TAP"
#define LCD_DUMP_MAGIC "SCRN" #define LCD_DUMP_MAGIC "SCRN"
#define LCD_DUMP_FMT_RGB565 1 #define LCD_DUMP_FMT_RGB565 1
#define LCD_DUMP_CMD_BUF_SIZE 16 #define LCD_DUMP_CMD_BUF_SIZE 16
@ -208,6 +209,15 @@ static uint8_t LCD_Dump_CommandPending(void)
s_cmd_buf[s_cmd_len++] = c; s_cmd_buf[s_cmd_len++] = c;
s_cmd_buf[s_cmd_len] = '\0'; s_cmd_buf[s_cmd_len] = '\0';
/* 合成点击:验证 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 (c == 0x01 || strstr(s_cmd_buf, LCD_DUMP_CMD) != NULL) { if (c == 0x01 || strstr(s_cmd_buf, LCD_DUMP_CMD) != NULL) {
s_cmd_len = 0U; s_cmd_len = 0U;
s_cmd_buf[0] = '\0'; s_cmd_buf[0] = '\0';

View File

@ -97,111 +97,149 @@ uint16_t TP_Read_AD(uint8_t cmd)
uint8_t i; uint8_t i;
uint16_t adc_val = 0; uint16_t adc_val = 0;
TP_CS_Clr(); // 片选拉低选中XPT2046 TP_CS_Clr();
Delay_us(2); // 片选稳定时间 Delay_us(2);
TP_Write_Byte(cmd);
TP_Write_Byte(cmd); // MCU通过DIN发送命令XPT2046的DIN输入接收
// 等待XPT2046完成AD转换约6us转换期间OUT无有效数据
Delay_us(6); Delay_us(6);
// 读取16位数据XPT2046通过自身OUT输出数据MCU通过OUT输入读取
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)
{ {
adc_val <<= 1; // 左移,准备接收下一位 adc_val <<= 1;
TP_CLK_Set();
// 时钟上升沿XPT2046更新OUT引脚输出下降沿MCU读取OUT输入 Delay_us(1);
TP_CLK_Set(); TP_CLK_Clr();
Delay_us(1); Delay_us(1);
TP_CLK_Clr(); if (TP_OUT_Read())
Delay_us(1); adc_val |= 0x01;
// MCU读取OUT引脚的输入数据高12位有效低4位丢弃
if (TP_OUT_Read())
{
adc_val |= 0x01;
}
} }
TP_CS_Set(); // 释放片选 TP_CS_Set();
return adc_val >> 4; // 取高12位有效数据 return adc_val >> 4;
} }
// 简单去抖动读取连续n次相同状态视为有效
uint8_t TP_CheckPressed(uint8_t n) uint8_t TP_CheckPressed(uint8_t n)
{ {
uint8_t count = 0; 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) if (TP_IRQ_Read() == 0)
{ // IRQ低电平表示按下 count++;
count++; Delay_us(100);
} }
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 TP_ReadRaw(void)
{ {
TP_Point p = {0, 0, 0}; TP_Point p = {0, 0, 0};
uint32_t x_sum = 0, y_sum = 0;
uint8_t i;
if (!TP_CheckPressed(3)) if (!TP_CheckPressed(3))
{ // 检查触摸状态 return p;
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;
return p; return p;
}
// 多次读取取平均值,减少噪声
uint32_t x_sum = 0, y_sum = 0;
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;
return p;
} }
// 校准参数需实际触摸四个角后修改示例为240x320屏 #define CALIB_X_MIN 200
#define CALIB_X_MIN 200 // 最小X原始值 #define CALIB_X_MAX 3800
#define CALIB_X_MAX 3800 // 最大X原始值 #define CALIB_Y_MIN 200
#define CALIB_Y_MIN 200 // 最小Y原始值 #define CALIB_Y_MAX 3800
#define CALIB_Y_MAX 3800 // 最大Y原始值
#define LCD_WIDTH 240 // LCD宽度 /* 竖屏:多数 ILI9341+XPT2046 板级需 XY 对调X 镜像与历史 240-tp.x 一致 */
#define LCD_HEIGHT 320 // LCD高度 #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) TP_Point TP_Read(void)
{ {
// char Info[40]; TP_Point raw = TP_ReadRaw();
TP_Point raw = TP_ReadRaw(); TP_Point calib = {0, 0, raw.pressed};
// sprintf (Info, "(x=%03d y=%03d) ", raw.x, raw.y); uint16_t ax, ay;
// LCD_ShowString(2, 176+16, (const uint8_t*)Info, RED, WHITE, 16, 0);
TP_Point calib = {0, 0, raw.pressed};
if (!raw.pressed) { if (!raw.pressed)
return calib;
#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 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; 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 (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;
return calib;
} }
// EXTI15_10中断服务函数处理TP_IRQ触发 // EXTI15_10中断服务函数处理TP_IRQ触发

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

View File

@ -1,4 +1,5 @@
#include "includes.h" #include "includes.h"
#include "SEGGER_RTT.h"
static struct rt_thread TaskScanThread; static struct rt_thread TaskScanThread;
static struct rt_thread TaskUIThread; static struct rt_thread TaskUIThread;
@ -44,13 +45,20 @@ void TaskUIThread_entry(void* parameter)
void TaskTouchThread_entry(void* parameter) void TaskTouchThread_entry(void* parameter)
{ {
static uint8_t s_last_pressed = 0;
while(1) while(1)
{ {
TP_Point tp = TP_Read(); /* 读 XPT2046 触摸坐标 */ TP_Point tp = TP_Read(); /* 已含轴交换/镜像,直接进手势 */
app_touch_process(240-tp.x, tp.y, tp.pressed); if (tp.pressed && !s_last_pressed)
/* DUMP 由 lcd_dump 专用线程轮询,勿在触摸线程里做 SPI 读屏,避免卡死触摸 */ {
char buf[40];
sprintf(buf, "TP %u,%u\n", (unsigned)tp.x, (unsigned)tp.y);
SEGGER_RTT_WriteString(0, buf);
}
s_last_pressed = tp.pressed;
app_touch_process(tp.x, tp.y, tp.pressed);
Read_Cur_Measure(); Read_Cur_Measure();
rt_thread_mdelay(10); rt_thread_mdelay(8);
} }
} }