Align touch with temperature XPT2046 driver and LVGL inverse map.

Mux VSPI between SD and touch; map like climate UI then undo ROTATION_270 so buttons hit.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-17 18:07:18 +08:00
parent d874cb192c
commit 156d9cf583
9 changed files with 99 additions and 94 deletions

View File

@@ -1,14 +1,12 @@
#include "touch.h"
#include "bus_vspi.h"
#include "pins.h"
#include <XPT2046_Touchscreen.h>
#include <Arduino.h>
/**
* SoftSPI XPT2046 → LVGL indev(物理 240x320,LVGL 再做 ROTATION_270)
*
* 串口实测:未反转时,点屏幕下方按钮对应到上方区域;
* 故对物理 X 取反,使视觉上下与逻辑 ly 一致。
*/
// 与 temperature 完全相同的硬件触摸驱动
static XPT2046_Touchscreen touchscreen(CYD_TOUCH_CS, CYD_TOUCH_IRQ);
int16_t g_dbg_phys_x = -1;
int16_t g_dbg_phys_y = -1;
@@ -16,107 +14,72 @@ int16_t g_dbg_logic_x = -1;
int16_t g_dbg_logic_y = -1;
bool g_dbg_touch_down = false;
static void soft_write(uint8_t data) {
for (int i = 7; i >= 0; --i) {
digitalWrite(CYD_TOUCH_MOSI, (data >> i) & 1);
digitalWrite(CYD_TOUCH_CLK, HIGH);
delayMicroseconds(2);
digitalWrite(CYD_TOUCH_CLK, LOW);
delayMicroseconds(2);
}
}
static uint16_t soft_read12() {
uint16_t v = 0;
digitalWrite(CYD_TOUCH_CLK, HIGH);
delayMicroseconds(2);
digitalWrite(CYD_TOUCH_CLK, LOW);
delayMicroseconds(2);
for (int i = 0; i < 12; ++i) {
digitalWrite(CYD_TOUCH_CLK, HIGH);
delayMicroseconds(2);
v = (uint16_t)((v << 1) | (digitalRead(CYD_TOUCH_MISO) ? 1u : 0u));
digitalWrite(CYD_TOUCH_CLK, LOW);
delayMicroseconds(2);
}
return v;
}
static uint16_t xpt_read(uint8_t cmd) {
digitalWrite(CYD_TOUCH_CS, LOW);
soft_write(cmd);
uint16_t v = soft_read12();
digitalWrite(CYD_TOUCH_CS, HIGH);
return v;
}
void touch_init() {
pinMode(CYD_TOUCH_CS, OUTPUT);
pinMode(CYD_TOUCH_CLK, OUTPUT);
pinMode(CYD_TOUCH_MOSI, OUTPUT);
pinMode(CYD_TOUCH_MISO, INPUT);
pinMode(CYD_TOUCH_IRQ, INPUT);
pinMode(CYD_TOUCH_CS, OUTPUT);
digitalWrite(CYD_TOUCH_CS, HIGH);
digitalWrite(CYD_TOUCH_CLK, LOW);
Serial.println("Touch: phys 240x320, invert X (from serial calib)");
pinMode(SD_CS, OUTPUT);
digitalWrite(SD_CS, HIGH);
cyd_vspi_select_touch();
if (!touchscreen.begin(cyd_vspi())) {
Serial.println("Touch: begin FAILED");
}
touchscreen.setRotation(2); // temperature 相同
Serial.println("Touch: XPT2046 + temp map + LVGL inverse");
}
void touch_read_cb(lv_indev_t * /*indev*/, lv_indev_data_t *data) {
uint16_t raw_x = xpt_read(0x90);
uint16_t raw_y = xpt_read(0xD0);
raw_x = (uint16_t)((raw_x + xpt_read(0x90)) / 2);
raw_y = (uint16_t)((raw_y + xpt_read(0xD0)) / 2);
cyd_vspi_select_touch();
const bool irq = (digitalRead(CYD_TOUCH_IRQ) == LOW);
const bool in_range =
(raw_x > 80 && raw_x < 4000 && raw_y > 80 && raw_y < 4000);
if (!in_range || (!irq && (raw_x < 150 || raw_y < 150))) {
data->state = LV_INDEV_STATE_RELEASED;
if (!(touchscreen.tirqTouched() && touchscreen.touched())) {
g_dbg_touch_down = false;
data->state = LV_INDEV_STATE_RELEASED;
return;
}
// setRotation(2) 等价
const int16_t p_x = (int16_t)raw_x;
const int16_t p_y = (int16_t)(4095 - raw_y);
TS_Point p = touchscreen.getPoint();
// 物理竖屏;X 反向(串口日志标定)
int16_t x = map(p_x, 200, 3700, SCREEN_WIDTH - 1, 0);
int16_t y = map(p_y, 240, 3800, 0, SCREEN_HEIGHT - 1);
// 1) 与 temperature 相同:得到「屏幕上看到的」横屏坐标 320x240
int16_t lx = map(p.y, 240, 3800, 1, UI_HOR_RES);
int16_t ly = map(p.x, 200, 3700, UI_VER_RES, 1);
if (x < 0) {
x = 0;
if (lx < 0) {
lx = 0;
}
if (y < 0) {
y = 0;
if (ly < 0) {
ly = 0;
}
if (x >= SCREEN_WIDTH) {
x = SCREEN_WIDTH - 1;
if (lx >= UI_HOR_RES) {
lx = UI_HOR_RES - 1;
}
if (y >= SCREEN_HEIGHT) {
y = SCREEN_HEIGHT - 1;
if (ly >= UI_VER_RES) {
ly = UI_VER_RES - 1;
}
// LVGL ROTATION_270 之后的逻辑坐标(与 indev_pointer_proc 一致)
// lx = y, ly = hor_res - x - 1 (hor_res=240)
const int16_t logic_x = y;
const int16_t logic_y = (int16_t)(SCREEN_WIDTH - 1 - x);
// 2) LVGL 在 ROTATION_270 时还会变换一次(create 240x320):
// lx_out = y_in
// ly_out = hor_res - 1 - x_in (hor_res = 240)
// 故要让最终 = (lx, ly),应上报:
// x_in = 239 - ly
// y_in = lx
const int16_t x_in = (int16_t)((SCREEN_WIDTH - 1) - ly);
const int16_t y_in = lx;
g_dbg_phys_x = x;
g_dbg_phys_y = y;
g_dbg_logic_x = logic_x;
g_dbg_logic_y = logic_y;
g_dbg_phys_x = p.x;
g_dbg_phys_y = p.y;
g_dbg_logic_x = lx;
g_dbg_logic_y = ly;
g_dbg_touch_down = true;
static uint32_t last_log = 0;
if (millis() - last_log > 150) {
if (millis() - last_log > 120) {
last_log = millis();
Serial.printf("Touch raw(%u,%u) phys(%d,%d) logic(%d,%d)\n", raw_x, raw_y, x,
y, logic_x, logic_y);
Serial.printf("Touch raw(%d,%d) UI(%d,%d) feed(%d,%d)\n", p.x, p.y, lx, ly,
x_in, y_in);
}
data->point.x = x;
data->point.y = y;
data->point.x = x_in;
data->point.y = y_in;
data->state = LV_INDEV_STATE_PRESSED;
}