diff --git a/CHANGELOG.md b/CHANGELOG.md index 54d7ca5..3aea755 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,16 @@ # Changelog +## 2026-10-17 — Touch = draw coords (CYD LVGL9 official) + +- Root cause: `LV_DISPLAY_ROTATION_270` double-transforms touch vs TFT_eSPI + (lvgl#7841 / CYD community). +- Fix per witnessmenow LVGL9 example: create display 320x240, rotation 0; + SoftSPI `setRotation(3)` map straight into the same 320x240 space. +- Red touch cursor follows reported point for visual check. + ## 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. diff --git a/firmware/cyd/VERSION.txt b/firmware/cyd/VERSION.txt index e4db89e..794aa17 100644 --- a/firmware/cyd/VERSION.txt +++ b/firmware/cyd/VERSION.txt @@ -1,5 +1,5 @@ name: CYD TF card + touch speaker (cyd) -git: aa08b23 +git: 3768e54 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 3A23773415A312ABE3DAE81652F22C618A0839C8DCCA77B10AF16682DD5E2BD0 + firmware.bin DC7BD1738756078B002918725F5CE6C6B349ADBF8E9ADC0A209668D07C0F00ED partitions.bin 148B959CBFF1C38AA8E1D5C0BA9D612C54997B945E56A63F41223EEF650653A1 diff --git a/firmware/cyd/firmware.bin b/firmware/cyd/firmware.bin index 0263d63..631e7d7 100644 Binary files a/firmware/cyd/firmware.bin and b/firmware/cyd/firmware.bin differ diff --git a/include/pins.h b/include/pins.h index eb26129..1714d99 100644 --- a/include/pins.h +++ b/include/pins.h @@ -21,10 +21,8 @@ #define CYD_TOUCH_CLK 25 #define CYD_TOUCH_CS 33 -// 物理竖屏分辨率(TFT 驱动) -#define SCREEN_WIDTH 240 -#define SCREEN_HEIGHT 320 - -// 横屏逻辑分辨率(LV_DISPLAY_ROTATION_270) -#define UI_HOR_RES 320 -#define UI_VER_RES 240 +// 横屏 UI(与 witnessmenow CYD LVGL9 官方例程一致:直接 320x240,不用 LVGL 二次旋转) +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 240 +#define UI_HOR_RES SCREEN_WIDTH +#define UI_VER_RES SCREEN_HEIGHT diff --git a/src/main.cpp b/src/main.cpp index b97c600..3111e24 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,10 +45,10 @@ void setup() { lv_init(); + // 官方 CYD LVGL9:直接 320x240,不用 LV_DISPLAY_ROTATION_*(避免触控二次变换错位) + // https://github.com/witnessmenow/ESP32-Cheap-Yellow-Display/tree/main/Examples/LVGL9 lv_display_t *disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf)); - // 与 temperature 相同:TFT rot3 + LVGL 270;触点先算 UI 坐标再反变换喂给 LVGL - lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270); lv_indev_t *indev = lv_indev_create(); lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); diff --git a/src/touch.cpp b/src/touch.cpp index 6948c98..c9218e9 100644 --- a/src/touch.cpp +++ b/src/touch.cpp @@ -4,9 +4,12 @@ #include /** - * SoftSPI XPT2046(与 SD 的 VSPI 分脚,互不抢总线) - * 标定对齐 temperature(rot2 + map 到 UI 320x240),再反变换喂给 LVGL - * ROTATION_270(见 lv_indev.c):out=(y_in, 239-x_in) + * SoftSPI XPT2046 — 与 SD 分脚 + * + * 对齐官方 CYD LVGL9 例程(witnessmenow): + * - 显示直接建为 320x240,LVGL rotation=0(触控不再被二次变换) + * - touch setRotation(3) + map 到 SCREEN_WIDTH/HEIGHT + * 参考:https://github.com/witnessmenow/ESP32-Cheap-Yellow-Display/blob/main/Examples/LVGL9/ */ int16_t g_dbg_phys_x = -1; @@ -57,64 +60,60 @@ void touch_init() { pinMode(CYD_TOUCH_IRQ, INPUT); digitalWrite(CYD_TOUCH_CS, HIGH); digitalWrite(CYD_TOUCH_CLK, LOW); - Serial.println("Touch: SoftSPI + temp map + LVGL inverse"); + Serial.println("Touch: SoftSPI rot3 map 320x240 (no LVGL rot)"); } 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); + uint16_t adc_x = xpt_read(0x90); + uint16_t adc_y = xpt_read(0xD0); + adc_x = (uint16_t)((adc_x + xpt_read(0x90)) / 2); + adc_y = (uint16_t)((adc_y + xpt_read(0xD0)) / 2); const bool irq = (digitalRead(CYD_TOUCH_IRQ) == LOW); const bool in_range = - (raw_x > 80 && raw_x < 4000 && raw_y > 80 && raw_y < 4000); + (adc_x > 80 && adc_x < 4000 && adc_y > 80 && adc_y < 4000); - if (!in_range || (!irq && (raw_x < 200 || raw_y < 200))) { + if (!in_range || (!irq && (adc_x < 200 || adc_y < 200))) { data->state = LV_INDEV_STATE_RELEASED; g_dbg_touch_down = false; return; } - // XPT2046_Touchscreen setRotation(2) - const int16_t p_x = (int16_t)raw_x; - const int16_t p_y = (int16_t)(4095 - raw_y); + // XPT2046_Touchscreen setRotation(3): + // xraw = 4095 - Y_sample, yraw = 4095 - X_sample + const int16_t p_x = (int16_t)(4095 - adc_y); + const int16_t p_y = (int16_t)(4095 - adc_x); - // temperature 横屏 UI 坐标 - 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 x = map(p_x, 200, 3700, 1, SCREEN_WIDTH); + int16_t y = map(p_y, 240, 3800, 1, SCREEN_HEIGHT); - if (lx < 0) { - lx = 0; + if (x < 0) { + x = 0; } - if (ly < 0) { - ly = 0; + if (y < 0) { + y = 0; } - if (lx >= UI_HOR_RES) { - lx = UI_HOR_RES - 1; + if (x >= SCREEN_WIDTH) { + x = SCREEN_WIDTH - 1; } - if (ly >= UI_VER_RES) { - ly = UI_VER_RES - 1; + if (y >= SCREEN_HEIGHT) { + y = SCREEN_HEIGHT - 1; } - // 反变换:让 LVGL ROTATION_270 之后落到 (lx, ly) - const int16_t x_in = (int16_t)((SCREEN_WIDTH - 1) - ly); - const int16_t y_in = lx; - g_dbg_phys_x = p_x; g_dbg_phys_y = p_y; - g_dbg_logic_x = lx; - g_dbg_logic_y = ly; + g_dbg_logic_x = x; + g_dbg_logic_y = y; g_dbg_touch_down = true; static uint32_t last_log = 0; if (millis() - last_log > 120) { last_log = millis(); - Serial.printf("Touch raw(%u,%u) UI(%d,%d) feed(%d,%d)\n", raw_x, raw_y, lx, - ly, x_in, y_in); + Serial.printf("Touch adc(%u,%u) p(%d,%d) -> (%d,%d)\n", adc_x, adc_y, p_x, + p_y, x, y); } - data->point.x = x_in; - data->point.y = y_in; + data->point.x = x; + data->point.y = y; data->state = LV_INDEV_STATE_PRESSED; } diff --git a/src/ui.cpp b/src/ui.cpp index 741d2a9..b2bad01 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -14,6 +14,7 @@ static lv_obj_t *btn_play; static lv_obj_t *lbl_speaker; static lv_obj_t *lbl_play; static lv_obj_t *lbl_touch_dbg; +static lv_obj_t *touch_cursor; static lv_obj_t *make_row(lv_obj_t *parent, const char *title, lv_obj_t **value_out) { lv_obj_t *row = lv_obj_create(parent); @@ -155,6 +156,18 @@ void ui_create() { lv_obj_set_style_text_font(lbl_touch_dbg, &lv_font_montserrat_14, 0); lv_obj_set_style_text_color(lbl_touch_dbg, lv_color_hex(0xFFB74D), 0); + // 触点光标:落在哪画在哪,验证与按钮重合 + touch_cursor = lv_obj_create(scr); + lv_obj_set_size(touch_cursor, 16, 16); + lv_obj_set_style_radius(touch_cursor, LV_RADIUS_CIRCLE, 0); + lv_obj_set_style_bg_color(touch_cursor, lv_color_hex(0xFF5252), 0); + lv_obj_set_style_bg_opa(touch_cursor, LV_OPA_COVER, 0); + lv_obj_set_style_border_width(touch_cursor, 2, 0); + lv_obj_set_style_border_color(touch_cursor, lv_color_hex(0xFFFFFF), 0); + lv_obj_add_flag(touch_cursor, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(touch_cursor, LV_OBJ_FLAG_CLICKABLE); + lv_obj_clear_flag(touch_cursor, LV_OBJ_FLAG_SCROLLABLE); + ui_refresh_audio_buttons(); } @@ -164,11 +177,19 @@ void ui_set_touch_debug(int16_t lx, int16_t ly, bool down) { } if (!down) { lv_label_set_text(lbl_touch_dbg, "Touch: up"); + if (touch_cursor) { + lv_obj_add_flag(touch_cursor, LV_OBJ_FLAG_HIDDEN); + } return; } char buf[40]; snprintf(buf, sizeof(buf), "Touch L:%d,%d", (int)lx, (int)ly); lv_label_set_text(lbl_touch_dbg, buf); + if (touch_cursor) { + lv_obj_clear_flag(touch_cursor, LV_OBJ_FLAG_HIDDEN); + lv_obj_set_pos(touch_cursor, lx - 8, ly - 8); + lv_obj_move_foreground(touch_cursor); + } } void ui_update(const SdCardInfo &info) {