Erste Schritte mit dem Sunton ESP32-S3
7 Zoll Display, LovyanGFX und LVGL

Das ESP32-S3 7 Zoll Display von Sunton ist eines der wenigen Boards, das einen modernen Mikrocontroller mit einem vergleichsweise großen Display kombiniert. Da meine ersten Schritte mit dem Board etwas holprig verlaufen sind, möchte ich es hier kurz vorstellen und zeigen, wie es mit wenigen Schritten in Betrieb genommen werden kann. Im Gegensatz zum mitgelieferten Beispiel Code verwende ich dafür nicht ArduinoGFX, sondern die meiner Meinung nach bessere LovyanGFX Bibliothek.

Eigenschaften des Sunton ESP32-S3 7 Zoll Displays

  • ESP32-S3 Mikrocontroller mit 8M PSRAM und 16M Flash.
  • 7 Zoll TN-Display mit einer Auflösung von 800x480 Pixel und paralleler RGB-565-Schnittstelle.
  • Kapazitiver 5-Punkt-Touch-Controller
  • I2S Audio Ausgabe mit MAX98357 Klasse D Verstärker
  • Micro-SD-Karten Slot
  • IO-Ports für Erweiterungen
  • USB Typ-C Anschluss

Makerfabs ESP32-S3 7 Zoll Display

Makerfabs ESP32-S3 7 Zoll Display

Leider hat das Board auch einige Schwachpunkte:

  • Kein IPS Panel, daher ist das Bild relativ kontrastarm und sehr blickwinkelabhängig.
  • Da das Display sehr viele Ports des ESP32 benötigt, stehen nur wenige Ports für Erweiterungen zur Verfügung.
  • Die 5V Versorgungsspannung steht an den Ports nicht zur Verfügung.
  • Das Board verwendet nicht den integrierten USB Controller des ESP32-S3. Daher ist auch kein JTAG Debugging möglich, da die Pins für den SD-Karten Slot verwendet werden.
  • Im Gegensatz zu den kleineren Displays von Sunton ist auf der Vorderseite kein Fotowiderstand verbaut, um die Helligkeit des Displays automatisch regeln zu können. 

PlatformIO Projekt einrichten

Der Code dieses kleinen Demo-Projekts ist in Visual Studio Code mit dem PlatformIO Plugin entwickelt. Bevor das Projekt gestartet wird, ist es erforderlich, eine Konfigurationsdatei für das Sunton Display mit dem folgenden Inhalt zu erstellen:

{
  "build": {
    "arduino": {
      "ldscript": "esp32s3_out.ld",
      "partitions": "default_16MB.csv",
      "memory_type": "qio_opi"
    },
    "core": "esp32",
    "extra_flags": [
      "-DARDUINO_ESP32S3_DEV",
      "-DBOARD_HAS_PSRAM",
      "-DARDUINO_USB_MODE=1",
      "-DARDUINO_RUNNING_CORE=1",
      "-DARDUINO_EVENT_RUNNING_CORE=1",
      "-DARDUINO_USB_CDC_ON_BOOT=0"
    ],
    "f_cpu": "240000000L",
    "f_flash": "80000000L",
    "flash_mode": "qio",
    "hwids": [
      [
        "0x303A",
        "0x1001"
      ]
    ],
    "mcu": "esp32s3",
    "variant": "esp32s3"
  },
  "connectivity": [
    "wifi"
  ],
  "debug": {
    "openocd_target": "esp32s3.cfg"
  },
  "frameworks": [
    "arduino",
    "espidf"
  ],
  "name": "Sunton ESP32-S3",
  "upload": {
    "flash_size": "16MB",
    "maximum_ram_size": 327680,
    "maximum_size": 16777216,    
    "use_1200bps_touch": true,
    "wait_for_upload_port": true,
    "require_upload_port": true,
    "speed": 460800
  },
  "url": "https://www.makerfabs.com/sunton-esp32-s3-7-inch-tn-display-with-touch.html",
  "vendor": "Sunton"
}


Diese Datei wird unter dem Namen sunton_s3.json im lokalen Benutzerverzeichnis in das Verzeichnis .platformio\platforms\espressif32\boards\ gepeichert. 

Danach wird in PlatformIO ein neues Projekt erzeugt. In der Liste der Boards wird das eben erstelle Board ausgewählt. Als Framework wird das Arduino Framework ausgewählt.

PlatformIO project for Makerfabs ESP32-S3 7 inch display

