diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2758611a..bcc7c781 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -91,6 +91,7 @@ - { env: "NM-CYD-C5" } - { env: "CYD-3248W535C" } - { env: "CYD-4827S043R" } + - { env: "lcdwiki-es3c35p" } - { env: "lilygo-t-dongle-s3-tft" } - { env: "lilygo-t-dongle-c5-tft" } - { env: "lilygo-t-display-S3-touch" } diff --git a/boards/lcdwiki-es3c35p/connections.md b/boards/lcdwiki-es3c35p/connections.md new file mode 100644 index 00000000..46d252d5 --- /dev/null +++ b/boards/lcdwiki-es3c35p/connections.md @@ -0,0 +1,107 @@ +# lcdwiki-es3c35p pinout + +3.5" ESP32-S3 (16MB flash, 8MB PSRAM), ST77922 panel over QSPI with the +capacitive touch block built into the same controller IC. Sibling of +[ES3C28P](../ES3C28P/connections.md) (2.8", standard SPI). +Sold rebadged, e.g. "Hosyond ESP32-S3 Touchscreen Module 3.5"" (Amazon B0H28X8SQ4). + +Sources: jlmeredith/ES3C35P `docs/HARDWARE.md`, `78/xiaozhi-esp32`'s +`boards/lcdwiki-es3c35p/`, and LCDwiki's published Arduino examples +(github.com/ydedox/st77922, Example_01 for the display, `esp_lcd_st77922.c` in +Example_08 for the touch register map). + +## Display -- ST77922, QSPI, no DC pin + +| Pin | GPIO | Notes | +| --- | ---- | ----- | +| CS | 10 | | +| SCLK | 12 | | +| D0 | 11 | QSPI SIO0 / MOSI | +| D1 | 13 | QSPI SIO1 / MISO | +| D2 | 14 | QSPI SIO2 / WP | +| D3 | 9 | QSPI SIO3 / HD | +| RST | -- | No reset pin; the display half needs none. | +| BL | 41 | Backlight, active HIGH (LEDC PWM) | + +All six QSPI pins are the ESP32-S3's FSPI IOMUX pins, which the driver requires +(`SPICOMMON_BUSFLAG_IOMUX_PINS`). If SPI bus init ever fails with "not using iomux +pins", check that `TFT_CS`/`TFT_SCLK` really are 10/12 at compile time. + +**`TFT_DC=-1` and `TFT_IPS=0` are load-bearing.** `include/pre_compiler.h` has a +"CYD defaults" block that silently redefines `TFT_CS`/`TFT_SCLK` (to 15/14) when a +board defines none of `TFT_DC`, `TFT_IPS`, `TFT_COL_OFS*`, `TFT_MOSI`. A QSPI board +has no DC or MOSI pin, so it must opt out explicitly, as `platformio.ini` does. + +Driven through `Arduino_ST77922_1L4` (`TFT_DATABUS_N=6` / +`TFT_DISPLAY_DRIVER_N=52`, `lib/DisplayDrivers/src/panels/Arduino_ST77922_1L4.h`), +a raw ESP-IDF `spi_master` driver modelled on the vendor's own Example_01 -- +Arduino_GFX's `Arduino_ESP32QSPI` hardcodes every phase onto 4 lines, while this +panel wants register writes on 1 line (opcode 0x02, address `reg << 8`) and pixel +data on 4 (opcode 0x32, address 0x003C00). The init table is the vendor's active +`st77922_lcd_init[]`, unmodified. + +Panel is native 320x480 **portrait**. `ROTATION=1` is the landscape mounting; the +panel's MADCTL does not remap addressing, so rotation is done in software by +`USE_CANVAS` (Arduino_Canvas) plus the driver's transform. Orientation and colour +order (RGB, no inversion) are confirmed correct on hardware at rotation 1. + +## Touch -- built into the ST77922, I2C 0x55, NOT FT6x36-compatible + +| Pin | GPIO | Notes | +| --- | ---- | ----- | +| SDA | 38 | shared with the ES8311 audio codec (addr 0x18) | +| SCL | 39 | | +| RST | 48 | idles **LOW**; the touch half does not answer until it is released (pulsed in `_setup_gpio()`). The display does not need it. | +| INT | 47 | active-low hint; the driver also polls at ~60 Hz | + +`TOUCH_CTRL_ST77922` is a self-contained driver in `src/hal/inputs/touch.cpp` +(SensorLib has none). 16-bit big-endian register addresses, repeated-start reads: +`0x0010` info byte (bit 3 = report available), `0x0009` max touches, `0x0014` +report block of `maxTouches x 8` bytes (`[valid:1|rsvd:1|x_h:6] x_l y_h y_l area +intensity rsvd rsvd`). **The whole block must be read in one transfer** -- reading +only the first record leaves the controller returning the same stale point forever. +Reports raw panel-native portrait coordinates (x 0-319, y 0-479), so the host-side +`SwapXY`/`MirrorX`/`MirrorY` table in `interface.cpp` applies. Rotation 1 is verified +on hardware (swap + mirror Y); rows 0/2/3 were also verified by the user. + +## SD card -- native 4-bit SDIO, dedicated bus + +| Pin | GPIO | +| --- | ---- | +| CLK | 5 | +| CMD | 4 | +| D0 | 6 | +| D1 | 7 | +| D2 | 2 | +| D3 | 3 | + +Not shared with anything else on this board. Verified working with a card inserted. +With no card the boot log reports `sdmmc_init_ocr ... 0x107` / "Failed to mount +SDCARD", which is expected. + +## Other pins + +| Function | GPIO | Notes | +| -------- | ---- | ----- | +| WS2812 status LED | 40 | Single LED, driven via `neopixelWrite()` | +| SC8002B audio amp shutdown | 1 | Active LOW; held HIGH (disabled) at boot | +| BOOT button | 0 | Only physical key; doubles as Esc | +| Battery sense | 8 | ADC1, 1:2 resistor divider (`ANALOG_BAT_PIN=8`, default `ANALOG_BAT_MULTIPLIER` 2.0). Found by an ADC scan of the unclaimed pins; verified against a multimeter (ADC 2.007 V x 2 = 4.01 V vs 3.99-4.00 V measured while charging). With USB attached the charger holds the rail near 4.1 V, so it reads high and Launcher shows the charging icon. | +| Free/unclaimed | 45, 46 | Per HARDWARE.md | + +## Status + +Verified on real hardware (COM6, 2026-11-09): display (orientation, colours, +backlight PWM), capacitive touch (menu navigation, on-screen keyboard / Wi-Fi +password entry), SD card, and all four rotations (display and touch, switched at +runtime from the Launcher Rotation menu), and battery sense (GPIO 8, checked +against a multimeter with a 3.7 V 850 mAh LiPo), and Turn-off / deep sleep +(screen fully dark, wakes on BOOT; tested on USB + battery). Touch cannot wake +it: the touch INT is GPIO 47, which is not an RTC pad, so BOOT (GPIO 0) is the +only wake source. `powerOff()` in `interface.cpp` latches the backlight pin low +for the sleep and releases it in `_setup_gpio()`. Not yet verified: sleep on +battery alone (USB unplugged). + +Audio is out of scope: Launcher has no sound output on any board (no tone/I2S/ +buzzer code in `src/`). The ES8311 codec (I2C 0x18) and SC8002B amp are only +kept quiet -- the amp enable (GPIO 1) is held off at boot -- and are untested. diff --git a/boards/lcdwiki-es3c35p/interface.cpp b/boards/lcdwiki-es3c35p/interface.cpp new file mode 100644 index 00000000..c4348674 --- /dev/null +++ b/boards/lcdwiki-es3c35p/interface.cpp @@ -0,0 +1,124 @@ +#include "hal/bright/bright.h" +#include "hal/device.h" +#include "hal/inputs/touch.h" +#include "idf/launcher_platform.h" +#include "powerSave.h" +#include +#include +#include +#include + +// lcdwiki-es3c35p -- ESP32-S3 + 3.5" ST77922 (QSPI) with the capacitive touch +// block built into the same controller IC (I2C, address 0x55, 16-bit register +// map -- not FT6x36-compatible; see TOUCH_CTRL_ST77922 in touch.cpp). Sibling of +// ES3C28P, which this file mirrors closely; see that board for the same +// pattern with a standard-SPI panel and a standard-address touch chip. +// The SD card is wired to the SDIO interface, not SPI; the pins live in +// platformio.ini and sd_functions.cpp mounts it in 4-bit mode from there. + +// The touch block answers at 0x55; passed to hal_touch_init() explicitly. +#define TOUCH_I2C_ADDR 0x55 + +// BOOT button (GPIO0) -- the only physical key on the module, used as Esc. +#define BOOT_BTN 0 + +static DeviceTouch touchCfg() { + DeviceTouch cfg; + cfg.pin_sda = TOUCH_SDA; + cfg.pin_scl = TOUCH_SCL; + // The reset line is pulsed once in _setup_gpio(), before the display starts. + // It only affects the touch half of the chip, but the touch driver must not + // pulse it again after init anyway, so it is left to _setup_gpio(). + cfg.pin_rst = -1; + cfg.pin_irq = TOUCH_INT; + // rotation: 0 1 2 3 + bool swapXY[4] = {false, true, false, true}; + bool mirrorX[4] = {false, false, true, true}; + bool mirrorY[4] = {false, true, true, false}; + for (int i = 0; i < 4; i++) { + cfg.SwapXY[i] = swapXY[i]; + cfg.MirrorX[i] = mirrorX[i]; + cfg.MirrorY[i] = mirrorY[i]; + } + return cfg; +} + +void _setup_gpio() { + // Coming back from powerOff(): release the backlight pin hold it set, or the + // digitalWrite() below would be ignored and the screen would stay dark. + gpio_deep_sleep_hold_dis(); + gpio_hold_dis((gpio_num_t)TFT_BL); + + // The ST77922 is one chip doing both display and touch, and the touch half's + // reset line (GPIO 48, "TOUCH_RST") idles LOW on this board: the chip does + // not answer on I2C (0x55) until it is pulsed. Release it once, up front. + // This is for touch only -- the display half works without it. + pinMode(TOUCH_RST, OUTPUT); + digitalWrite(TOUCH_RST, LOW); + delay(20); + digitalWrite(TOUCH_RST, HIGH); + delay(200); + + // Backlight on right away (active HIGH), before the panel is initialised; + // the brightness PWM in _post_setup_gpio() takes over the pin afterwards. + pinMode(TFT_BL, OUTPUT); + digitalWrite(TFT_BL, HIGH); + + // Audio codec disabled by default (Audio_EN is active LOW) + pinMode(AUDIO_EN_PIN, OUTPUT); + digitalWrite(AUDIO_EN_PIN, HIGH); + + // WS2812 status LED off. neopixelWrite() ships with the Arduino core, so the + // board needs no external LED library. + neopixelWrite(RGB_LED_PIN, 0, 0, 0); + + // BOOT button doubles as the Esc key + launcherGpioInputPullup(BOOT_BTN); +} + +void _post_setup_gpio() { + // Backlight PWM -- must be done after tft.init() + hal_bright_attach(TFT_BL); + hal_bright_set(TFT_BL, bright); + + // Capacitive touch, at this board's nonstandard I2C address. + hal_touch_init(touchCfg(), TOUCH_I2C_ADDR); +} + +void _setBrightness(uint8_t brightval) { hal_bright_set(TFT_BL, brightval); } + +void InputHandler(void) { + static long tm = launcherMillis(); + if (launcherMillis() - tm > 250 || LongPress) { + LTouchPoint t; + checkPowerSaveTime(); + if (hal_touch_read(touchCfg(), t)) { + tm = launcherMillis(); + if (!hal_touch_apply(t)) return; + } else if (launcherGpioRead(BOOT_BTN) == LOW) { + tm = launcherMillis(); + if (!wakeUpScreen()) { + AnyKeyPress = true; + EscPress = true; + } + } + } +} + +void powerOff() { + neopixelWrite(RGB_LED_PIN, 0, 0, 0); + + // Backlight off and latched off for the duration of deep sleep; the pad would + // otherwise float once the LEDC peripheral loses power. Released in _setup_gpio(). + pinMode(TFT_BL, OUTPUT); + digitalWrite(TFT_BL, LOW); + gpio_hold_en((gpio_num_t)TFT_BL); + gpio_deep_sleep_hold_en(); + + // The BOOT button (GPIO0, an RTC pad) is the only possible wake source: the + // touch interrupt is on GPIO47, which is not an RTC GPIO, so a touch cannot + // wake a deep sleep. Without this the board only restarts via reset / power. + esp_sleep_enable_ext0_wakeup(GPIO_NUM_0, LOW); + vTaskDelay(pdMS_TO_TICKS(200)); + esp_deep_sleep_start(); +} diff --git a/boards/lcdwiki-es3c35p/platformio.ini b/boards/lcdwiki-es3c35p/platformio.ini new file mode 100644 index 00000000..7ea48e0f --- /dev/null +++ b/boards/lcdwiki-es3c35p/platformio.ini @@ -0,0 +1,102 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/platformio.ini/page/projectconf.html + +##################### lcdwiki-es3c35p (ESP32-S3 + 3.5" ST77922 QSPI + built-in cap touch) #################### +; 3.5" ESP32-S3 IPS LCD Display Module, 320x480, quad-SPI (no DC pin) ST77922 +; panel, sold rebadged as "Hosyond ESP32-S3 Touchscreen Module 3.5"" (Amazon +; B0H28X8SQ4) among others. Sibling of ES3C28P (2.8", standard 4-wire SPI) -- +; see boards/ES3C28P for that board's config, most of which this one mirrors. +; https://www.lcdwiki.com/3.5inch_ESP32-S3_Display +; +; The panel is driven by Arduino_ST77922_1L4 (TFT_DATABUS_N=6 / +; TFT_DISPLAY_DRIVER_N=52, lib/DisplayDrivers/src/panels/Arduino_ST77922_1L4.h), +; and the built-in touch block by TOUCH_CTRL_ST77922 (src/hal/inputs/touch.cpp). +; Tested on hardware: display, touch, SD, all four rotations, battery sense, +; Turn-off / wake. See connections.md. +[env:lcdwiki-es3c35p] +board = esp32s3 +board_build.partitions = support_files/custom_16Mb.csv +build_src_filter = ${env.build_src_filter} +<../boards/lcdwiki-es3c35p> +build_flags = + ${env.build_flags} + -DBOARD_HAS_PSRAM + -DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_USB_MODE=1 + + -DDEVICE_NAME='"lcdwiki-es3c35p"' + -DOTA_TAG='"lcdwiki_es3c35p"' + + ; Panel native size is 320x480 portrait; ROTATION=1 is the confirmed + ; landscape mounting (see Arduino_ST77922_1L4.h's rotation-transform note). + -DROTATION=1 + + -DHAS_TOUCH=1 + -DTOUCH_CTRL_ST77922=1 # self-contained driver in src/hal/inputs/touch.cpp (NOT FT6x36-compatible) + ; TFT -- ST77922 320x480 over QSPI, no DC pin. This is NOT the same bus + ; as ES3C28P's Arduino_HWSPI (0): this panel's command phase is a single + ; QSPI line while its data phase is 4, which Arduino_GFX's own QSPI bus + ; (TFT_DATABUS_N=1) cannot do -- see Arduino_ST77922_1L4.h. + ; Draw into a full-screen PSRAM canvas and flush it in one transfer. Without + ; it every pixel is its own SPI transaction (~20 s to paint the boot screen). + ; The canvas also owns rotation, which suits a panel that can't rotate itself. + -DUSE_CANVAS=1 + -D TFT_DATABUS_N=6 # Arduino_ST77922_1L4 (QSPI, single-line command phase) + -D TFT_DISPLAY_DRIVER_N=52 # Arduino_ST77922_1L4 + -DTFT_WIDTH=320 + -DTFT_HEIGHT=480 + ; QSPI has no DC pin, but defining TFT_DC (even as -1) is what stops + ; include/pre_compiler.h's "CYD defaults" block from overriding TFT_CS/TFT_SCLK + ; below with 15/14 -- it fires when none of TFT_DC/TFT_IPS/TFT_COL_OFS*/TFT_MOSI is set. + -DTFT_DC=-1 + -DTFT_IPS=0 + -DTFT_CS=10 + -DTFT_SCLK=12 + -DTFT_D0=11 + -DTFT_D1=13 + -DTFT_D2=14 + -DTFT_D3=9 + -DTFT_RST=-1 ; no reset pin -- RST is tied to CHIP_PU on this board (see connections.md) + -DTFT_BL=41 + + ; Backlight PWM (TFT_BRIGHT_CHANNEL/Bits/FREQ come from pre_compiler.h) + -DDIMMER_SETUP=20 + + ; Capacitive touch -- built into the ST77922 itself, I2C 0x55 with a 16-bit + ; register map; shared I2C bus with the ES8311 codec. + -DTOUCH_SDA=38 + -DTOUCH_SCL=39 + -DTOUCH_RST=48 + -DTOUCH_INT=47 + + ; SD card -- native 4-bit SDIO, not shared with anything else on this board. + -DUSE_SD_MMC=1 + -DSDM=SD_MMC + -DSD_MMC_4BIT=1 + -DPIN_SD_CLK=5 + -DPIN_SD_CMD=4 + -DPIN_SD_D0=6 + -DPIN_SD_D1=7 + -DPIN_SD_D2=2 + -DPIN_SD_D3=3 + + ; WS2812 status LED, driven by the core's neopixelWrite() -- no extra library + -DRGB_LED_PIN=40 + + ; SC8002B audio amplifier shutdown line (active LOW). Held off at boot; + ; the I2S pins are unused by the launcher. + -DAUDIO_EN_PIN=1 + + ; Battery sense: GPIO 8 reads ~2.0 V with a ~4.0 V LiPo attached, i.e. a 1:2 + ; divider (Launcher's default ANALOG_BAT_MULTIPLIER of 2.0 applies). Found by an + ; ADC scan and confirmed against a multimeter (see connections.md). + -DANALOG_BAT_PIN=8 + +lib_ignore = + XPowersLib diff --git a/lib/DisplayDrivers b/lib/DisplayDrivers index 79d2dc75..95c1db90 160000 --- a/lib/DisplayDrivers +++ b/lib/DisplayDrivers @@ -1 +1 @@ -Subproject commit 79d2dc757d0684a3f63ab027920edd152972f93f +Subproject commit 95c1db9003a3b3de541ca148328f3c23e6056269 diff --git a/platformio.ini b/platformio.ini index b3163407..a5d64e51 100644 --- a/platformio.ini +++ b/platformio.ini @@ -47,6 +47,7 @@ default_envs = ; headless-xiao-esp32s3-16mb ; headless-xiao-esp32c5-8mb ; heltec-lora32-expansion-v2 + ; lcdwiki-es3c35p ; lilygo-t5-epaper-s3-pro ; lilygo-t-deck ; lilygo-t-deck-plus diff --git a/src/hal/inputs/touch.cpp b/src/hal/inputs/touch.cpp index 5e9c6490..b3b874b2 100644 --- a/src/hal/inputs/touch.cpp +++ b/src/hal/inputs/touch.cpp @@ -153,10 +153,85 @@ struct TouchDrvAXS5106L { void reset() {} }; static TouchDrvAXS5106L touch; + +#elif defined(TOUCH_CTRL_ST77922) +#include +// ST77922 (lcdwiki-es3c35p). The touch block lives inside the display controller +// and is NOT FT6x36-compatible: register addresses are 16 bits wide (MSB first) +// and reads use a repeated start. SensorLib has no driver for it, so, like +// AXS5106L above, the whole driver lives here. Layout (from the vendor's +// esp_lcd_touch_st77922): +// 0x0010 info byte: bit 3 = "a coordinate report is available" +// 0x0014 8-byte report per touch: [valid:1|rsvd:1|x_h:6] x_l y_h y_l area intensity rsvd rsvd +// Coordinates are reported raw (panel-native portrait), so the host-side +// SwapXY/MirrorX/MirrorY path applies, same as FT6X36/AXS5106L. +#define ST77922_TOUCH_ADDRESS 0x55 +#define ST77922_REG_FW_VERSION 0x0000 +#define ST77922_REG_MAX_XY 0x0005 // max X (2 bytes), max Y (2 bytes), max touches (1 byte) +#define ST77922_REG_MAX_TOUCHES 0x0009 +#define ST77922_MAX_TOUCHES 10 // upper bound for the on-stack report buffer +#define ST77922_REG_INFO 0x0010 +#define ST77922_REG_REPORT 0x0014 +#define ST77922_INFO_WITH_COORD 0x08 +#define ST77922_REPORT_VALID 0x80 + +struct TouchDrvST77922 { + TwoWire *wire = nullptr; + int8_t irq = -1; + uint8_t addr = ST77922_TOUCH_ADDRESS; + uint32_t lastPoll = 0; + + bool readReg(uint16_t reg, uint8_t *buf, size_t len) { + if (!wire) return false; + wire->beginTransmission(addr); + wire->write((uint8_t)(reg >> 8)); + wire->write((uint8_t)(reg & 0xFF)); + if (wire->endTransmission(false) != 0) return false; // repeated start, no STOP + if (wire->requestFrom((uint8_t)addr, len) < len) return false; + for (size_t i = 0; i < len; ++i) buf[i] = (uint8_t)wire->read(); + return true; + } + + bool getPoint(int16_t *x, int16_t *y, uint8_t) { + // INT is an active-low hint; poll at ~60Hz when it is not asserted so a + // module that never pulls it low still works. + const uint32_t now = launcherMillis(); + if (irq >= 0 && launcherGpioRead(irq) != LOW && (uint32_t)(now - lastPoll) < 16) return false; + lastPoll = now; + + uint8_t info = 0; + if (!readReg(ST77922_REG_INFO, &info, 1)) return false; + if (!(info & ST77922_INFO_WITH_COORD)) return false; + // Read the whole report block in one transfer, as the vendor driver does + // (max-touches x 8 bytes): the controller does not refresh its report + // buffer if only the first record is read, so a partial read leaves the + // same stale point reported forever. + uint8_t maxTouches = 0; + if (!readReg(ST77922_REG_MAX_TOUCHES, &maxTouches, 1)) return false; + if (maxTouches == 0 || maxTouches > ST77922_MAX_TOUCHES) maxTouches = ST77922_MAX_TOUCHES; + uint8_t r[ST77922_MAX_TOUCHES * 8]; + if (!readReg(ST77922_REG_REPORT, r, (size_t)maxTouches * 8)) return false; + int p = -1; + for (uint8_t i = 0; i < maxTouches; ++i) { + if (r[i * 8] & ST77922_REPORT_VALID) { + p = i * 8; + break; + } + } + if (p < 0) return false; + *x = (int16_t)(((r[p] & 0x3F) << 8) | r[p + 1]); + *y = (int16_t)((r[p + 2] << 8) | r[p + 3]); + return true; + } + + void reset() {} +}; +static TouchDrvST77922 touch; #endif #if defined(TOUCH_CTRL_GT911) || defined(TOUCH_CTRL_CST8XX) || defined(TOUCH_CTRL_FT6X36) || \ - defined(TOUCH_CTRL_GT9895) || defined(TOUCH_CTRL_HI8561) || defined(TOUCH_CTRL_AXS5106L) + defined(TOUCH_CTRL_GT9895) || defined(TOUCH_CTRL_HI8561) || defined(TOUCH_CTRL_AXS5106L) || \ + defined(TOUCH_CTRL_ST77922) #include static TwoWire &wireFor(const DeviceTouch &cfg) { return cfg.i2c_bus ? *static_cast(cfg.i2c_bus) : Wire; @@ -271,6 +346,35 @@ bool hal_touch_init(const DeviceTouch &cfg, uint8_t i2c_addr, bool xpt_shared_sp uint8_t probe[AXS5106L_REPORT_LEN]; return touch.readReport(probe); } +#elif defined(TOUCH_CTRL_ST77922) + (void)xpt_shared_spi; + if (cfg.pin_sda >= 0 && cfg.pin_scl >= 0) wireFor(cfg).begin(cfg.pin_sda, cfg.pin_scl); + wireFor(cfg).setClock(400000); + // The touch half of the ST77922 idles in reset on this board; the board's + // _setup_gpio() releases that line (GPIO 48) before this runs, so pin_rst + // stays -1 here and touchResetPulse() only handles an optional reset_cb. + touchResetPulse(cfg); + if (cfg.pin_irq >= 0) launcherGpioInputPullup(cfg.pin_irq); + touch.wire = &wireFor(cfg); + touch.irq = cfg.pin_irq; + touch.addr = i2c_addr ? i2c_addr : ST77922_TOUCH_ADDRESS; + // Probe with real register reads and report what the chip says about itself. + { + uint8_t ver = 0, info[5] = {0}; + if (!touch.readReg(ST77922_REG_FW_VERSION, &ver, 1) || + !touch.readReg(ST77922_REG_MAX_XY, info, sizeof(info))) { + launcherConsolePrintf("[ST77922 touch] no answer at 0x%02X\n", touch.addr); + return false; + } + launcherConsolePrintf( + "[ST77922 touch] fw=%u maxX=%u maxY=%u maxTouches=%u\n", + ver, + (info[0] << 8) | info[1], + (info[2] << 8) | info[3], + info[4] + ); + return true; + } #else (void)cfg; (void)i2c_addr; @@ -282,7 +386,7 @@ bool hal_touch_init(const DeviceTouch &cfg, uint8_t i2c_addr, bool xpt_shared_sp bool hal_touch_read(const DeviceTouch &cfg, LTouchPoint &out) { #if defined(TOUCH_CTRL_XPT2046) || defined(TOUCH_CTRL_GT911) || defined(TOUCH_CTRL_CST8XX) || \ defined(TOUCH_CTRL_FT6X36) || defined(TOUCH_CTRL_GT9895) || defined(TOUCH_CTRL_HI8561) || \ - defined(TOUCH_CTRL_AXS5106L) + defined(TOUCH_CTRL_AXS5106L) || defined(TOUCH_CTRL_ST77922) uint8_t r = rotation & 0x03; int16_t screenW, screenH; panelSize(screenW, screenH); @@ -296,11 +400,12 @@ bool hal_touch_read(const DeviceTouch &cfg, LTouchPoint &out) { if (!touch.touched()) return false; auto p = touch.getPointScaled(); int16_t x = p.x, y = p.y; -#elif defined(TOUCH_CTRL_FT6X36) || defined(TOUCH_CTRL_AXS5106L) +#elif defined(TOUCH_CTRL_FT6X36) || defined(TOUCH_CTRL_AXS5106L) || defined(TOUCH_CTRL_ST77922) int16_t x = 0, y = 0; if (!touch.getPoint(&x, &y, 1)) return false; #endif -#if defined(TOUCH_CTRL_XPT2046) || defined(TOUCH_CTRL_FT6X36) || defined(TOUCH_CTRL_AXS5106L) +#if defined(TOUCH_CTRL_XPT2046) || defined(TOUCH_CTRL_FT6X36) || defined(TOUCH_CTRL_AXS5106L) || \ + defined(TOUCH_CTRL_ST77922) if (cfg.SwapXY[r]) { int16_t t = x; x = y; @@ -354,7 +459,8 @@ bool hal_touch_read_raw(LTouchPoint &out) { out.pressed = true; return true; #elif defined(TOUCH_CTRL_GT911) || defined(TOUCH_CTRL_CST8XX) || defined(TOUCH_CTRL_FT6X36) || \ - defined(TOUCH_CTRL_GT9895) || defined(TOUCH_CTRL_HI8561) || defined(TOUCH_CTRL_AXS5106L) + defined(TOUCH_CTRL_GT9895) || defined(TOUCH_CTRL_HI8561) || defined(TOUCH_CTRL_AXS5106L) || \ + defined(TOUCH_CTRL_ST77922) int16_t x = 0, y = 0; if (!touch.getPoint(&x, &y, 1)) return false; out.x = x;