diff --git a/README.md b/README.md index bddec45..aed5014 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ When using Bambu Cloud, BambuHelper connects through Bambu Lab's cloud MQTT serv | ![QD ES3N28P 2.8 inch](img/es3n28p-2.8.png) | **QD ES3N28P 2.8"** (ILI9341V) | `240x320` **ILI9341V** IPS panel over 4-wire SPI with ESP32-S3-N16R8 (`16MB` flash / `8MB` PSRAM) and **FT6336** capacitive touch on I2C. Use the `es3n28p` firmware build. Reuses the same `240x320` layout as the Waveshare 2"/2.8" boards and supports **up to 2 printers** (up to 4 with the experimental opt-in). The board carries an onboard WS2812 and ES8311 audio that the firmware does not drive, and the buzzer is disabled by default because its default pin is an I2S clock line. Sold as the QD electronic 2.8" IPS ESP32-S3 module (board codes ES3C28P / ES3N28P); see issue [#125](https://github.com/Keralots/BambuHelper/issues/125). | [@gwbuss](https://github.com/gwbuss) | | ![Waveshare 3.5 inch](img/ws_lcd_350.png) | **Waveshare ESP32-S3-Touch-LCD-3.5** | `320x480` **ST7796** IPS panel over plain 4-wire SPI (native LovyanGFX, not the Guition board's AXS15231B QSPI), ESP32-S3R8 (`16MB` flash / `8MB` PSRAM), and **FT6336** capacitive touch on I2C. The LCD reset line is driven through an on-board **TCA9554** I2C IO expander rather than a GPIO. Use the `ws_lcd_350` firmware build. Reuses the same `320x480` layout as the JC3248W535 and supports **up to 2 printers** (up to 4 with the experimental opt-in). The board has no buzzer hardware. Pinout from the [Waveshare wiki](https://www.waveshare.com/wiki/ESP32-S3-Touch-LCD-3.5). Product page: [waveshare.com/product/esp32-s3-touch-lcd-3.5.htm](https://www.waveshare.com/product/esp32-s3-touch-lcd-3.5.htm) | _community_ | | ![Panlee SC05_X](img/sc05_x.jpg) | **Panlee SC05_X / ZX2D80CE02S 2.8"** | `240x320` **ST7789** IPS panel driven over an **8-bit 8080 parallel** bus, ESP32-S3 with `8MB` flash / QSPI PSRAM, and **FT5X06** capacitive touch on I2C. Use the `sc05_x` firmware build. This is not the 3.5" WT32-SC01 Plus; selecting the `wt32_sc01_plus` firmware on this board leaves the screen black because the LCD driver, resolution, backlight, reset, touch, and data pins are different. The onboard USB-C connector carries native ESP32-S3 USB data for serial and Improv; the separate 7-pin debug header exposes UART0, EN, and BOOT for the vendor's external ZXACC-ESPDB flashing adapter. Pinout and photo from the official Wireless-Tag / PanelLan datasheet and PanelLan Arduino library. | [@nunnypern](https://github.com/nunnypern) | +| _(no photo yet)_ | **LCDwiki ES3C35P 3.5"** | `320x480` **ST77922 QSPI** panel with capacitive touch built into the display IC (I2C `0x55`), ESP32-S3 with `16MB` flash / `8MB` octal PSRAM. Use the `es3c35p` firmware build. Reuses the same `320x480` layout as the JC3248W535 and supports **up to 2 printers** (up to 4 with the experimental opt-in). Has a battery indicator (GPIO8, 1:2 divider) and a **WS2812 status LED** on GPIO40. The board has no buzzer; the audio amp (ES8311) is not supported yet. The ST77922 needs register writes on one QSPI line and pixels on four, so it uses its own small LovyanGFX panel class. Tested on a single unit. | _community_ | | ![Panlee WT32-SC01 Plus](img/SC01Plus3.5.png) | **Panlee WT32-SC01 Plus 3.5"** | `320x480` **ST7796** IPS panel driven over an **8-bit 8080 parallel** bus (the project's first `Bus_Parallel8` board, not SPI or QSPI), ESP32-S3-N16R8 (`16MB` flash / `8MB` PSRAM), and **FT6336** capacitive touch on I2C. Use the `wt32_sc01_plus` firmware build. Reuses the same `320x480` layout as the JC3248W535 and supports **up to 2 printers** (up to 4 with the experimental opt-in). The board has an I2S audio amp, but the buzzer is disabled by default because its default pin is the touch I2C clock line. Sold as the *WT32-SC01 Plus*. | [@cliomjh](https://github.com/cliomjh) | | ![Waveshare 2.8C round](img/ws_lcd_28c.jpg) | **Waveshare ESP32-S3-Touch-LCD-2.8C** | `480x480` **round** **ST7701** IPS panel on the ESP32-S3's LCD_CAM RGB bus (16-bit parallel, with a 3-wire SPI command interface), ESP32-S3R8 (`16MB` flash / `8MB` PSRAM), and **GT911** capacitive touch. Use the `ws_lcd_28c` firmware build. Gets its own `480x480` round layout - the 240x240 round dashboard at exactly 2x, with a larger font tier - and supports **up to 2 printers** (up to 4 with the experimental opt-in). LCD reset, LCD CS, touch reset and the buzzer all hang off an on-board **TCA9554** I2C IO expander; the buzzer is therefore on/off only, so melodies play as rhythm without pitch. Battery voltage is read on GPIO4. IMU, RTC and microSD are not wired up in firmware. Hardware-confirmed by the requester in issue [#171](https://github.com/Keralots/BambuHelper/issues/171). The RGB pixel clock runs at a conservative 12 MHz and is an `.ini` knob. Product page: [waveshare.com/esp32-s3-touch-lcd-2.8c.htm](https://www.waveshare.com/esp32-s3-touch-lcd-2.8c.htm) | _community_ | | ![CYD TZT variant](img/CYD_tzt.png) | **CYD / TZT L1435-2.4** (ST7789) | Looks almost identical to the standard CYD, but uses a `240x320` **ST7789V** panel (instead of ILI9341) and the backlight is on GPIO27. Use the `tzt_2432` firmware build - the regular `cyd` build will give you a black screen on this hardware. Due to RAM limits, this board supports **1 printer only**. Often sold on Aliexpress as *"TZT ESP32 LVGL 2.4 inch LCD TFT 240*320 With Touch"*. | _community_ | @@ -484,6 +485,7 @@ The device reboots automatically once the update is written; the web page reload | ESP32-S3 Super Mini | `BambuHelper-esp32s3-v3.9.2-Full.bin` | | ESP32-S3 Super Mini + 1.28" GC9A01 round | `BambuHelper-esp32s3_round-v3.9.2-Full.bin` | | Guition JC3248W535 | `BambuHelper-jc3248w535-v3.9.2-Full.bin` | +| LCDwiki ES3C35P 3.5" | `BambuHelper-es3c35p-v3.9.2-Full.bin` | | Waveshare ESP32-S3-Zero | `BambuHelper-esp32s3_zero-v3.9.2-Full.bin` | | Waveshare ESP32-S3-Zero + 2.0" 240x320 panel | `BambuHelper-esp32s3_zero_320-v3.9.2-Full.bin` | | CYD / ESP32-2432S028 | `BambuHelper-cyd-v3.9.2-Full.bin` | diff --git a/boards/lcdwiki_es3c35p.ini b/boards/lcdwiki_es3c35p.ini new file mode 100644 index 0000000..12ac67e --- /dev/null +++ b/boards/lcdwiki_es3c35p.ini @@ -0,0 +1,60 @@ +; ============================================================================= +; LCDwiki ES3C35P (3.5" ESP32-S3, ST77922 320x480 QSPI + built-in cap touch) +; https://www.lcdwiki.com/3.5inch_ESP32-S3_Display +; Sold under several names, e.g. Hosyond "ESP32-S3 Touchscreen Module 3.5"" and +; the AliExpress "cheap black display" listings. +; +; Core: ESP32-S3 N16R8 (16 MB flash, PSRAM) - standalone env, like ws_lcd_350. +; +; Display: ST77922 over quad-SPI with NO DC pin. Register writes go out on one +; data line and pixel data on four, which neither LovyanGFX's SPI bus nor +; Arduino_GFX's Arduino_ESP32QSPI can produce, so src/lgfx_panel_st77922_qspi.hpp +; drives the SPI device itself. Pins (CS=10 SCLK=12 D0=11 D1=13 D2=14 D3=9) are +; fixed in that class. No reset pin (tied to the chip's own reset). +; Like jc3248w535 it draws into a full-frame PSRAM sprite pushed once per tick +; (PANEL_REQUIRES_AXS_FRAME_SPRITE); the panel itself stays at rotation 0. +; +; Touch: built into the ST77922 itself (I2C 0x55, 16-bit register addresses), +; NOT FT6x36-compatible -> src/button_touch_st77922.cpp. RST (GPIO48) idles low +; and must be pulsed before the touch half answers. +; +; Battery: GPIO 8 through a 1:2 divider (ADC 2.007 V x 2 = 4.01 V against a +; multimeter reading of 3.99-4.00 V). +; +; Hardware-tested pin map and driver come from the Launcher port of this board +; (bmorcelli/Launcher PR 475). Audio (ES8311 + amp) is not driven here. +; ============================================================================= +[env:es3c35p] +platform = espressif32@6.12.0 +board = esp32-s3-devkitc-1 +framework = arduino +monitor_speed = 115200 +board_build.partitions = partitions_16mb.csv +board_build.arduino.memory_type = qio_opi +board_upload.flash_size = 16MB +board_upload.maximum_size = 16777216 +board_build.f_flash = 80000000L +board_build.flash_mode = qio +lib_deps = ${common.lib_deps} +build_flags = + -D BOARD_VARIANT=\"es3c35p\" + -D BOARD_NAME=\"LCDwiki_ES3C35P\" + -D BOARD_PANEL=\"ST77922_320x480_QSPI\" + -D BOARD_IS_ES3C35P=1 + -D ENABLE_OTA_AUTO=1 + -D BOARD_HAS_PSRAM=1 + -D ARDUINO_USB_CDC_ON_BOOT=1 + ; --- Layout profile (same 320x480 as jc3248w535 / ws_lcd_350) --- + -D DISPLAY_320x480=1 + -D BACKLIGHT_PIN=41 + ; --- Battery sense: GPIO 8, 1:2 divider --- + -D BOARD_HAS_BATTERY=1 + -D BOARD_HAS_BAT_ADC=1 + -D BAT_ADC_PIN=8 + -D BAT_VOLT_DIVIDER=2.0f + ; --- Touch (ST77922 integrated controller, I2C @0x55) --- + -D USE_ST77922_TOUCH=1 + -D ST77922_TOUCH_SDA=38 + -D ST77922_TOUCH_SCL=39 + -D ST77922_TOUCH_INT=47 + -D ST77922_TOUCH_RST=48 diff --git a/docs/flasher.js b/docs/flasher.js index 6808788..1bcd52c 100644 --- a/docs/flasher.js +++ b/docs/flasher.js @@ -255,6 +255,22 @@ const BOARDS = { ], credit: { name: '@theNailz', u: 'https://github.com/theNailz' }, }, + es3c35p: { + chipFamily: 'ESP32-S3', + label: 'LCDwiki ES3C35P 3.5" (320x480, ST77922 QSPI) - community (not owned by maintainer)', + name: 'LCDwiki ES3C35P 3.5"', + badge: 'community', + display: '3.5" ST77922, 320x480, QSPI', + touch: 'capacitive (in display IC)', + printers: 'up to 2 (opt-in 4)', + desc: 'All-in-one 3.5" ESP32-S3 board with 16MB flash, 8MB octal ' + + 'PSRAM, a battery sense input and a WS2812 status LED on GPIO 40. ' + + 'Reuses the JC3248W535 full-frame layout. There is no buzzer ' + + 'hardware and no ES8311 audio driver yet.', + links: [ + { t: 'LCDwiki', u: 'http://www.lcdwiki.com/' }, + ], + }, cyd: { chipFamily: 'ESP32', label: 'CYD / ESP32-2432S028 (ILI9341, 240x320)', diff --git a/include/config.h b/include/config.h index 50af8d3..3f3e3e6 100644 --- a/include/config.h +++ b/include/config.h @@ -196,12 +196,22 @@ #define DISPLAY_IS_ROUND 0 #endif -#if defined(BOARD_IS_JC3248W535) +// ES3C35P's ST77922 QSPI panel follows the same contract (fixed native 320x480 +// portrait, whole-frame push via pushRawPixels(), rotation on the sprite), so it +// shares this path. The macro keeps its historical AXS name. +#if defined(BOARD_IS_JC3248W535) || defined(BOARD_IS_ES3C35P) #define PANEL_REQUIRES_AXS_FRAME_SPRITE 1 #else #define PANEL_REQUIRES_AXS_FRAME_SPRITE 0 #endif +// Panel class behind the frame sprite (display_ui.h FramePanel typedef). +#if defined(BOARD_IS_ES3C35P) +#define PANEL_FRAME_IS_ST77922 1 +#else +#define PANEL_FRAME_IS_ST77922 0 +#endif + // Display reset/control routed through an I2C IO expander instead of GPIOs: // SenseCAP (PCA9535), ws_lcd_350 and ws_lcd_28c (TCA9554). This cap only signals "bring up // Wire.h for the expander"; the per-chip register bring-up stays board-specific @@ -215,7 +225,7 @@ // Touch panels where a wake tap reads as a long press (resistive XPT2046 - both the // library driver and the LovyanGFX shared-bus one on cyd_2432s024 - and AXS15231B). // main.cpp suppresses hold-to-dim on the sleep screens for these. -#if defined(USE_XPT2046) || defined(USE_AXS_TOUCH) || defined(BOARD_IS_CYD_2432S024) +#if defined(USE_XPT2046) || defined(USE_AXS_TOUCH) || defined(USE_ST77922_TOUCH) || defined(BOARD_IS_CYD_2432S024) #define TOUCH_WAKE_LONGPRESS 1 #else #define TOUCH_WAKE_LONGPRESS 0 @@ -256,7 +266,7 @@ #define BOARD_HAS_ONBOARD_RGB_PWM 0 #endif -#if defined(BOARD_IS_S3_ZERO) || defined(BOARD_IS_ES3N28P) +#if defined(BOARD_IS_S3_ZERO) || defined(BOARD_IS_ES3N28P) || defined(BOARD_IS_ES3C35P) #define BOARD_HAS_ONBOARD_RGB_PIXEL 1 #else #define BOARD_HAS_ONBOARD_RGB_PIXEL 0 @@ -359,6 +369,7 @@ defined(BOARD_IS_WS280) || defined(BOARD_IS_WS350) || \ defined(BOARD_IS_JC3248W535) || defined(BOARD_IS_SC01PLUS) || \ defined(BOARD_IS_ES3N28P) || defined(BOARD_IS_SC05X) || \ + defined(BOARD_IS_ES3C35P) || \ defined(BOARD_IS_SENSECAP) || defined(BOARD_IS_AMOLED216) || \ defined(BOARD_IS_WS28C) #define HAS_FULL_HMS_TABLE 1 @@ -465,7 +476,7 @@ // Edge glow (border light effect on print complete / failed) // ============================================================================= #define GLOW_THICKNESS_PX 8 // border band thickness (rectangular) -#if defined(BOARD_IS_JC3248W535) +#if defined(BOARD_IS_JC3248W535) || defined(BOARD_IS_ES3C35P) #define GLOW_ANIM_MS 80 // every tick flushes the full QSPI frame - keep modest #elif DISPLAY_IS_ROUND #define GLOW_ANIM_MS 40 // round ring = a few fillArc wedges, 25 fps fine @@ -544,6 +555,10 @@ // as a GPIO regardless of the enabled flag, which would hijack SCLK and freeze // the panel after boot. Disable (0) so the SPI clock is left alone. #define BUZZER_DEFAULT_PIN 0 +#elif defined(BOARD_IS_ES3C35P) +// LCDwiki ES3C35P: no buzzer; audio is an ES8311 codec + amp, which has no GPIO +// backend here. The generic S3 default (GPIO 5) is this board's SD CLK. Disable (0). +#define BUZZER_DEFAULT_PIN 0 #elif defined(BOARD_IS_WS28C) // Waveshare 2.8C: the buzzer is expander bit 7, not a GPIO, so there is no pin // to configure. 0 keeps the GPIO backend out of it - and the generic S3 @@ -616,6 +631,8 @@ #if BOARD_HAS_ONBOARD_RGB_PIXEL #if defined(BOARD_IS_ES3N28P) #define ONBOARD_RGB_DATA_PIN 42 +#elif defined(BOARD_IS_ES3C35P) +#define ONBOARD_RGB_DATA_PIN 40 // LCDwiki ES3C35P WS2812 #else #define ONBOARD_RGB_DATA_PIN 21 // Waveshare ESP32-S3-Zero #endif diff --git a/src/button.cpp b/src/button.cpp index 447218c..dedd9d3 100644 --- a/src/button.cpp +++ b/src/button.cpp @@ -38,6 +38,12 @@ void sanitizeButtonPin() { if (buttonPin == AXS_TOUCH_SCL) { clash("AXS touch SCL"); return; } if (buttonPin == AXS_TOUCH_INT) { clash("AXS touch INT"); return; } #endif +#if defined(USE_ST77922_TOUCH) + if (buttonPin == ST77922_TOUCH_SDA) { clash("ST77922 touch SDA"); return; } + if (buttonPin == ST77922_TOUCH_SCL) { clash("ST77922 touch SCL"); return; } + if (buttonPin == ST77922_TOUCH_INT) { clash("ST77922 touch INT"); return; } + if (buttonPin == ST77922_TOUCH_RST) { clash("ST77922 touch RST"); return; } +#endif #if defined(USE_FT6336) if (buttonPin == FT6336_SDA) { clash("FT6336 touch SDA"); return; } if (buttonPin == FT6336_SCL) { clash("FT6336 touch SCL"); return; } diff --git a/src/button_touch_backend.h b/src/button_touch_backend.h index 6861f50..8e5399e 100644 --- a/src/button_touch_backend.h +++ b/src/button_touch_backend.h @@ -12,7 +12,7 @@ // At most one EXPLICIT driver may be selected. Zero is valid (no-touch boards - // esp32s3, esp32c3, the round/DIY envs). More than one is a build error. #if (defined(USE_XPT2046) + defined(USE_CST816) + defined(USE_CST328) \ - + defined(USE_FT5X06) + defined(USE_FT6336) + defined(USE_AXS_TOUCH) \ + + defined(USE_FT5X06) + defined(USE_FT6336) + defined(USE_AXS_TOUCH) + defined(USE_ST77922_TOUCH) \ + defined(USE_GT911)) > 1 #error "button_touch: more than one explicit touch driver (USE_*) selected" #endif diff --git a/src/button_touch_none.cpp b/src/button_touch_none.cpp index 8c0239d..1a9f996 100644 --- a/src/button_touch_none.cpp +++ b/src/button_touch_none.cpp @@ -8,7 +8,7 @@ #include "button_touch_backend.h" #if (defined(USE_XPT2046) + defined(USE_CST816) + defined(USE_CST328) \ - + defined(USE_FT5X06) + defined(USE_FT6336) + defined(USE_AXS_TOUCH) \ + + defined(USE_FT5X06) + defined(USE_FT6336) + defined(USE_AXS_TOUCH) + defined(USE_ST77922_TOUCH) \ + defined(USE_GT911)) == 0 #if defined(TOUCH_CS) diff --git a/src/button_touch_st77922.cpp b/src/button_touch_st77922.cpp new file mode 100644 index 0000000..5e9a3c0 --- /dev/null +++ b/src/button_touch_st77922.cpp @@ -0,0 +1,85 @@ +// ST77922 integrated touch backend (lcdwiki_es3c35p). See button_touch_backend.h. +// The touch block sits inside the display controller (I2C 0x55) and is NOT +// FT6x36-compatible: register addresses are 16 bits wide (MSB first) and reads +// use a repeated start. BambuHelper only needs "finger down", so this is a level +// backend: touchPoll() reports whether the controller currently has a valid +// touch record. Register layout (from the vendor's esp_lcd_touch_st77922): +// 0x0010 info byte: bit 3 = "a coordinate report is available" +// 0x0009 max simultaneous touches +// 0x0014 8-byte record per touch, bit 7 of byte 0 = valid +// The whole maxTouches*8 report block MUST be read in one transfer, otherwise the +// controller never refreshes its buffer and keeps reporting a stale touch. +#include "button_touch_backend.h" + +#if defined(USE_ST77922_TOUCH) + +#include + +#define ST77922_TOUCH_ADDR 0x55 +#define ST77922_REG_MAX_TOUCH 0x0009 +#define ST77922_REG_INFO 0x0010 +#define ST77922_REG_REPORT 0x0014 +#define ST77922_INFO_HAS_COORD 0x08 +#define ST77922_REPORT_VALID 0x80 +#define ST77922_MAX_TOUCHES 10 + +static bool busReady = false; +static bool seen = false; + +static bool stReadReg(uint16_t reg, uint8_t* buf, size_t len) { + Wire.beginTransmission(ST77922_TOUCH_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)ST77922_TOUCH_ADDR, len) < len) return false; + for (size_t i = 0; i < len; i++) buf[i] = (uint8_t)Wire.read(); + return true; +} + +void touchInit() { + // The touch half of the IC holds in reset until RST (GPIO48) is pulsed; the + // display half does not need it. + pinMode(ST77922_TOUCH_RST, OUTPUT); + digitalWrite(ST77922_TOUCH_RST, LOW); + delay(10); + digitalWrite(ST77922_TOUCH_RST, HIGH); + delay(50); + Wire.begin(ST77922_TOUCH_SDA, ST77922_TOUCH_SCL); + Wire.setClock(400000); + busReady = true; + pinMode(ST77922_TOUCH_INT, INPUT_PULLUP); + uint8_t info = 0; + if (stReadReg(ST77922_REG_INFO, &info, 1)) { + Serial.printf("ST77922 touch initialized (I2C SDA=%d SCL=%d INT=%d RST=%d, addr 0x%02X)\n", + ST77922_TOUCH_SDA, ST77922_TOUCH_SCL, ST77922_TOUCH_INT, + ST77922_TOUCH_RST, ST77922_TOUCH_ADDR); + seen = true; + } else { + Serial.printf("ST77922 touch did not answer at init (addr 0x%02X); will keep retrying at runtime\n", + ST77922_TOUCH_ADDR); + } +} + +TouchPoll touchPoll() { + if (!busReady) return {TouchEvent::Unavailable, false}; + uint8_t info = 0; + if (!stReadReg(ST77922_REG_INFO, &info, 1)) return {TouchEvent::Unavailable, false}; + if (!seen) { + Serial.println("ST77922 touch became responsive at runtime"); + seen = true; + } + if (!(info & ST77922_INFO_HAS_COORD)) return {TouchEvent::None, false}; + + uint8_t maxTouches = 0; + if (!stReadReg(ST77922_REG_MAX_TOUCH, &maxTouches, 1)) return {TouchEvent::Unavailable, false}; + if (maxTouches == 0 || maxTouches > ST77922_MAX_TOUCHES) maxTouches = ST77922_MAX_TOUCHES; + uint8_t r[ST77922_MAX_TOUCHES * 8]; + if (!stReadReg(ST77922_REG_REPORT, r, (size_t)maxTouches * 8)) + return {TouchEvent::Unavailable, false}; + for (uint8_t i = 0; i < maxTouches; i++) { + if (r[i * 8] & ST77922_REPORT_VALID) return {TouchEvent::None, true}; + } + return {TouchEvent::None, false}; +} + +#endif // USE_ST77922_TOUCH diff --git a/src/clock_pong.cpp b/src/clock_pong.cpp index 13afe5b..ad1284b 100644 --- a/src/clock_pong.cpp +++ b/src/clock_pong.cpp @@ -39,12 +39,12 @@ #define ARK_BALL_SIZE (SCREEN_W >= 480 ? ARK_BALL_SIZE_BASE * 2 : ARK_BALL_SIZE_BASE) #define ARK_PADDLE_H_BASE 4 #define ARK_PADDLE_H (SCREEN_W >= 480 ? ARK_PADDLE_H_BASE * 2 : ARK_PADDLE_H_BASE) -// JC3248W535 (320x480, PSRAM sprite + ~20ms QSPI flush) needs faster -// physics: the play field is twice as tall as the 240px reference and the -// effective loop rate sits well below 50 Hz, so the base 3.0 px/tick ball +// Frame-sprite boards (320x480, PSRAM sprite + full QSPI flush per tick) need +// faster physics: the play field is twice as tall as the 240px reference and +// the effective loop rate sits well below 50 Hz, so the base 3.0 px/tick ball // crawls across the screen. Paddle keeps its >ball-speed margin so it can // still track the ball. -#if defined(BOARD_IS_JC3248W535) +#if PANEL_REQUIRES_AXS_FRAME_SPRITE #define ARK_BALL_SPEED 6.0f #define ARK_PADDLE_SPEED 8 #else diff --git a/src/display_gauges.cpp b/src/display_gauges.cpp index 71b2c16..c2c81f6 100644 --- a/src/display_gauges.cpp +++ b/src/display_gauges.cpp @@ -1055,15 +1055,15 @@ static FontID fitHumidityValueFont(lgfx::LovyanGFX& gfx, const char* value, // Draw gauge value/secondary text transparently on every panel. The center // disc is cleared before redraw, so glyphs blend against the bg there; where a // large value spills past the inner circle the arc shows through instead of an -// opaque box. JC3248W535 renders into a readable 16bpp PSRAM frame sprite, so +// opaque box. Frame-sprite boards render into a readable 16bpp PSRAM sprite, so // antialiased readback blends the same as the direct-draw panels — the opaque // background (which was what clipped the arc on the small split gauges) is not // needed. static void setGaugeClearedTextColor(lgfx::LovyanGFX& gfx, uint16_t fg, uint16_t bg) { -#if defined(BOARD_IS_JC3248W535) +#if PANEL_REQUIRES_AXS_FRAME_SPRITE // Transparent value text fixed the smaller dual-printer gauges in PORTRAIT, - // but the rotated (landscape) JC sprite renders transparent antialiased text + // but the rotated (landscape) frame sprite renders transparent antialiased text // black. Keep the explicit background in landscape, where the opaque box was // always fine; stay transparent in portrait. if (dispSettings.rotation == 1 || dispSettings.rotation == 3) { diff --git a/src/display_ui.cpp b/src/display_ui.cpp index ec068be..6d7e712 100644 --- a/src/display_ui.cpp +++ b/src/display_ui.cpp @@ -115,7 +115,7 @@ lgfx::LGFX_Device& displayDevice() { return _tft_instance; } // Direct panel pointer for JC3248W535 sprite escape-hatch; nullptr on all // other boards so the extern declaration in display_ui.h is always satisfied. #if PANEL_REQUIRES_AXS_FRAME_SPRITE -lgfx::Panel_AXS15231B_AGFX* g_axs_panel = _tft_instance.panelAXS(); +FramePanel* g_axs_panel = _tft_instance.framePanel(); // Full-frame PSRAM sprite. All BambuHelper draws are redirected here in // initDisplay() (via tft_ptr), then flushed to the panel once per loop() @@ -132,7 +132,7 @@ static bool g_frame_dirty = true; static unsigned long g_last_flush_ms = 0; static const unsigned long FRAME_KEEPALIVE_MS = 500; #else -lgfx::Panel_AXS15231B_AGFX* g_axs_panel = nullptr; +FramePanel* g_axs_panel = nullptr; #endif void markFrameDirty() { diff --git a/src/display_ui.h b/src/display_ui.h index e2df19e..878cbaa 100644 --- a/src/display_ui.h +++ b/src/display_ui.h @@ -6,7 +6,13 @@ // Forward-declare the panel type so callers can use the pointer without // pulling in the full header (which includes Arduino_GFX headers). -namespace lgfx { inline namespace v1 { class Panel_AXS15231B_AGFX; } } +namespace lgfx { inline namespace v1 { class Panel_AXS15231B_AGFX; class Panel_ST77922_QSPI; } } +// Panel wrapper type of the boards that draw into a full-frame PSRAM sprite. +#if PANEL_FRAME_IS_ST77922 +typedef lgfx::Panel_ST77922_QSPI FramePanel; +#else +typedef lgfx::Panel_AXS15231B_AGFX FramePanel; +#endif enum ScreenState { SCREEN_SPLASH, @@ -111,9 +117,9 @@ extern lgfx::LovyanGFX* tft_ptr; // LGFX_Device, not LovyanGFX, so it cannot go through `tft`. lgfx::LGFX_Device& displayDevice(); -// Direct pointer to the AXS15231B panel wrapper; only non-null on -// BOARD_IS_JC3248W535 builds. Used by the sprite direct-push diagnostic. -extern lgfx::Panel_AXS15231B_AGFX* g_axs_panel; +// Direct pointer to the frame-sprite panel wrapper; only non-null when +// PANEL_REQUIRES_AXS_FRAME_SPRITE. flushFrame() pushes the sprite through it. +extern FramePanel* g_axs_panel; void initDisplay(); void updateDisplay(); @@ -124,15 +130,15 @@ bool screenCaptureBegin(const uint16_t** buf, int16_t* w, int16_t* h); void screenCaptureEnd(); // Flush the off-screen framebuffer sprite to the panel in one contiguous -// raster write. No-op on boards that draw directly (all except -// BOARD_IS_JC3248W535, which uses a full-screen PSRAM sprite to work around -// the AXS15231B QSPI-mode addressing limits). Call once per loop tick after +// raster write. No-op on boards that draw directly (all except the +// PANEL_REQUIRES_AXS_FRAME_SPRITE ones - JC3248W535 and ES3C35P - whose QSPI +// panels cannot take arbitrary windows). Call once per loop tick after // UI draws to commit the frame. void flushFrame(); // Mark the off-screen framebuffer sprite as dirty so the next flushFrame() // actually pushes to the panel. No-op on boards that draw directly (all -// except BOARD_IS_JC3248W535). Call from any code path that writes pixels +// except PANEL_REQUIRES_AXS_FRAME_SPRITE). Call from any code path that writes pixels // into the sprite; a keepalive tick in flushFrame() guarantees the panel // still gets a refresh even if a dirty mark is missed. void markFrameDirty(); diff --git a/src/led.cpp b/src/led.cpp index 9901d6a..b5425a9 100644 --- a/src/led.cpp +++ b/src/led.cpp @@ -258,6 +258,17 @@ bool isLedPinAllowed(uint8_t pin) { if (pin >= 26 && pin <= 37) return false; // SPI flash + PSRAM (qio_opi) if (pin > 48) return false; +#elif defined(BOARD_IS_ES3C35P) + // LCDwiki ES3C35P (ST77922 QSPI 320x480, touch inside the display IC) + if (pin == 9 || pin == 10 || pin == 11 || pin == 12 || pin == 13 || pin == 14) return false; // display QSPI + if (pin == 41) return false; // backlight + if (pin == 38 || pin == 39 || pin == 47 || pin == 48) return false; // touch I2C SDA/SCL + INT + RST + if (pin == 2 || pin == 3 || pin == 4 || pin == 5 || pin == 6 || pin == 7) return false; // SD card (4-bit SDIO) + if (pin == 1 || pin == 8 || pin == 40) return false; // audio amp EN, battery sense, WS2812 + if (pin == 19 || pin == 20) return false; // USB CDC D-/D+ + if (pin >= 26 && pin <= 37) return false; // SPI flash + PSRAM + if (pin > 48) return false; + #elif defined(BOARD_IS_C3) // LOLIN C3 mini if (pin == 6 || pin == 7 || pin == 10 || pin == 20 || pin == 21) return false; // display SPI diff --git a/src/lgfx_boards.h b/src/lgfx_boards.h index db6272b..ccd0414 100644 --- a/src/lgfx_boards.h +++ b/src/lgfx_boards.h @@ -689,9 +689,23 @@ class LGFX_JC3248W535 : public lgfx::LGFX_Device { // databus class hard-codes them at construction anyway. setPanel(&_panel); } - lgfx::Panel_AXS15231B_AGFX* panelAXS() { return &_panel; } + lgfx::Panel_AXS15231B_AGFX* framePanel() { return &_panel; } }; static LGFX_JC3248W535 _tft_instance; +#elif defined(BOARD_IS_ES3C35P) +// --- LCDwiki ES3C35P + ST77922 320x480 QSPI ---------------------------------- +// Panel_ST77922_QSPI owns its SPI device (registers on 1 line, pixels on 4 - a +// combination no stock LovyanGFX / Arduino_GFX bus can produce). Pins are fixed +// inside the panel class: CS=10 SCLK=12 D0=11 D1=13 D2=14 D3=9. +// Same frame-sprite contract as the JC3248W535 block above. +#include "lgfx_panel_st77922_qspi.hpp" +class LGFX_ES3C35P : public lgfx::LGFX_Device { + lgfx::Panel_ST77922_QSPI _panel; +public: + LGFX_ES3C35P() { setPanel(&_panel); } + lgfx::Panel_ST77922_QSPI* framePanel() { return &_panel; } +}; +static LGFX_ES3C35P _tft_instance; #elif defined(BOARD_IS_C3_ROUND) // --- ESP32-C3 Super Mini + GC9A01 1.28" round 240x240 ------------------------ // Bare 7-pin SPI module, same wiring as the ST7789 C3 map (SCL=21, SDA=20, diff --git a/src/lgfx_panel_st77922_qspi.hpp b/src/lgfx_panel_st77922_qspi.hpp new file mode 100644 index 0000000..750ced7 --- /dev/null +++ b/src/lgfx_panel_st77922_qspi.hpp @@ -0,0 +1,255 @@ +// LovyanGFX Panel_Device for the ST77922 320x480 panel on the LCDwiki ES3C35P +// (3.5" ESP32-S3 module). The panel is quad-SPI with no DC pin and wants register +// writes on ONE data line but pixel data on FOUR ("1-1-4"), which neither +// LovyanGFX's SPI bus nor Arduino_GFX's Arduino_ESP32QSPI can produce (both send +// the command phase on all four lines). So this class owns the SPI device and +// talks to it through ESP-IDF's spi_master directly. +// +// Contract (same as Panel_AXS15231B_AGFX, so display_ui.cpp treats them alike): +// the panel stays at rotation 0 (native 320x480 portrait) forever; the app draws +// into a full-frame PSRAM sprite, with rotation applied to the sprite, and +// pushes it once per loop tick with pushRawPixels(). Direct drawing is limited +// to what the boot path needs (full-screen fills). +// +// Transfer format follows the board vendor's own example (github.com/ydedox/st77922, +// Example_01): +// register write : opcode 0x02, 24-bit address = reg << 8, params on one line +// pixel write : opcode 0x32, address 0x003C00 (write-memory-continue), +// data on four lines, after a 0x2A/0x2B window +// the window's X start and width must be multiples of 4 +#pragma once + +#include +#include +#include +#include + +#include "lgfx_panel_st77922_qspi_init.h" + +namespace lgfx { +inline namespace v1 { + +class Panel_ST77922_QSPI : public Panel_Device { +public: + static constexpr int PIN_CS = 10, PIN_SCLK = 12, PIN_D0 = 11, PIN_D1 = 13, + PIN_D2 = 14, PIN_D3 = 9; + static constexpr uint32_t SPI_HZ = 80000000; + // Pixels per SPI transaction (the vendor's own value); 2 bytes each. + static constexpr int32_t CHUNK_PIXELS = 0x4000; + + Panel_ST77922_QSPI() { + _cfg.memory_width = _cfg.panel_width = 320; + _cfg.memory_height = _cfg.panel_height = 480; + _cfg.offset_x = 0; + _cfg.offset_y = 0; + _cfg.offset_rotation = 0; + _cfg.dummy_read_pixel = 0; + _cfg.dummy_read_bits = 0; + _cfg.readable = false; + _cfg.invert = false; + _cfg.rgb_order = false; + _cfg.dlen_16bit = false; + _cfg.bus_shared = false; + _write_depth = color_depth_t::rgb565_2Byte; + _read_depth = color_depth_t::rgb565_2Byte; + } + + ~Panel_ST77922_QSPI() { + if (_buf) heap_caps_free(_buf); + if (_cmd) heap_caps_free(_cmd); + } + + bool init(bool /*use_reset*/) override { + if (_init_done) return true; + // DMA-capable internal RAM for pixel chunks and register parameters. + _buf = (uint16_t*)heap_caps_malloc(CHUNK_PIXELS * 2, MALLOC_CAP_DMA); + _cmd = (uint8_t*)heap_caps_malloc(64, MALLOC_CAP_DMA); + if (!_buf || !_cmd) { + Serial.println("ST77922: DMA buffer alloc failed"); + return false; + } + + spi_bus_config_t buscfg = {}; + buscfg.data0_io_num = PIN_D0; + buscfg.data1_io_num = PIN_D1; + buscfg.sclk_io_num = PIN_SCLK; + buscfg.data2_io_num = PIN_D2; + buscfg.data3_io_num = PIN_D3; + buscfg.max_transfer_sz = CHUNK_PIXELS * 2 + 8; + buscfg.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_IOMUX_PINS | + SPICOMMON_BUSFLAG_QUAD; + esp_err_t err = spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO); + if (err != ESP_OK) { + Serial.printf("ST77922: spi_bus_initialize failed (%d)\n", (int)err); + return false; + } + + spi_device_interface_config_t devcfg = {}; + devcfg.mode = 0; + devcfg.clock_speed_hz = SPI_HZ; + devcfg.spics_io_num = PIN_CS; + devcfg.flags = SPI_DEVICE_HALFDUPLEX; + devcfg.queue_size = 17; + err = spi_bus_add_device(SPI2_HOST, &devcfg, &_dev); + if (err != ESP_OK) { + Serial.printf("ST77922: spi_bus_add_device failed (%d)\n", (int)err); + return false; + } + + const size_t n = sizeof(es3c35p_lcd_init_cmds) / sizeof(es3c35p_lcd_init_cmds[0]); + for (size_t i = 0; i < n; i++) { + writeReg((uint8_t)es3c35p_lcd_init_cmds[i].cmd, es3c35p_lcd_init_cmds[i].data, + es3c35p_lcd_init_cmds[i].data_bytes); + if (es3c35p_lcd_init_cmds[i].delay_ms) delay(es3c35p_lcd_init_cmds[i].delay_ms); + } + _init_done = true; + _width = _cfg.panel_width; + _height = _cfg.panel_height; + return true; + } + + // The SPI device is owned here, so LovyanGFX's bus plumbing is unused. + void initBus(void) override {} + void releaseBus(void) override {} + void beginTransaction(void) override {} + void endTransaction(void) override {} + + color_depth_t setColorDepth(color_depth_t) override { + _write_depth = color_depth_t::rgb565_2Byte; + _read_depth = color_depth_t::rgb565_2Byte; + return _write_depth; + } + + // The panel does not rotate (its MADCTL does not remap addressing) and the + // frame sprite owns rotation, so the panel is pinned to native portrait. + void setRotation(uint_fast8_t) override { + _rotation = 0; + _internal_rotation = 0; + _width = _cfg.panel_width; + _height = _cfg.panel_height; + } + + void setInvert(bool /*invert*/) override {} + void setSleep(bool /*flg*/) override {} + void setPowerSave(bool /*flg*/) override {} + void waitDisplay(void) override {} + bool displayBusy(void) override { return false; } + + // Panel_Device's defaults for these dereference `_bus`, which is null here. + void initDMA(void) override {} + void waitDMA(void) override {} + bool dmaBusy(void) override { return false; } + void display(uint_fast16_t, uint_fast16_t, uint_fast16_t, uint_fast16_t) override {} + + // Full-screen / rectangle fill. Used by the boot path (tft.fillScreen) before + // the frame sprite exists. The panel needs X start and width in multiples of + // 4, so the rectangle is widened to the next multiple. + void writeFillRectPreclipped(uint_fast16_t x, uint_fast16_t y, + uint_fast16_t w, uint_fast16_t h, + uint32_t rawcolor) override { + if (!_dev) return; + int32_t x0 = x & ~3; + int32_t x1 = (x + w + 3) & ~3; + if (x1 > _cfg.panel_width) x1 = _cfg.panel_width; + setWindow(x0, y, x1 - 1, y + h - 1); + size_t total = (size_t)(x1 - x0) * h; + // rawcolor is rgb565_2Byte, i.e. already in wire (MSB-first) byte order. + const uint16_t c = (uint16_t)rawcolor; + const size_t first = total > CHUNK_PIXELS ? CHUNK_PIXELS : total; + for (size_t i = 0; i < first; i++) _buf[i] = c; + while (total) { + size_t n = total > CHUNK_PIXELS ? CHUNK_PIXELS : total; + sendPixels(n); + total -= n; + } + } + + void drawPixelPreclipped(uint_fast16_t x, uint_fast16_t y, uint32_t rawcolor) override { + writeFillRectPreclipped(x, y, 1, 1, rawcolor); + } + + // Window is only used together with the (unsupported here) direct pixel paths. + void setWindow(uint_fast16_t xs, uint_fast16_t ys, + uint_fast16_t xe, uint_fast16_t ye) override { + if (!_dev) return; + uint8_t cx[4] = {(uint8_t)(xs >> 8), (uint8_t)xs, (uint8_t)(xe >> 8), (uint8_t)xe}; + uint8_t cy[4] = {(uint8_t)(ys >> 8), (uint8_t)ys, (uint8_t)(ye >> 8), (uint8_t)ye}; + writeReg(0x2A, cx, 4); + writeReg(0x2B, cy, 4); + } + + // Arbitrary-window pixel streaming is not supported: the controller's X + // alignment rule makes it unsafe, and nothing draws this way once the frame + // sprite is active. Stubbed so a stray call is harmless. + void writeBlock(uint32_t, uint32_t) override {} + void writePixels(pixelcopy_t*, uint32_t, bool) override {} + void writeImage(uint_fast16_t, uint_fast16_t, uint_fast16_t, uint_fast16_t, + pixelcopy_t*, bool) override {} + void writeImageARGB(uint_fast16_t, uint_fast16_t, uint_fast16_t, uint_fast16_t, + pixelcopy_t*) override {} + void copyRect(uint_fast16_t, uint_fast16_t, uint_fast16_t, uint_fast16_t, + uint_fast16_t, uint_fast16_t) override {} + + // Push a whole 320x480 frame. `data` is an LGFX 16bpp sprite buffer, whose + // bytes are already MSB-first (rgb565_2Byte), which is exactly the wire order, + // so it is copied through unchanged. + void pushRawPixels(uint16_t* data, uint32_t length) { + if (!_dev || length == 0) return; + setWindow(0, 0, _cfg.panel_width - 1, _cfg.panel_height - 1); + const uint16_t* src = data; + while (length) { + size_t n = length > (uint32_t)CHUNK_PIXELS ? CHUNK_PIXELS : length; + memcpy(_buf, src, n * 2); // DMA needs internal RAM; the sprite is in PSRAM + sendPixels(n); + src += n; + length -= n; + } + } + + // The panel is write-only over this bus. + uint32_t readCommand(uint_fast16_t, uint_fast8_t, uint_fast8_t) override { return 0; } + uint32_t readData(uint_fast8_t, uint_fast8_t) override { return 0; } + void readRect(uint_fast16_t, uint_fast16_t, uint_fast16_t, uint_fast16_t, + void*, pixelcopy_t*) override {} + int32_t getScanLine(void) override { return 0; } + void writeCommand(uint32_t, uint_fast8_t) override {} + void writeData(uint32_t, uint_fast8_t) override {} + +private: + // One register write: opcode 0x02, address (reg << 8), params on one line. + void writeReg(uint8_t reg, const void* data, size_t len) { + spi_transaction_ext_t t = {}; + t.base.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR; + t.base.cmd = 0x02; + t.base.addr = (uint32_t)reg << 8; + t.command_bits = 8; + t.address_bits = 24; + if (len && len <= 64) { + memcpy(_cmd, data, len); + t.base.tx_buffer = _cmd; + t.base.length = 8 * len; + } + spi_device_polling_transmit(_dev, (spi_transaction_t*)&t); + } + + // n pixels from _buf: opcode 0x32, address 0x003C00, data on four lines. + void sendPixels(size_t n) { + spi_transaction_ext_t t = {}; + t.base.flags = SPI_TRANS_MODE_QIO | SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR; + t.base.cmd = 0x32; + t.base.addr = 0x3C << 8; + t.command_bits = 8; + t.address_bits = 24; + t.base.tx_buffer = _buf; + t.base.length = n * 16; + spi_device_polling_transmit(_dev, (spi_transaction_t*)&t); + } + + bool _init_done = false; + spi_device_handle_t _dev = nullptr; + uint16_t* _buf = nullptr; + uint8_t* _cmd = nullptr; +}; + +} // namespace v1 +} // namespace lgfx diff --git a/src/lgfx_panel_st77922_qspi_init.h b/src/lgfx_panel_st77922_qspi_init.h new file mode 100644 index 0000000..29f1cb2 --- /dev/null +++ b/src/lgfx_panel_st77922_qspi_init.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include + +typedef struct { + int cmd; // register + const void *data; // parameter bytes + size_t data_bytes; // number of parameter bytes + unsigned int delay_ms; // delay after this register write +} st77922_lcd_init_cmd_t; + +// Verbatim from the board vendor's own example (LCDwiki ES3C35P, +// github.com/ydedox/st77922 Example_01, the ACTIVE st77922_lcd_init[] table -- +// not the commented-out one above it), renamed only. Confirmed working on this +// board with the vendor's transport. Format: st77922_lcd_init_cmd_t. +static const st77922_lcd_init_cmd_t es3c35p_lcd_init_cmds[] = { + {0xF1, (uint8_t[]){0x00}, 1, 0}, + {0x60, (uint8_t[]){0x00, 0x00, 0x00}, 3, 0}, + {0x65, (uint8_t[]){0x80}, 1, 0}, + {0x79, (uint8_t[]){0x06}, 1, 0}, + {0x7B, (uint8_t[]){0x00, 0x08, 0x08}, 3, 0}, + {0x80, (uint8_t[]){0x55, 0x62, 0x2F, 0x17, 0xF0, 0x52, 0x70, 0xD2, 0x52, 0x62, 0xEA}, 11, 0}, + {0x81, (uint8_t[]){0x26, 0x52, 0x72, 0x27}, 4, 0}, + {0x84, (uint8_t[]){0x92, 0x25}, 2, 0}, + {0x87, (uint8_t[]){0x10, 0x10, 0x58, 0x00, 0x02, 0x3A}, 6, 0}, + {0x88, (uint8_t[]){0x00, 0x00, 0x2C, 0x10, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x06}, 15, 0}, + {0x89, (uint8_t[]){0x00, 0x00, 0x00}, 3, 0}, + {0x8A, (uint8_t[]){0x13, 0x00, 0x2C, 0x00, 0x00, 0x2C, 0x10, 0x10, 0x00, 0x3E, 0x19}, 11, 0}, + {0x8B, (uint8_t[]){0x15, 0xB1, 0xB1, 0x44, 0x96, 0x2C, 0x10, 0x97, 0x8E}, 9, 0}, + {0x8C, (uint8_t[]){0x1D, 0xB1, 0xB1, 0x44, 0x96, 0x2C, 0x10, 0x50, 0x0F, 0x01, 0xC5, 0x12, 0x09}, 13, 0}, + {0x8D, (uint8_t[]){0x0C}, 1, 0}, + {0x8E, (uint8_t[]){0x33, 0x01, 0x0C, 0x13, 0x01, 0x01}, 6, 0}, + {0xB3, (uint8_t[]){0x00, 0x30}, 2, 0}, + {0xF1, (uint8_t[]){0x00}, 1, 0}, + {0x71, (uint8_t[]){0xD0}, 1, 0}, + {0x66, (uint8_t[]){0x02, 0x3F}, 2, 0}, + {0xBE, (uint8_t[]){0x26, 0x00, 0x9D}, 3, 0}, + {0x70, (uint8_t[]){0x01, 0xA0, 0x11, 0x40, 0xE0, 0x00, 0x11, 0x69, 0x11, 0x00, 0x00, 0x1A}, 12, 0}, + {0x90, (uint8_t[]){0x04, 0x04, 0x55, 0x74, 0x00, 0x40, 0x43, 0x27, 0x27}, 9, 0}, + {0x91, (uint8_t[]){0x04, 0x04, 0x55, 0x75, 0x00, 0x40, 0x42, 0x27, 0x27}, 9, 0}, + {0x92, (uint8_t[]){0x04, 0x44, 0x55, 0xC0, 0x06, 0x00, 0x07, 0x05, 0x90, 0x27}, 10, 0}, + {0x93, (uint8_t[]){0x04, 0x43, 0x11, 0x00, 0x00, 0x00, 0x00, 0x05, 0x90, 0x27}, 10, 0}, + {0x94, (uint8_t[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 6, 0}, + {0x95, (uint8_t[]){0x96, 0x16, 0x00, 0x00, 0xFF}, 5, 0}, + {0x96, (uint8_t[]){0x44, 0x53, 0x03, 0x12, 0x23, 0x24, 0x06, 0x05, 0x94, 0x27, 0x00, 0x44}, 12, 0}, + {0x97, (uint8_t[]){0x44, 0x53, 0x47, 0x56, 0x20, 0x20, 0x02, 0x01, 0x94, 0x27, 0x00, 0x44}, 12, 0}, + {0xBA, (uint8_t[]){0x55, 0x94, 0x2D, 0x94, 0x27}, 5, 0}, + {0x9A, (uint8_t[]){0x40, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00}, 7, 0}, + {0x9B, (uint8_t[]){0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00}, 7, 0}, + {0x9C, (uint8_t[]){0x5C, 0x12, 0x00, 0x00, 0x10, 0x12, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00}, 13, 0}, + {0x9D, (uint8_t[]){0x8A, 0x51, 0x00, 0x00, 0x00, 0x80, 0x1E, 0x01}, 8, 0}, + {0x9E, (uint8_t[]){0x51, 0x00, 0x00, 0x00, 0x80, 0x1E, 0x01}, 7, 0}, + {0xB4, (uint8_t[]){0x1D, 0x1C, 0x1E, 0x0B, 0x14, 0x02, 0x13, 0x09, 0x1E, 0x00, 0x1E, 0x10}, 12, 0}, + {0xB5, (uint8_t[]){0x1D, 0x1C, 0x1E, 0x0A, 0x15, 0x03, 0x11, 0x08, 0x1E, 0x01, 0x1E, 0x12}, 12, 0}, + {0xB6, (uint8_t[]){0x77, 0x77, 0x00, 0x0A, 0xFF, 0x0A, 0xFF}, 7, 0}, + {0x86, (uint8_t[]){0xCD, 0x04, 0xB1, 0x02, 0x58, 0x12, 0x58, 0x0C, 0x13, 0x01, 0xA5, 0x00, 0xA5, 0xA5}, 14, 0}, + {0xB7, (uint8_t[]){0x07, 0x0A, 0x0E, 0x06, 0x05, 0x03, 0x2B, 0x03, 0x03, 0x42, 0x07, 0x10, 0x10, 0x2E, 0x3F, 0x0D}, 16, 0}, + {0xB8, (uint8_t[]){0x07, 0x0A, 0x0D, 0x05, 0x05, 0x02, 0x2B, 0x02, 0x03, 0x42, 0x06, 0x10, 0x0F, 0x2E, 0x3F, 0x0D}, 16, 0}, + {0xB9, (uint8_t[]){0x23, 0x23}, 2, 0}, + {0xBF, (uint8_t[]){0x10, 0x14, 0x14, 0x0B, 0x0B, 0x0B}, 6, 0}, + {0xF2, (uint8_t[]){0x00}, 1, 0}, + {0x73, (uint8_t[]){0x04, 0xDA, 0x12, 0x54, 0x47}, 5, 0}, + {0x77, (uint8_t[]){0x6B, 0x5B, 0xFD, 0xC3, 0xC5}, 5, 0}, + {0x7A, (uint8_t[]){0x15, 0x27}, 2, 0}, + {0x7B, (uint8_t[]){0x04, 0x57}, 2, 0}, + {0x7E, (uint8_t[]){0x01, 0x0E}, 2, 0}, + {0xBF, (uint8_t[]){0x36}, 1, 0}, + {0xE3, (uint8_t[]){0x40, 0x40}, 2, 0}, + {0xF0, (uint8_t[]){0x00}, 1, 0}, + {0xD0, (uint8_t[]){0x00}, 1, 0}, + {0x2A, (uint8_t[]){0x00, 0x00, 0x01, 0x3F}, 4, 0}, + {0x2B, (uint8_t[]){0x00, 0x00, 0x01, 0xDF}, 4, 0}, + {0x21, (uint8_t[]){0x00}, 0, 0}, + {0x11, (uint8_t[]){0x00}, 0, 120}, + {0x29, (uint8_t[]){0x00}, 0, 0}, + {0x2C, (uint8_t[]){0x00}, 0, 0}, + {0x3A, (uint8_t[]){0x01}, 1, 0}, + {0x36, (uint8_t[]){0x00}, 1, 0}, + {0x35, (uint8_t[]){0x01}, 1, 20}, +}; diff --git a/src/settings.cpp b/src/settings.cpp index fd76ab5..c2905b8 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -783,7 +783,7 @@ void loadSettings() { rotState.lastRotateMs = 0; // Button settings -#if defined(USE_CST816) || defined(USE_CST328) || defined(USE_XPT2046) || defined(USE_FT5X06) || defined(USE_FT6336) || defined(USE_AXS_TOUCH) || defined(USE_GT911) || defined(TOUCH_CS) +#if defined(USE_CST816) || defined(USE_CST328) || defined(USE_XPT2046) || defined(USE_FT5X06) || defined(USE_FT6336) || defined(USE_AXS_TOUCH) || defined(USE_ST77922_TOUCH) || defined(USE_GT911) || defined(TOUCH_CS) buttonType = (ButtonType)prefs.getUChar("btn_type", BTN_TOUCHSCREEN); #else buttonType = (ButtonType)prefs.getUChar("btn_type", BTN_DISABLED); diff --git a/tools/release.py b/tools/release.py index e53610a..4163c45 100644 --- a/tools/release.py +++ b/tools/release.py @@ -56,6 +56,7 @@ "ws_lcd_28c", "wt32_sc01_plus", "jc3248w535", + "es3c35p", "cyd", "tzt_2432", "cyd_2432s024",