Restore SoftSPI touch and exclusive VSPI for SD.

HW SPI remux froze touch at (0,4095) and broke SD; keep temperature UI map plus LVGL inverse feed.

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

View File

@@ -1,11 +1,14 @@
# Changelog # Changelog
## 2026-10-17 — SoftSPI touch + LVGL inverse (fix mux)
- VSPI remux broke SD mount and froze touch at raw(0,4095).
- Touch back to SoftSPI on 25/32/39/33; SD keeps exclusive VSPI 18/19/23/5.
- Same temperature UI map, then invert LVGL ROTATION_270 for button hits.
## 2026-10-17 — Touch = temperature driver + LVGL inverse ## 2026-10-17 — Touch = temperature driver + LVGL inverse
- Use `XPT2046_Touchscreen` + VSPI pin mux (touch/SD), same as climate project. - Attempted XPT2046 HW SPI + VSPI pin mux; superseded (broke SD/touch).
- 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 ## 2026-10-17 — Touch calib from COM3 logs

View File

@@ -1,5 +1,5 @@
name: CYD TF card + touch speaker (cyd) name: CYD TF card + touch speaker (cyd)
git: d874cb1 git: 156d9cf
date: 2026-10-17 date: 2026-10-17
board: ESP32-2432S028R (ILI9341) board: ESP32-2432S028R (ILI9341)
features: TF/SD info, SoftSPI touch, GPIO26 speaker features: TF/SD info, SoftSPI touch, GPIO26 speaker
@@ -12,5 +12,5 @@ offsets:
sha256: sha256:
bootloader.bin 3D234A7471F67B013686DABD4DEE7C1FA915C9928463616A94BC9297ACF1ABF8 bootloader.bin 3D234A7471F67B013686DABD4DEE7C1FA915C9928463616A94BC9297ACF1ABF8
boot_app0.bin F94C5D786A7A8FAB06AC5D10E33BF37711A6697636DC037559EA19CC410A17F0 boot_app0.bin F94C5D786A7A8FAB06AC5D10E33BF37711A6697636DC037559EA19CC410A17F0
firmware.bin 51A8044247B56F0E95F118490189ECD6B05E0014ACE5607AC1A1FCD666466FDE firmware.bin 3A23773415A312ABE3DAE81652F22C618A0839C8DCCA77B10AF16682DD5E2BD0
partitions.bin 148B959CBFF1C38AA8E1D5C0BA9D612C54997B945E56A63F41223EEF650653A1 partitions.bin 148B959CBFF1C38AA8E1D5C0BA9D612C54997B945E56A63F41223EEF650653A1

Binary file not shown.

View File

@@ -1,8 +0,0 @@
#pragma once
#include <SPI.h>
// CYD:触摸与 SD 共用 VSPI,按需切换引脚
SPIClass &cyd_vspi();
void cyd_vspi_select_touch();
void cyd_vspi_select_sd();

View File

@@ -14,7 +14,7 @@
// 板载 SC8002B 功放 → P4 喇叭座(DAC2) // 板载 SC8002B 功放 → P4 喇叭座(DAC2)
#define SPEAKER_PIN 26 #define SPEAKER_PIN 26
// 触摸 XPT2046(SoftSPI,避开 VSPI;勿用 TOUCH_CS,会与 TFT_eSPI 冲突) // 触摸 XPT2046(SoftSPI 专用脚,VSPI 留给 SD;勿用 TOUCH_CS,会与 TFT_eSPI 冲突)
#define CYD_TOUCH_IRQ 36 #define CYD_TOUCH_IRQ 36
#define CYD_TOUCH_MOSI 32 #define CYD_TOUCH_MOSI 32
#define CYD_TOUCH_MISO 39 #define CYD_TOUCH_MISO 39

View File

