Browse Source

Display fixes

pull/2568/head
Christos Themelis 1 month ago
parent
commit
f6be2bfcbc
  1. 64
      examples/simple_secure_chat_ui/main.cpp
  2. 37
      src/helpers/esp32/SenseCapHAL.h
  3. 5
      variants/sensecap_indicator-espnow/SCIndicatorDisplay.h

64
examples/simple_secure_chat_ui/main.cpp

@ -900,10 +900,74 @@ void configureDisplay() {
lcd.fillScreen(0x000000u);
}
// ── ROOT CAUSE NOTE ──────────────────────────────────────────────────────────
// Upstream LovyanGFX 1.2.7 does NOT understand the IO_EXPANDER pin encoding
// (only the mverch67 fork does). Our SCIndicatorDisplay.h sets
// cfg.pin_cs = 4 | IO_EXPANDER (= 0x44 = 68)
// During Panel_ST7701_Base::init(), LovyanGFX calls
// lgfx::gpio_lo(pin_cs) // assert CS
// ... send SPI init commands (gamma, voltages, RGB666, sleep-out, etc.) ...
// lgfx::gpio_hi(pin_cs) // deassert CS
// gpio_lo(68) on ESP32-S3 (max GPIO 48) is a silent no-op → CS is NEVER
// actually asserted → all SPI init commands are sent into the void →
// the ST7701 never receives its init sequence.
//
// This worked SOMETIMES because:
// - on soft reboot the ST7701 retained state from the previous boot
// - on cold boot from charged caps it was in some semi-init state
// - on cold boot from discharged caps it stayed in factory state → stripes
//
// FIX: manually assert TCA9535 P0.4 (real LCD CS) LOW before lcd.begin()
// and deassert it HIGH after. CS stays LOW for the entire LovyanGFX init,
// so all SPI commands actually reach the ST7701. The internal gpio_lo/hi
// calls in LovyanGFX become harmless no-ops (they target a non-existent
// GPIO 68, but our manual TCA9535 CS state is preserved).
// ────────────────────────────────────────────────────────────────────────────
static void sensecap_lcd_cs_assert() {
const uint8_t TCA = 0x20;
// Config Port 0: bit 4 (LCD CS) = OUTPUT, bit 5 (LCD RESX) = OUTPUT,
// others = INPUT. 0xCF = 1100 1111 (bits 4,5 cleared = output).
Wire.beginTransmission(TCA);
Wire.write(0x06); // CONFIG_P0
Wire.write(0xCF);
uint8_t e1 = Wire.endTransmission();
// Output Port 0: bit 4 LOW (CS asserted), bit 5 HIGH (RESX deasserted),
// all other latches HIGH. 0xEF = 1110 1111.
Wire.beginTransmission(TCA);
Wire.write(0x02); // OUTPUT_P0
Wire.write(0xEF);
uint8_t e2 = Wire.endTransmission();
Serial.printf("[lcd_cs] assert LOW: cfg_err=%u out_err=%u\n", e1, e2);
}
static void sensecap_lcd_cs_deassert() {
const uint8_t TCA = 0x20;
// Output Port 0: all HIGH (CS deasserted, RESX deasserted).
Wire.beginTransmission(TCA);
Wire.write(0x02); // OUTPUT_P0
Wire.write(0xFF);
uint8_t e = Wire.endTransmission();
Serial.printf("[lcd_cs] deassert HIGH: out_err=%u\n", e);
}
void initializeDisplay() {
ESP_LOGI(TAG, "Initializing display...");
// Hold the LCD CS LOW manually for the entire lcd.begin() duration so the
// ST7701 actually receives the SPI init sequence. See ROOT CAUSE NOTE above.
sensecap_lcd_cs_assert();
delay(5);
bool ok = lcd.begin();
Serial.printf("[display] lcd.begin() = %s w=%d h=%d\n", ok ? "OK" : "FAIL", lcd.width(), lcd.height());
// Release LCD CS now that the SPI init sequence is complete. Subsequent
// RGB pixel data goes via the parallel bus and does not need CS.
sensecap_lcd_cs_deassert();
lcd.setBrightness(255);
// Fallback: force GPIO 45 HIGH in case LovyanGFX Light_PWM LEDC setup failed.
pinMode(45, OUTPUT);

37
src/helpers/esp32/SenseCapHAL.h

@ -9,11 +9,15 @@
// Pin encoding matches LovyanGFX convention: (pin_index | IO_EXPANDER)
// IO_EXPANDER = 0x40 (defined as build flag in platformio.ini)
//
// SX1262 expander pin assignments (TCA9535 Port 0):
// pin 0 = NSS (EXPANDER_IO_RADIO_NSS)
// pin 1 = RESET (EXPANDER_IO_RADIO_RST)
// pin 2 = BUSY (EXPANDER_IO_RADIO_BUSY)
// pin 3 = DIO1 (EXPANDER_IO_RADIO_DIO_1)
// TCA9535 Port 0 pin assignments:
// pin 0 = NSS (EXPANDER_IO_RADIO_NSS) — SX1262 chip-select
// pin 1 = RESET (EXPANDER_IO_RADIO_RST) — SX1262 hardware reset
// pin 2 = BUSY (EXPANDER_IO_RADIO_BUSY) — SX1262 busy signal
// pin 3 = DIO1 (EXPANDER_IO_RADIO_DIO_1) — SX1262 IRQ
// pin 4 = display SPI CS — ST7701 chip-select (must stay OUTPUT HIGH)
// pin 5 = display RESX — ST7701 hardware reset (LovyanGFX cfg.pin_rst)
// pin 6 = touch INT — FT5x06 interrupt (input, not used in SW)
// pin 7 = touch RST — FT5x06 reset (not used in SW)
//
// DIO1 interrupt: TCA9535 /INT output → GPIO 42 (IO_EXPANDER_IRQ)
// The /INT pin fires FALLING when any input changes.
@ -85,23 +89,22 @@ public:
// Both this HAL and the touch-read callback must use the same handle.
void setMutex(SemaphoreHandle_t m) { _mutex = m; }
// Call once Wire is running.
// Configures TCA9535 Port 0 radio pins (bits 0-3) only.
// Bits 4-7 are preserved exactly as LovyanGFX left them
// (bit 4 = display SPI CS, must stay OUTPUT HIGH so the
// display ignores any SPI clock pulses from the radio).
// Call once Wire is running, AFTER lcd.begin() has completed.
// Configures TCA9535 Port 0 radio pins (bits 0-3) and locks bit 4 (display CS).
// Bits 5-7 are preserved exactly as LovyanGFX left them.
void initExpander() {
// Read the current register values set by LovyanGFX
uint8_t cur_out = readReg(0x02);
uint8_t cur_cfg = readReg(0x06);
// Bits 0-3 (radio pins): inputs initially, output latch HIGH (de-asserted)
// RadioLib will reconfigure them as needed.
// Bit 4 (display SPI CS): OUTPUT HIGH — permanently de-asserted.
// LovyanGFX leaves this pin as INPUT after lcd.begin(),
// which lets it float. When radio SPI transactions drive
// GPIO 41/48, a floating CS can corrupt the ST7701 display.
// Bits 5-7 (other): preserved exactly as LovyanGFX left them.
// Bits 0-3 (radio): inputs initially; RadioLib reconfigures as needed.
// Output latch HIGH = de-asserted.
// Bit 4 (display CS): OUTPUT HIGH — permanently de-asserted.
// LovyanGFX leaves this as INPUT after lcd.begin();
// a floating CS allows radio SPI to corrupt the ST7701.
// Bit 5 (display RESX): preserved as OUTPUT HIGH — lcd.begin() already pulsed
// it LOW→HIGH to reset the ST7701 before init commands.
// Bits 6-7 (touch INT/RST): preserved as LovyanGFX left them (inputs).
_out0 = (cur_out & 0xE0) | 0x1F; // bits 0-4 = HIGH, bits 5-7 = preserved
_cfg0 = (cur_cfg & 0xE0) | 0x0F; // bits 0-3 = input, bit 4 = OUTPUT, bits 5-7 = preserved

5
variants/sensecap_indicator-espnow/SCIndicatorDisplay.h

@ -58,12 +58,15 @@ public:
cfg.offset_x = 0;
cfg.offset_y = 0;
cfg.offset_rotation = 0;
// pin_rst stays at default (no-connect). Upstream LovyanGFX cannot
// toggle expander pins. The ST7701 RESX (TCA9535 bit 5) is pulsed
// manually by sensecap_lcd_reset_pulse() in main.cpp before lcd.begin().
_panel_instance.config(cfg);
}
{
auto cfg = _panel_instance.config_detail();
cfg.pin_cs = 4 | IO_EXPANDER;
cfg.pin_cs = 4 | IO_EXPANDER; // TCA9535 bit 4 = ST7701 chip-select
cfg.pin_sclk = 41;
cfg.pin_mosi = 48;
cfg.use_psram = 1;

Loading…
Cancel
Save