Display Freezes Unless Touched: LovyanGFX & LVGL Issue

by SLV Team 55 views

Hey guys, having a weird issue with my display and hoping someone can help me out! I'm working on a project using an ESP32-S3 with a parallel RGB display panel (UEDX80480050E-WB-A, to be exact), and I'm using LovyanGFX 1.2.7 and LVGL 8.3.4 with the Arduino framework in VS Code/PlatformIO. I'm trying to display thermal images from an MLX90640, which are sent from another ESP32 via ESP-NOW. The ESP-NOW and SPI communication are solid, but I'm running into a snag with the display.

The Pesky Problem: Image Flow Only When Touching the Screen

The main problem? My display seems to freeze unless I'm physically touching the screen. Let me break it down: I've got my display panel set up with a GUI that has different screens for various functions. One of these screens is dedicated to displaying the thermal camera feed. When I navigate to this thermal camera screen, a single thermal image pops up, but then the display freezes. Here's the really strange part: as soon as I touch the screen, the images start flowing again, updating as they should. But the instant I lift my finger, everything freezes once more. I'm pretty sure it's not a noise issue, given the quality of my signals and power rails.

It's a head-scratcher, right? I'm an analog electrical engineer by trade, so coding, especially embedded stuff, is still a bit new to me. I'm feeling pretty confident that the issue lies somewhere in the interaction between LovyanGFX and LVGL, but I'm having a tough time pinpointing the exact cause. To get your expert eyes on this, I've already shared my spi_link.cpp and lvgl_port.cpp files. And now, let's dive into my GFX header file, which might hold some clues to our mystery.

Diving Deep into the GFX Header File

#pragma once
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#include <lgfx/v1/platforms/esp32s3/Bus_RGB.hpp>
#include <lgfx/v1/platforms/esp32s3/Panel_RGB.hpp>
#include <lgfx/v1/touch/Touch_GT911.hpp>
#include <lgfx/v1/platforms/esp32/Light_PWM.hpp>
#include <driver/i2c.h>

class LGFX : public lgfx::LGFX_Device
{
  lgfx::Panel_RGB   _panel_instance;   // the panel
  lgfx::Bus_RGB     _bus_instance;     // RGB dotclock bus
  lgfx::Touch_GT911 _touch_instance;   // GT911 touch
  lgfx::Light_PWM   _light_instance;   // backlight

public:
  LGFX(void)
  {
    // ---------------- RGB BUS (pins + timings + pixel clock) ----------------
    {
      auto cfg = _bus_instance.config();

      cfg.panel = &_panel_instance;    // bind bus->panel

      cfg.pin_d0  = GPIO_NUM_8;    // B0
      cfg.pin_d1  = GPIO_NUM_3;    // B1
      cfg.pin_d2  = GPIO_NUM_46;   // B2
      cfg.pin_d3  = GPIO_NUM_9;    // B3
      cfg.pin_d4  = GPIO_NUM_1;    // B4
      // G pins
      cfg.pin_d5  = GPIO_NUM_5;    // G0
      cfg.pin_d6  = GPIO_NUM_6;    // G1
      cfg.pin_d7  = GPIO_NUM_7;    // G2
      cfg.pin_d8  = GPIO_NUM_15;   // G3
      cfg.pin_d9  = GPIO_NUM_16;   // G4
      cfg.pin_d10 = GPIO_NUM_4;    // G5
      // R pins
      cfg.pin_d11 = GPIO_NUM_45;   // R0
      cfg.pin_d12 = GPIO_NUM_48;   // R1
      cfg.pin_d13 = GPIO_NUM_47;   // R2
      cfg.pin_d14 = GPIO_NUM_21;   // R3
      cfg.pin_d15 = GPIO_NUM_14;   // R4

      // --- Control pins ---
      cfg.pin_henable = GPIO_NUM_40; // DE
      cfg.pin_vsync   = GPIO_NUM_41; // VSYNC
      cfg.pin_hsync   = GPIO_NUM_39; // HSYNC
      cfg.pin_pclk    = GPIO_NUM_42; // PCLK

      // --- Pixel clock ---
      cfg.freq_write  = 16000000;

      // --- Timings (800x480) ---
      cfg.hsync_polarity    = 0;
      cfg.hsync_front_porch = 40;
      cfg.hsync_pulse_width = 48;
      cfg.hsync_back_porch  = 40;

      cfg.vsync_polarity    = 0;
      cfg.vsync_front_porch = 13;
      cfg.vsync_pulse_width = 1;
      cfg.vsync_back_porch  = 31;
      cfg.pclk_active_neg   = 0;

      _bus_instance.config(cfg);
      _panel_instance.setBus(&_bus_instance);
    }

    // ---------------- PANEL (generic flags) ----------------------------------
    {
      auto cfg = _panel_instance.config();
    
      cfg.pin_cs   = -1;
      cfg.pin_rst  = -1;
      cfg.pin_busy = -1;

      cfg.panel_width   = 800;
      cfg.panel_height  = 480;
      cfg.offset_x      = 0;
      cfg.offset_y      = 0;
      cfg.offset_rotation = 0;

      cfg.dummy_read_pixel = 8;
      cfg.dummy_read_bits  = 1;

      cfg.readable   = false;
      cfg.invert     = false;
      cfg.rgb_order  = false;
      cfg.dlen_16bit = false;
      cfg.bus_shared = false;

      _panel_instance.config(cfg);
    }

    // ---------------- BACKLIGHT (PWM on GPIO2 / ch7) -------------------------
    {
      auto l = _light_instance.config();
      l.pin_bl      = GPIO_NUM_2;
      l.invert      = false;
      l.freq        = 44100;
      l.pwm_channel = 7;
      _light_instance.config(l);
      _panel_instance.setLight(&_light_instance);
    }

    // ---------------- TOUCH (GT911 @ I2C0, force 0x5D) -----------------------
    {
      // Reset dance to force the GT911 to I2C addr 0x5D:
    const int PIN_GT_INT = -1;
    const int PIN_GT_RST = 38;

      auto t = _touch_instance.config();
      t.x_min = 0;   t.x_max = 799;
      t.y_min = 0;   t.y_max = 479;

      t.pin_int = -1;
      t.pin_rst = GPIO_NUM_38;

      t.bus_shared      = false;
      t.offset_rotation = 0;

      // I2C0 wiring on this board
      t.i2c_port = I2C_NUM_0;
      t.i2c_addr = 0x14;
      t.pin_sda  = GPIO_NUM_19;
      t.pin_scl  = GPIO_NUM_20;
      t.freq     = 400000;

      _touch_instance.config(t);
      _panel_instance.setTouch(&_touch_instance);
    }

    setPanel(&_panel_instance);
  }
};

