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:
parent
b996c8876e
commit
b8eaf2c808
|
|
@ -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,25 @@ 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;
|
||||
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,29 +76,27 @@ 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;
|
||||
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;
|
||||
|
|
@ -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);
|
||||
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 +117,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 +130,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
64
UI/UI_Idle.c
64
UI/UI_Idle.c
|
|
@ -22,6 +22,28 @@ void RGB_Y_TO_G()
|
|||
/* 模式选择页焦点:0=万能 1=普通 2=专业,默认选中万能(与规范一致) */
|
||||
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 文字整行位图 */
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
gImage_Mode0831_Universal_Row_180_52,
|
||||
s_mode_select_focus == 0);
|
||||
|
|
@ -65,12 +80,21 @@ void UI_DrawModeSelectScreen(void)
|
|||
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[] = {
|
||||
/* Xmin,Xmax,Ymin,Ymax, Flag, Value, ID, action
|
||||
* Flag=按钮序号;ID=进入的 TAB(0万能Song / 1专业Expert / 2普通Free) */
|
||||
{ 10, 229, 40, 126, 0, 0, 0, NULL }, /* 万能模式 → SongMode */
|
||||
{ 10, 229, 132, 218, 1, 0, 2, NULL }, /* 普通模式 → FreeMode */
|
||||
{ 10, 229, 224, 310, 2, 0, 1, NULL }, /* 专业模式 → ExpertMode */
|
||||
/* 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))
|
||||
|
|
@ -156,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;
|
||||
|
|
@ -167,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);
|
||||
|
|
@ -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 &&
|
||||
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;
|
||||
|
||||
case MSG_ID_TOUCH_LONG_REPEAT:
|
||||
/* 模式页暂无长按动作 */
|
||||
break;
|
||||
|
||||
case MSG_ID_BATTERY_VALUE:
|
||||
Draw_Status_Battery_Icon(msg.ID);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -417,6 +417,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 &&
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#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_MAGIC "SCRN"
|
||||
#define LCD_DUMP_FMT_RGB565 1
|
||||
#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] = '\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) {
|
||||
s_cmd_len = 0U;
|
||||
s_cmd_buf[0] = '\0';
|
||||
|
|
|
|||
|
|
@ -97,111 +97,149 @@ 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输入
|
||||
TP_CLK_Set();
|
||||
Delay_us(1);
|
||||
TP_CLK_Clr();
|
||||
Delay_us(1);
|
||||
|
||||
// MCU读取OUT引脚的输入数据(高12位有效,低4位丢弃)
|
||||
if (TP_OUT_Read())
|
||||
{
|
||||
adc_val |= 0x01;
|
||||
}
|
||||
adc_val <<= 1;
|
||||
TP_CLK_Set();
|
||||
Delay_us(1);
|
||||
TP_CLK_Clr();
|
||||
Delay_us(1);
|
||||
if (TP_OUT_Read())
|
||||
adc_val |= 0x01;
|
||||
}
|
||||
|
||||
TP_CS_Set(); // 释放片选
|
||||
return adc_val >> 4; // 取高12位有效数据
|
||||
TP_CS_Set();
|
||||
return adc_val >> 4;
|
||||
}
|
||||
|
||||
// 简单去抖动读取(连续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++)
|
||||
{
|
||||
if (TP_IRQ_Read() == 0)
|
||||
{ // IRQ低电平表示按下
|
||||
count++;
|
||||
}
|
||||
Delay_us(100);
|
||||
}
|
||||
|
||||
return (count >= n/2) ? 1 : 0;
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
if (TP_IRQ_Read() == 0)
|
||||
count++;
|
||||
Delay_us(100);
|
||||
}
|
||||
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};
|
||||
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;
|
||||
}
|
||||
|
||||
// 多次读取取平均值,减少噪声
|
||||
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 // 最小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高度
|
||||
#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};
|
||||
TP_Point raw = TP_ReadRaw();
|
||||
TP_Point calib = {0, 0, raw.pressed};
|
||||
uint16_t ax, ay;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 线性映射:将原始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触发)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "includes.h"
|
||||
#include "SEGGER_RTT.h"
|
||||
|
||||
static struct rt_thread TaskScanThread;
|
||||
static struct rt_thread TaskUIThread;
|
||||
|
|
@ -44,13 +45,20 @@ 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);
|
||||
/* DUMP 由 lcd_dump 专用线程轮询,勿在触摸线程里做 SPI 读屏,避免卡死触摸 */
|
||||
TP_Point tp = TP_Read(); /* 已含轴交换/镜像,直接进手势 */
|
||||
if (tp.pressed && !s_last_pressed)
|
||||
{
|
||||
char buf[40];
|
||||
sprintf(buf, "TP %u,%u\n", (unsigned)tp.x, (unsigned)tp.y);
|
||||
SEGGER_RTT_WriteString(0, buf);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue