diff --git a/CHANGELOG.md b/CHANGELOG.md index 82ee745..f572897 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/firmware/cyd/VERSION.txt b/firmware/cyd/VERSION.txt index 4d63276..2f3ae3c 100644 --- a/firmware/cyd/VERSION.txt +++ b/firmware/cyd/VERSION.txt @@ -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 diff --git a/firmware/cyd/firmware.bin b/firmware/cyd/firmware.bin index 87b8922..300b5e9 100644 Binary files a/firmware/cyd/firmware.bin and b/firmware/cyd/firmware.bin differ diff --git a/include/bus_vspi.h b/include/bus_vspi.h new file mode 100644 index 0000000..a853d44 --- /dev/null +++ b/include/bus_vspi.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +// CYD:触摸与 SD 共用 VSPI,按需切换引脚 +SPIClass &cyd_vspi(); +void cyd_vspi_select_touch(); +void cyd_vspi_select_sd(); diff --git a/platformio.ini b/platformio.ini index 5d72fc5..1d2bfd0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -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 diff --git a/src/bus_vspi.cpp b/src/bus_vspi.cpp new file mode 100644 index 0000000..6db60bd --- /dev/null +++ b/src/bus_vspi.cpp @@ -0,0 +1,26 @@ +#include "bus_vspi.h" +#include "pins.h" + +#include + +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; +} diff --git a/src/main.cpp b/src/main.cpp index 24aa43c..b97c600 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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(); diff --git a/src/sd_info.cpp b/src/sd_info.cpp index 3ee300a..c127d37 100644 --- a/src/sd_info.cpp +++ b/src/sd_info.cpp @@ -1,11 +1,11 @@ #include "sd_info.h" +#include "bus_vspi.h" #include "pins.h" #include #include #include -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; diff --git a/src/touch.cpp b/src/touch.cpp index 256cf1b..f11bbec 100644 --- a/src/touch.cpp +++ b/src/touch.cpp @@ -1,14 +1,12 @@ #include "touch.h" +#include "bus_vspi.h" #include "pins.h" +#include #include -/** - * 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; }