The code you've shared provides a comprehensive configuration for your display setup. You've meticulously set up the RGB bus, panel, backlight, and touch functionalities. Let's break down some key areas that might be contributing to this peculiar behavior.

RGB Bus Configuration

In the RGB bus configuration, you've defined the pin assignments, pixel clock frequency, and timings. The freq_write is set to 16MHz, and the timings for HSYNC and VSYNC are also configured. It's crucial to ensure that these timings align with the display panel's specifications. Any discrepancies here could lead to display issues, but it's less likely to cause the specific behavior you're seeing where touch input triggers updates.

Panel Configuration

The panel configuration includes settings like resolution, offsets, and flags. You've set the panel dimensions to 800x480, which matches your display. The readable flag is set to false, which is a common optimization for performance, as it disables reading from the display memory. Again, everything here seems fairly standard, but we can keep this in mind as we continue to debug.

Touch Controller (GT911) Configuration

This is where things get interesting. You're using a GT911 touch controller, connected via I2C. The configuration includes I2C port details, address, and pin assignments. The touch coordinates are mapped to the display resolution (0-799 for X and 0-479 for Y). You've even included a "reset dance" to force the GT911 to a specific I2C address. The fact that touching the screen makes the display update strongly suggests a connection to the touch controller's behavior.

Potential Areas to Investigate

Given the symptoms, there are a few potential areas we can focus on:

  1. Touch Interrupt Handling: The fact that touching the screen triggers updates implies that the touch controller might be triggering some process that causes the display to refresh. We need to see how the touch input is being handled and if it's somehow tied to the display update loop.
  2. LVGL Integration: LVGL uses a tick-based system for updates. If the touch input is somehow triggering this tick, it could explain the behavior. We need to examine the LVGL integration to see if the lv_tick_inc() function is being called correctly and if there are any issues with the main loop.
  3. Power Saving Modes: It's possible that the display or the ESP32 is entering a power-saving mode, and the touch input is waking it up. We can rule this out by checking the power management settings and ensuring that the display is not being turned off unintentionally.
  4. Task Prioritization: In a multi-tasking environment like FreeRTOS (which Arduino uses), task priorities can affect how often tasks are executed. It's worth investigating if the display update task has a lower priority and is only being triggered when the touch input task runs.

What Code Files Should We Examine Further?

To help me narrow down the issue, could you share the following files (or relevant snippets) if you haven't already?

  1. Main Application Loop: This is where the loop() function resides in your Arduino sketch. It's crucial to see how you're handling the main loop, LVGL tick updates, and any other relevant tasks.
  2. Thermal Camera Screen Code: The code that's specifically responsible for displaying the thermal camera feed. This will help us understand how the images are being processed and displayed.
  3. Touch Input Handling Code: Any code that's handling the touch input from the GT911 controller. This might include interrupt handlers or polling routines.

By examining these files, we can hopefully trace the flow of execution and pinpoint the exact cause of the freezing issue. This is a tricky problem, but we can solve this together!

Let's Crack This Case!

I know this can be frustrating, but debugging is part of the fun (well, sometimes!). The fact that you've got solid ESP-NOW and SPI communication is a huge win. Now, it's just a matter of figuring out this display update puzzle.

I'm here to help you through this. Share the additional code snippets, and let's dig deeper! We'll get those thermal images flowing smoothly in no time. Don't hesitate to ask any questions, no matter how small they might seem. Let's get this done! 😉