@@ -9,7 +9,6 @@ board_build.partitions = default.csv
lib_deps = lib_deps =
bodmer/TFT_eSPI@^2.5.43 bodmer/TFT_eSPI@^2.5.43
lvgl/lvgl@9.2.2 lvgl/lvgl@9.2.2
https://github.com/PaulStoffregen/XPT2046_Touchscreen.git
build_flags = build_flags =
-DCORE_DEBUG_LEVEL=0 -DCORE_DEBUG_LEVEL=0

View File

@@ -1,26 +0,0 @@
#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;
}

View File

@@ -1,11 +1,11 @@
#include "sd_info.h" #include "sd_info.h"
#include "bus_vspi.h"
#include "pins.h" #include "pins.h"
#include <FS.h> #include <FS.h>
#include <SD.h> #include <SD.h>
#include <SPI.h> #include <SPI.h>
static SPIClass sd_spi(VSPI);
static bool sd_ready = false; static bool sd_ready = false;
static const char *card_type_name(uint8_t t) { static const char *card_type_name(uint8_t t) {
@@ -60,12 +60,13 @@ bool sd_init() {
pinMode(SD_CS, OUTPUT); pinMode(SD_CS, OUTPUT);
digitalWrite(SD_CS, HIGH); digitalWrite(SD_CS, HIGH);
// 触摸 SoftSPI CS 拉高,避免干扰(脚位不同,但防误选)
pinMode(CYD_TOUCH_CS, OUTPUT); pinMode(CYD_TOUCH_CS, OUTPUT);
digitalWrite(CYD_TOUCH_CS, HIGH); digitalWrite(CYD_TOUCH_CS, HIGH);
cyd_vspi_select_sd(); sd_spi.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);
if (!SD.begin(SD_CS, cyd_vspi(), 25000000)) { if (!SD.begin(SD_CS, sd_spi, 25000000)) {
sd_ready = false; sd_ready = false;
return false; return false;
} }
@@ -76,7 +77,6 @@ bool sd_init() {
void sd_deinit() { void sd_deinit() {
if (sd_ready) { if (sd_ready) {
cyd_vspi_select_sd();
SD.end(); SD.end();
sd_ready = false; sd_ready = false;
} }
@@ -94,8 +94,6 @@ SdCardInfo sd_read_info() {
info.free_bytes = 0; info.free_bytes = 0;
info.root_files = 0; info.root_files = 0;
cyd_vspi_select_sd();
if (!sd_ready && !sd_init()) { if (!sd_ready && !sd_init()) {
info.error = "Mount failed"; info.error = "Mount failed";
return info; return info;

View File

@@ -1,12 +1,13 @@
#include "touch.h" #include "touch.h"
#include "bus_vspi.h"
#include "pins.h" #include "pins.h"
#include <XPT2046_Touchscreen.h>
#include <Arduino.h> #include <Arduino.h>
// 与 temperature 完全相同的硬件触摸驱动 /**
static XPT2046_Touchscreen touchscreen(CYD_TOUCH_CS, CYD_TOUCH_IRQ); * SoftSPI XPT2046(与 SD 的 VSPI 分脚,互不抢总线)
* 标定对齐 temperature(rot2 + map 到 UI 320x240),再反变换喂给 LVGL
* ROTATION_270(见 lv_indev.c):out=(y_in, 239-x_in)
*/
int16_t g_dbg_phys_x = -1; int16_t g_dbg_phys_x = -1;
int16_t g_dbg_phys_y = -1; int16_t g_dbg_phys_y = -1;
@@ -14,35 +15,74 @@ int16_t g_dbg_logic_x = -1;
int16_t g_dbg_logic_y = -1; int16_t g_dbg_logic_y = -1;
bool g_dbg_touch_down = false; bool g_dbg_touch_down = false;
void touch_init() { static void soft_write(uint8_t data) {
pinMode(CYD_TOUCH_IRQ, INPUT); for (int i = 7; i >= 0; --i) {
pinMode(CYD_TOUCH_CS, OUTPUT); digitalWrite(CYD_TOUCH_MOSI, (data >> i) & 1);
digitalWrite(CYD_TOUCH_CS, HIGH); digitalWrite(CYD_TOUCH_CLK, HIGH);
pinMode(SD_CS, OUTPUT); delayMicroseconds(2);
digitalWrite(SD_CS, HIGH); digitalWrite(CYD_TOUCH_CLK, LOW);
delayMicroseconds(2);
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");
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);
digitalWrite(CYD_TOUCH_CS, HIGH);
digitalWrite(CYD_TOUCH_CLK, LOW);
Serial.println("Touch: SoftSPI + temp map + LVGL inverse");
} }
void touch_read_cb(lv_indev_t * /*indev*/, lv_indev_data_t *data) { void touch_read_cb(lv_indev_t * /*indev*/, lv_indev_data_t *data) {
cyd_vspi_select_touch(); 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);
if (!(touchscreen.tirqTouched() && touchscreen.touched())) { const bool irq = (digitalRead(CYD_TOUCH_IRQ) == LOW);
g_dbg_touch_down = false; const bool in_range =
(raw_x > 80 && raw_x < 4000 && raw_y > 80 && raw_y < 4000);
if (!in_range || (!irq && (raw_x < 200 || raw_y < 200))) {
data->state = LV_INDEV_STATE_RELEASED; data->state = LV_INDEV_STATE_RELEASED;
g_dbg_touch_down = false;
return; return;
} }
TS_Point p = touchscreen.getPoint(); // XPT2046_Touchscreen setRotation(2)
const int16_t p_x = (int16_t)raw_x;
const int16_t p_y = (int16_t)(4095 - raw_y);
// 1) 与 temperature 相同:得到「屏幕上看到的」横屏坐标 320x240 // temperature 横屏 UI 坐标
int16_t lx = map(p.y, 240, 3800, 1, UI_HOR_RES); int16_t lx = map(p_y, 240, 3800, 1, UI_HOR_RES);
int16_t ly = map(p.x, 200, 3700, UI_VER_RES, 1); int16_t ly = map(p_x, 200, 3700, UI_VER_RES, 1);
if (lx < 0) { if (lx < 0) {
lx = 0; lx = 0;
@@ -57,17 +97,12 @@ void touch_read_cb(lv_indev_t * /*indev*/, lv_indev_data_t *data) {
ly = UI_VER_RES - 1; ly = UI_VER_RES - 1;
} }
// 2) LVGL 在 ROTATION_270 时还会变换一次(create 240x320): // 反变换:让 LVGL ROTATION_270 之后落到 (lx, ly)
// 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 x_in = (int16_t)((SCREEN_WIDTH - 1) - ly);
const int16_t y_in = lx; const int16_t y_in = lx;
g_dbg_phys_x = p.x; g_dbg_phys_x = p_x;
g_dbg_phys_y = p.y; g_dbg_phys_y = p_y;
g_dbg_logic_x = lx; g_dbg_logic_x = lx;
g_dbg_logic_y = ly; g_dbg_logic_y = ly;
g_dbg_touch_down = true; g_dbg_touch_down = true;
@@ -75,8 +110,8 @@ void touch_read_cb(lv_indev_t * /*indev*/, lv_indev_data_t *data) {
static uint32_t last_log = 0; static uint32_t last_log = 0;
if (millis() - last_log > 120) { if (millis() - last_log > 120) {
last_log = millis(); last_log = millis();
Serial.printf("Touch raw(%d,%d) UI(%d,%d) feed(%d,%d)\n", p.x, p.y, lx, ly, Serial.printf("Touch raw(%u,%u) UI(%d,%d) feed(%d,%d)\n", raw_x, raw_y, lx,
x_in, y_in); ly, x_in, y_in);
} }
data->point.x = x_in; data->point.x = x_in;