In dem neuen Projekt wird zunächst die Datei platform.ini angepasst. Folgende Änderungen werden darin gemacht:

  • Version 6.3.1 des Espressif Frameworks verwenden
  • C++ Standard 17 statt 11 verwenden
  • Mehr Compiler Optimierungen einschalten
  • EInbinden der LovyanGFX und LVGL Bibliotheken
  • Include Pfad für LVGL anpassen, damit lvgl.h im eigenen Projekt gefunden wird

Der Inhalt sieht dann folgendermaßen aus:

; 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/projectconf.html

[env:sunton_s3]
platform = espressif32@6.3.1
board = sunton_s3
framework = arduino
monitor_speed = 115200
monitor_port = COM3
upload_port = COM3

build_unflags = 
    -Os
    -std=gnu++11
build_flags = 
    -O3
    -std=gnu++17
    -DCORE_DEBUG_LEVEL=3
    -DLV_CONF_INCLUDE_SIMPLE
    -DLV_CONF_SUPPRESS_DEFINE_CHECK
    -I./src

lib_deps =
    lovyan03/LovyanGFX@^1.1.7
    lvgl/lvgl@^8.3.7

 

LovyanGFX Treiber

Derzeitt gibt es noch keinen Treiber für das Sunton ESP32-S3 7 Zoll Display. Es sind jedoch Treiber für kleinere Versionen des Displays verfügbar. Es reicht also aus, die Konfiguration für das 7 Zoll Display zu adaptieren und im Projekt zu speichern. Die angepasste Version sieht folgendermaßen aus:

#define LGFX_USE_V1
#include <LovyanGFX.hpp>

#include <lgfx/v1/platforms/esp32s3/Panel_RGB.hpp>
#include <lgfx/v1/platforms/esp32s3/Bus_RGB.hpp>
#include <driver/i2c.h>

class LGFX : public lgfx::LGFX_Device
{
public:

  lgfx::Bus_RGB     _bus_instance;
  lgfx::Panel_RGB   _panel_instance;
  lgfx::Light_PWM   _light_instance;
  lgfx::Touch_GT911 _touch_instance;

  LGFX(void)
  {
    {
      auto cfg = _panel_instance.config();

      cfg.memory_width  = 800;
      cfg.memory_height = 480;
      cfg.panel_width  = 800;
      cfg.panel_height = 480;

      cfg.offset_x = 0;
      cfg.offset_y = 0;

      _panel_instance.config(cfg);
    }

    {
      auto cfg = _panel_instance.config_detail();

      cfg.use_psram = 1;

      _panel_instance.config_detail(cfg);
    }

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

      cfg.pin_henable = GPIO_NUM_41;
      cfg.pin_vsync   = GPIO_NUM_40;
      cfg.pin_hsync   = GPIO_NUM_39;
      cfg.pin_pclk    = GPIO_NUM_42;
      cfg.freq_write  = 12000000;

      cfg.hsync_polarity    = 0;
      cfg.hsync_front_porch = 8;
      cfg.hsync_pulse_width = 2;
      cfg.hsync_back_porch  = 43;
      cfg.vsync_polarity    = 0;
      cfg.vsync_front_porch = 8;
      cfg.vsync_pulse_width = 2;
      cfg.vsync_back_porch  = 12;
      cfg.pclk_idle_high    = 1;
      _bus_instance.config(cfg);
    }
    _panel_instance.setBus(&_bus_instance);

    {
      auto cfg = _light_instance.config();
      cfg.pin_bl = GPIO_NUM_2;
      _light_instance.config(cfg);
    }
    _panel_instance.light(&_light_instance);

    {
      auto cfg = _touch_instance.config();
      cfg.x_min      = 0;
      cfg.y_min      = 0;
      cfg.bus_shared = false;
      cfg.offset_rotation = 0;
      // I2C connection
      cfg.i2c_port   = I2C_NUM_0;
      cfg.pin_sda    = GPIO_NUM_19;
      cfg.pin_scl    = GPIO_NUM_20;
      cfg.pin_int    = GPIO_NUM_NC;
      cfg.pin_rst    = GPIO_NUM_38;
      cfg.x_max      = 800;
      cfg.y_max      = 480;
      cfg.freq       = 100000;
      _touch_instance.config(cfg);
      _panel_instance.setTouch(&_touch_instance);
    }

    setPanel(&_panel_instance);
  }
};


