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:
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-10-17 — Touch = temperature driver + LVGL inverse
|
||||
|
||||
- Use `XPT2046_Touchscreen` + VSPI pin mux (touch/SD), same as climate project.
|
||||
- Map with temperature formula to UI 320x240, then invert LVGL ROTATION_270
|
||||
so buttons align with fingers.
|
||||
- On-screen `Touch L:x,y` shows intended UI coordinates.
|
||||
|
||||
## 2026-10-17 — Touch calib from COM3 logs
|
||||
|
||||
- Logged live `Touch phys/logic` on COM3; inverted physical X so bottom matches UI.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: CYD TF card + touch speaker (cyd)
|
||||
git: b09fca9
|
||||
git: d874cb1
|
||||
date: 2026-10-17
|
||||
board: ESP32-2432S028R (ILI9341)
|
||||
features: TF/SD info, SoftSPI touch, GPIO26 speaker
|
||||
@@ -12,5 +12,5 @@ offsets:
|
||||
sha256:
|
||||
bootloader.bin 3D234A7471F67B013686DABD4DEE7C1FA915C9928463616A94BC9297ACF1ABF8
|
||||
boot_app0.bin F94C5D786A7A8FAB06AC5D10E33BF37711A6697636DC037559EA19CC410A17F0
|
||||
firmware.bin 0D561A81D52B8EF52DC8597FAE6952142D267B4A6B9BE6FE9979EF5698003F56
|
||||
firmware.bin 51A8044247B56F0E95F118490189ECD6B05E0014ACE5607AC1A1FCD666466FDE
|
||||
partitions.bin 148B959CBFF1C38AA8E1D5C0BA9D612C54997B945E56A63F41223EEF650653A1
|
||||
|
||||
Binary file not shown.
8
include/bus_vspi.h
Normal file
8
include/bus_vspi.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <SPI.h>
|
||||
|
||||
// CYD:触摸与 SD 共用 VSPI,按需切换引脚
|
||||
SPIClass &cyd_vspi();
|
||||
void cyd_vspi_select_touch();
|
||||
void cyd_vspi_select_sd();
|
||||
@@ -9,6 +9,7 @@ board_build.partitions = default.csv
|
||||
lib_deps =
|
||||
bodmer/TFT_eSPI@^2.5.43
|
||||
lvgl/lvgl@9.2.2
|
||||
https://github.com/PaulStoffregen/XPT2046_Touchscreen.git
|
||||
|
||||
build_flags =
|
||||
-DCORE_DEBUG_LEVEL=0
|
||||
|
||||
26
src/bus_vspi.cpp
Normal file
26
src/bus_vspi.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "bus_vspi.h"
|
||||
#include "pins.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
static SPIClass g_vspi(VSPI);
|
||||
static bool g_touch_pins = false;
|
||||
|
||||
SPIClass &cyd_vspi() {
|
||||
return g_vspi;
|
||||
}
|
||||
|
||||
void cyd_vspi_select_touch() {
|
||||
// 释放 SD CS,避免挂总线
|
||||
pinMode(SD_CS, OUTPUT);
|
||||
digitalWrite(SD_CS, HIGH);
|
||||
g_vspi.begin(CYD_TOUCH_CLK, CYD_TOUCH_MISO, CYD_TOUCH_MOSI, CYD_TOUCH_CS);
|
||||
g_touch_pins = true;
|
||||
}
|
||||
|
||||
void cyd_vspi_select_sd() {
|
||||
pinMode(CYD_TOUCH_CS, OUTPUT);
|
||||
digitalWrite(CYD_TOUCH_CS, HIGH);
|
||||
g_vspi.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);
|
||||
g_touch_pins = false;
|
||||
}
|
||||
@@ -47,7 +47,7 @@ void setup() {
|
||||
|
||||
lv_display_t *disp =
|
||||
lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));
|
||||
// 横屏;触点报物理 240x320,由 LVGL 做旋转变换
|
||||
// 与 temperature 相同:TFT rot3 + LVGL 270;触点先算 UI 坐标再反变换喂给 LVGL
|
||||
lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270);
|
||||
|
||||
lv_indev_t *indev = lv_indev_create();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "sd_info.h"
|
||||
#include "bus_vspi.h"
|
||||
#include "pins.h"
|
||||
|
||||
#include <FS.h>
|
||||
#include <SD.h>
|
||||
#include <SPI.h>
|
||||
|
||||
static SPIClass sd_spi(VSPI);
|
||||
static bool sd_ready = false;
|
||||
|
||||
static const char *card_type_name(uint8_t t) {
|
||||
@@ -18,7 +18,6 @@ static const char *card_type_name(uint8_t t) {
|
||||
}
|
||||
}
|
||||
|
||||
// 读引导扇区,尽量识别 FAT 类型(Arduino SD 挂载的是 FatFs)
|
||||
static const char *detect_filesystem() {
|
||||
File root = SD.open("/");
|
||||
if (!root) {
|
||||
@@ -26,13 +25,10 @@ static const char *detect_filesystem() {
|
||||
}
|
||||
root.close();
|
||||
|
||||
// 通过容量启发式:Arduino SD 库挂载成功即为 FAT 系
|
||||
// 再尝试从卷信息推断常见类型
|
||||
uint64_t total = SD.totalBytes();
|
||||
if (total == 0) {
|
||||
return "FAT";
|
||||
}
|
||||
// FatFs 对 >32MB 常用 FAT32;小卡可能是 FAT16
|
||||
if (total > 32ULL * 1024ULL * 1024ULL) {
|
||||
return "FAT32";
|
||||
}
|
||||
@@ -64,11 +60,12 @@ bool sd_init() {
|
||||
|
||||
pinMode(SD_CS, OUTPUT);
|
||||
digitalWrite(SD_CS, HIGH);
|
||||
pinMode(CYD_TOUCH_CS, OUTPUT);
|
||||
digitalWrite(CYD_TOUCH_CS, HIGH);
|
||||
|
||||
sd_spi.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);
|
||||
cyd_vspi_select_sd();
|
||||
|
||||
// CYD 上 SD 与触摸争用 VSPI;本工程不用硬件触摸,频率适中更稳
|
||||
if (!SD.begin(SD_CS, sd_spi, 25000000)) {
|
||||
if (!SD.begin(SD_CS, cyd_vspi(), 25000000)) {
|
||||
sd_ready = false;
|
||||
return false;
|
||||
}
|
||||
@@ -79,6 +76,7 @@ bool sd_init() {
|
||||
|
||||
void sd_deinit() {
|
||||
if (sd_ready) {
|
||||
cyd_vspi_select_sd();
|
||||
SD.end();
|
||||
sd_ready = false;
|
||||
}
|
||||
@@ -96,6 +94,8 @@ SdCardInfo sd_read_info() {
|
||||
info.free_bytes = 0;
|
||||
info.root_files = 0;
|
||||
|
||||
cyd_vspi_select_sd();
|
||||
|
||||
if (!sd_ready && !sd_init()) {
|
||||
info.error = "Mount failed";
|
||||
return info;
|
||||
|
||||
129
src/touch.cpp
129
src/touch.cpp
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user