Anfangs hatte ich das Problem, dass das Bild bei hoher CPU Last des ESP32 gelegentlich geflackert hat. In dem Video kann man das Flackern gut sehen:

Die Ursache war der zu hohe Wert für cfg.freq_write. Erst mit einem Wert von 12000000 lief das Display auch unter hoher Last problemlos.

GUI erstellen mit LVGL

Bevor die ersten Widgets mit der LVGL Bibliothek angezeigt werden können, sind noch einige Schritte nötig:

  • Die Datei lvgl/lv_conf_template.h in das lokale Projekt unter den Namen lv_conf.h kopieren
  • Zeichenpuffer erstellen
  • Funktion erstellen, die das gerenderte Bild in den Speicher des Displays kopiert
  • Funktion erstellen, die ein Eingabe des Touchpad liest

Mehr Infos dazu findet man in der LovyanGFX Dokumenation.

Für das Sunton Display siehtt der Code dazu folgendermaßen aus:

#include <Arduino.h>
#include <Wire.h>
#include <lv_conf.h>
#include <lvgl.h>
#include "gui.h"

#include "../ui/ui.h"
#include "../gfx/LGFX_ESP32S3_RGB_MakerfabsParallelTFTwithTouch70.h"


static const char* TAG = "gui";

static const uint16_t screenWidth  = 800;
static const uint16_t screenHeight = 480;

static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[2][ screenWidth * 10 ];

LGFX gfx;

void my_disp_flush( lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p )
{
    if (gfx.getStartCount() == 0)
    {   
        gfx.startWrite();
    }
    gfx.pushImageDMA( area->x1
                    , area->y1
                    , area->x2 - area->x1 + 1
                    , area->y2 - area->y1 + 1
                    , ( lgfx::rgb565_t* )&color_p->full);
    lv_disp_flush_ready( disp );
}

void my_touchpad_read( lv_indev_drv_t * indev_driver, lv_indev_data_t * data )
{
    uint16_t touchX, touchY;

    data->state = LV_INDEV_STATE_REL;

    if( gfx.getTouch( &touchX, &touchY ) )
    {
        data->state = LV_INDEV_STATE_PR;

        /*Set the coordinates*/
        data->point.x = touchX;
        data->point.y = touchY;
    }
}

void gui_start(){

  // ----------- GFX -------------
  gfx.begin();
  gfx.setBrightness(127);


  lv_init();
  lv_disp_draw_buf_init( &draw_buf, buf[0], buf[1], screenWidth * 10 );

  /*Initialize the display*/
  static lv_disp_drv_t disp_drv;
  lv_disp_drv_init( &disp_drv );
  disp_drv.hor_res = screenWidth;
  disp_drv.ver_res = screenHeight;
  disp_drv.flush_cb = my_disp_flush;
  disp_drv.draw_buf = &draw_buf;
  lv_disp_drv_register( &disp_drv );

  /*Initialize the input device driver*/
  static lv_indev_drv_t indev_drv;
  lv_indev_drv_init( &indev_drv );
  indev_drv.type = LV_INDEV_TYPE_POINTER;
  indev_drv.read_cb = my_touchpad_read;
  lv_indev_drv_register( &indev_drv );

  // Your UI code or SquareLine Studio Code
  ui_init(); 
}


Damit ist der Treiber initialisiert und das eigene UI kann umgesetzt werden. Das kann von Hand gemacht werden, aber auch mit dem grafischen UI Editor SquareLine Studio, was das Erstellen deutlich vereinfacht. Für kleine Projekte ist das Tool gratis, bei größeren Projekten muss man eine monatliche bzw. jährliche Lizenz erwerben. Der Vorteil von SquareLine Studio ist, dass das grafische UI einfach exportiert und in das eigene Projekt kopiert werden kann.

Arduino Code

Was noch fehlt sind ein paar Zeilen Arduino Code mit der setup() und der loop() Methode:

#include <Arduino.h>
#include <lv_conf.h>
#include <lvgl.h>

#include "gui/gui.h"

void setup() {
  gui_start();
}

void loop() {
  lv_timer_handler();

  delay(5);
}


Damit ist das kleine Beispiel komplett. Bei Fragen könnt ihr mir gerne eine Nachricht hinterlassen. Der komplette Sourcecode ist auf GitHub verfügbar: https://github.com/HarryVienna/Makerfabs-Sunton-ESP32-S3-7-Display-with-LovyanGFX-and-LVGL

Konversation wird geladen