Hi everyone,
I need some help.
I’m trying to get this display working in ESP-IDF with LVGL 8.3.
However, the screen keeps flickering…
I’ve already tried changing various settings… and I don’t know what to do.
I managed to get a similar display—the ESP32-4827S043—working.
It had the same problem.
However, applying the same settings doesn’t work because the resolution is different.
Does anyone have any suggestions?
Below is the code I am using:
PanelConfig.c:
#include "f_panelConfig.h"
#include "driver/gpio.h"
/* =========================
* Sunton ESP32-8048S043C
* 4.3" RGB 800x480, ST7262
* ========================= */
#define LCD_H_RES 800
#define LCD_V_RES 480
#define LCD_PIXEL_CLOCK_HZ (16 * 1000 * 1000)
#define LCD_HSYNC_GPIO 39
#define LCD_VSYNC_GPIO 41
#define LCD_DE_GPIO 40
#define LCD_PCLK_GPIO 42
#define LCD_DISP_GPIO GPIO_NUM_NC
/* Ordem RGB565 física do painel */
#define LCD_DATA_R0 45
#define LCD_DATA_R1 48
#define LCD_DATA_R2 47
#define LCD_DATA_R3 21
#define LCD_DATA_R4 14
#define LCD_DATA_G0 5
#define LCD_DATA_G1 6
#define LCD_DATA_G2 7
#define LCD_DATA_G3 15
#define LCD_DATA_G4 16
#define LCD_DATA_G5 4
#define LCD_DATA_B0 8
#define LCD_DATA_B1 3
#define LCD_DATA_B2 46
#define LCD_DATA_B3 9
#define LCD_DATA_B4 1
/* Timing RGB usado pelo painel JC8048B043N_I / ST7262. */
#define LCD_HSYNC_PULSE_WIDTH 4
#define LCD_HSYNC_BACK_PORCH 8 //Estava em 16 e estava ruim.
#define LCD_HSYNC_FRONT_PORCH 8
#define LCD_VSYNC_PULSE_WIDTH 4
#define LCD_VSYNC_BACK_PORCH 8 //Estava em 10 e estava ruim.
#define LCD_VSYNC_FRONT_PORCH 8
esp_lcd_rgb_panel_config_t f_panelConfig_get(void) {
const int data_gpio_nums[16] = {
LCD_DATA_B0,
LCD_DATA_B1,
LCD_DATA_B2,
LCD_DATA_B3,
LCD_DATA_B4,
LCD_DATA_G0,
LCD_DATA_G1,
LCD_DATA_G2,
LCD_DATA_G3,
LCD_DATA_G4,
LCD_DATA_G5,
LCD_DATA_R0,
LCD_DATA_R1,
LCD_DATA_R2,
LCD_DATA_R3,
LCD_DATA_R4
};
esp_lcd_rgb_panel_config_t panel_config = {
.clk_src = LCD_CLK_SRC_PLL160M,
.data_width = 16,
.bits_per_pixel = 16,
.num_fbs = 2, //Estava em 1 (Esse 0 é um teste para ver se o driver aloca 1 frame buffer por padrão, como documentado)
.bounce_buffer_size_px = LCD_H_RES * 16,
.sram_trans_align = 64,
.psram_trans_align = 64,
.hsync_gpio_num = LCD_HSYNC_GPIO,
.vsync_gpio_num = LCD_VSYNC_GPIO,
.de_gpio_num = LCD_DE_GPIO,
.pclk_gpio_num = LCD_PCLK_GPIO,
.disp_gpio_num = LCD_DISP_GPIO,
.data_gpio_nums = {
data_gpio_nums[0], data_gpio_nums[1], data_gpio_nums[2], data_gpio_nums[3],
data_gpio_nums[4], data_gpio_nums[5], data_gpio_nums[6], data_gpio_nums[7],
data_gpio_nums[8], data_gpio_nums[9], data_gpio_nums[10], data_gpio_nums[11],
data_gpio_nums[12], data_gpio_nums[13], data_gpio_nums[14], data_gpio_nums[15]
},
.timings = {
.pclk_hz = LCD_PIXEL_CLOCK_HZ,
.h_res = LCD_H_RES,
.v_res = LCD_V_RES,
.hsync_pulse_width = LCD_HSYNC_PULSE_WIDTH,
.hsync_back_porch = LCD_HSYNC_BACK_PORCH,
.hsync_front_porch = LCD_HSYNC_FRONT_PORCH,
.vsync_pulse_width = LCD_VSYNC_PULSE_WIDTH,
.vsync_back_porch = LCD_VSYNC_BACK_PORCH,
.vsync_front_porch = LCD_VSYNC_FRONT_PORCH,
.flags = {
.hsync_idle_low = 0,
.vsync_idle_low = 0,
.de_idle_high = 0,
.pclk_active_neg = 1,
.pclk_idle_high = 0,
},
},
.flags = {
.fb_in_psram = 1,
.disp_active_low = 0,
},
};
return panel_config;
}
lcd8048.c:
#include "f_lcd8048.h"
#include "f_panelConfig.h"
#include <stdint.h>
#include <stdbool.h>
#include "esp_log.h"
#include "esp_check.h"
#include "driver/gpio.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_rgb.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
static const char *TAG = "f_lcd8048";
static esp_lcd_panel_handle_t s_panel = NULL;
static uint16_t *s_framebuffer = NULL;
static SemaphoreHandle_t s_sem_vsync = NULL;
static volatile bool s_flush_pending = false;
static inline uint16_t rgb565(uint8_t r, uint8_t g, uint8_t b) {
return (uint16_t)(((r & 0xF8) << 8) |
((g & 0xFC) << 3) |
((b & 0xF8) >> 3));
}
static bool IRAM_ATTR rgb_vsync_cb(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx) {
(void)panel;
(void)edata;
(void)user_ctx;
if (!s_flush_pending || s_sem_vsync == NULL) {
return false;
}
BaseType_t hp_task_woken = pdFALSE;
xSemaphoreGiveFromISR(s_sem_vsync, &hp_task_woken);
return (hp_task_woken == pdTRUE);
}
static void backlight_init(void) {
const gpio_config_t io_conf = {
.pin_bit_mask = 1ULL << LCD_BKLT_GPIO,
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
ESP_ERROR_CHECK(gpio_config(&io_conf));
gpio_set_level(LCD_BKLT_GPIO, 0);
}
static void backlight_on(void) {
gpio_set_level(LCD_BKLT_GPIO, 1);
}
static void fill_screen(uint16_t color) {
if (s_framebuffer == NULL) {return;}
const size_t total_pixels = LCD_H_RES * LCD_V_RES;
for (size_t i = 0; i < total_pixels; i++) {
s_framebuffer[i] = color;
}
}
static void draw_test_pattern(void) {
if (s_framebuffer == NULL) {
return;
}
const uint16_t colors[] = {
rgb565(255, 255, 255), // branco
rgb565(255, 255, 0), // amarelo
rgb565( 0, 255, 255), // ciano
rgb565( 0, 255, 0), // verde
rgb565(255, 0, 255), // magenta
rgb565(255, 0, 0), // vermelho
rgb565( 0, 0, 255), // azul
rgb565( 0, 0, 0) // preto
};
const int bar_count = sizeof(colors) / sizeof(colors[0]);
const int bar_width = LCD_H_RES / bar_count;
for (int y = 0; y < LCD_V_RES; y++) {
for (int x = 0; x < LCD_H_RES; x++) {
int idx = x / bar_width;
if (idx >= bar_count) {
idx = bar_count - 1;
}
s_framebuffer[y * LCD_H_RES + x] = colors[idx];
}
}
for (int x = 0; x < LCD_H_RES; x++) {
s_framebuffer[x] = rgb565(255, 255, 255);
s_framebuffer[(LCD_V_RES - 1) * LCD_H_RES + x] = rgb565(255, 255, 255);
}
for (int y = 0; y < LCD_V_RES; y++) {
s_framebuffer[y * LCD_H_RES] = rgb565(255, 255, 255);
s_framebuffer[y * LCD_H_RES + (LCD_H_RES - 1)] = rgb565(255, 255, 255);
}
}
esp_err_t f_setup_lcd8048(void) {
if (s_panel != NULL) {ESP_LOGW(TAG, "Display already initialized.");return ESP_OK;}
backlight_init();
esp_lcd_rgb_panel_config_t panel_config = f_panelConfig_get();
ESP_RETURN_ON_ERROR(esp_lcd_new_rgb_panel(&panel_config, &s_panel), TAG, "esp_lcd_new_rgb_panel falhou");
ESP_RETURN_ON_ERROR(esp_lcd_panel_reset(s_panel), TAG, "esp_lcd_panel_reset falhou");
ESP_RETURN_ON_ERROR(esp_lcd_panel_init(s_panel), TAG, "esp_lcd_panel_init falhou");
if (s_sem_vsync == NULL) {
s_sem_vsync = xSemaphoreCreateBinary();
ESP_RETURN_ON_FALSE(s_sem_vsync != NULL, ESP_ERR_NO_MEM, TAG, "Falha ao criar s_sem_vsync");
}
s_flush_pending = false;
xSemaphoreTake(s_sem_vsync, 0);
esp_lcd_rgb_panel_event_callbacks_t cbs = {
// .on_bounce_frame_finish = rgb_bounce_frame_finish_cb,
.on_vsync = rgb_vsync_cb,
};
ESP_RETURN_ON_ERROR(esp_lcd_rgb_panel_register_event_callbacks(s_panel, &cbs, NULL), TAG, "esp_lcd_rgb_panel_register_event_callbacks falhou");
void *fb0 = NULL;
ESP_RETURN_ON_ERROR(esp_lcd_rgb_panel_get_frame_buffer(s_panel, 1, &fb0), TAG,"esp_lcd_rgb_panel_get_frame_buffer falhou");
s_framebuffer = (uint16_t *)fb0;
backlight_on();
fill_screen(rgb565(0, 0, 0));
// draw_test_pattern();
ESP_LOGI(TAG, "Display 4.3 initialized successfully (%dx%d).", LCD_H_RES, LCD_V_RES);
return ESP_OK;
}
void f_display43_begin_flush_sync(void) {
s_flush_pending = true;
if (s_sem_vsync != NULL) {
xSemaphoreTake(s_sem_vsync, 0);
}
}
bool f_display43_wait_flush_release(TickType_t timeout_ticks) {
if (s_sem_vsync == NULL) {
return false;
}
if (xSemaphoreTake(s_sem_vsync, timeout_ticks) != pdTRUE) {
return false;
}
s_flush_pending = false;
return true;
}
esp_lcd_panel_handle_t f_display43_get_panel(void) {return s_panel;}
uint16_t *f_display43_get_framebuffer(void) {return s_framebuffer; }
init-lvgl.c:
#include "f_init-lvgl.h"
#include "f_lcd8048.h"
#include "f_panelConfig.h"
#include <string.h>
#include "esp_log.h"
#include "esp_check.h"
#include "esp_timer.h"
#include "esp_heap_caps.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_task_wdt.h"
static const char *TAG = "f_init_lvgl";
#define LVGL_TICK_PERIOD_MS 2 //Estava 10
#define LVGL_TASK_PERIOD_MS 20 // Estava 40
#define LVGL_BUF_LINES 160
static SemaphoreHandle_t s_lvgl_mutex = NULL;
static esp_timer_handle_t s_lvgl_tick_timer = NULL;
static TaskHandle_t s_lvgl_task_handle = NULL;
static lv_disp_draw_buf_t s_draw_buf;
static lv_color_t *s_buf1 = NULL;
static lv_color_t *s_buf2 = NULL;
static lv_disp_drv_t s_disp_drv;
static lv_disp_t *s_disp = NULL;
static bool s_lvgl_initialized = false;
static void lvgl_tick_cb(void *arg) {
(void)arg;
lv_tick_inc(LVGL_TICK_PERIOD_MS);
}
// static void lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) {
// esp_lcd_panel_handle_t panel = f_display43_get_panel();
// if (panel == NULL) { lv_disp_flush_ready(drv);return;}
// f_display43_begin_flush_sync();
// if (!f_display43_wait_flush_release(pdMS_TO_TICKS(100))) {ESP_LOGW(TAG, "Timeout waiting for VSYNC for flush");}
// esp_err_t err = esp_lcd_panel_draw_bitmap(panel, area->x1, area->y1, area->x2 + 1, area->y2 + 1, color_map);
// if (err != ESP_OK) {ESP_LOGE(TAG, "esp_lcd_panel_draw_bitmap failed: %s", esp_err_to_name(err));}
// lv_disp_flush_ready(drv);
// }
static void lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) {
esp_lcd_panel_handle_t panel = f_display43_get_panel();
if (panel == NULL) {
lv_disp_flush_ready(drv);
return;
}
esp_err_t err = esp_lcd_panel_draw_bitmap(panel, area->x1, area->y1, area->x2 + 1, area->y2 + 1, color_map);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_lcd_panel_draw_bitmap failed: %s", esp_err_to_name(err));
}
lv_disp_flush_ready(drv);
}
static void lvgl_port_task(void *arg) {
(void)arg;
esp_task_wdt_delete(NULL);
while (1) {
lock();
lv_timer_handler();
unlock();
vTaskDelay(pdMS_TO_TICKS(LVGL_TASK_PERIOD_MS));
}
}
void lock(void) { if (s_lvgl_mutex == NULL) { return; } xSemaphoreTake(s_lvgl_mutex, portMAX_DELAY); }
void unlock(void) { if (s_lvgl_mutex != NULL) { xSemaphoreGive(s_lvgl_mutex); } }
lv_disp_t *f_lvgl_get_display(void) { return s_disp;}
esp_err_t f_init_lvgl(void) {
if (s_lvgl_initialized) {
ESP_LOGW(TAG, "LVGL already initialized.");
return ESP_OK;
}
//ESP_RETURN_ON_ERROR(f_setupDisplay43(), TAG, "f_setupDisplay43 falhou");
if (s_lvgl_mutex == NULL) {
s_lvgl_mutex = xSemaphoreCreateMutex();
ESP_RETURN_ON_FALSE(s_lvgl_mutex != NULL, ESP_ERR_NO_MEM, TAG, "Failed to create LVGL mutex");
}
lv_init();
const size_t buf_pixels = LCD_H_RES * LVGL_BUF_LINES;
const size_t buf_bytes = buf_pixels * sizeof(lv_color_t);
s_buf1 = heap_caps_malloc(buf_bytes, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
s_buf2 = heap_caps_malloc(buf_bytes, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(s_buf1 != NULL, ESP_ERR_NO_MEM, TAG, "Failed to allocate s_buf1");
ESP_RETURN_ON_FALSE(s_buf2 != NULL, ESP_ERR_NO_MEM, TAG, "Failed to allocate s_buf2");
memset(s_buf1, 0, buf_bytes);
memset(s_buf2, 0, buf_bytes);
lv_disp_draw_buf_init(&s_draw_buf, s_buf1, s_buf2, buf_pixels);
lv_disp_drv_init(&s_disp_drv);
s_disp_drv.hor_res = LCD_H_RES;
s_disp_drv.ver_res = LCD_V_RES;
s_disp_drv.flush_cb = lvgl_flush_cb;
s_disp_drv.draw_buf = &s_draw_buf;
s_disp_drv.full_refresh = 0;
s_disp = lv_disp_drv_register(&s_disp_drv);
ESP_RETURN_ON_FALSE(s_disp != NULL, ESP_FAIL, TAG, "Failed to register display in LVGL");
const esp_timer_create_args_t tick_args = {
.callback = lvgl_tick_cb,
.arg = NULL,
.dispatch_method = ESP_TIMER_TASK,
.name = "lvgl_tick"
};
ESP_RETURN_ON_ERROR(esp_timer_create(&tick_args, &s_lvgl_tick_timer), TAG, "esp_timer_create failed");
ESP_RETURN_ON_ERROR(esp_timer_start_periodic(s_lvgl_tick_timer, LVGL_TICK_PERIOD_MS * 1000), TAG, "esp_timer_start_periodic failed");
BaseType_t task_ok = xTaskCreatePinnedToCore(lvgl_port_task, "t_lvgl", 4000, NULL, tskIDLE_PRIORITY+5, &s_lvgl_task_handle, 1);
ESP_RETURN_ON_FALSE(task_ok == pdPASS, ESP_FAIL, TAG, "Failed to create LVGL task");
s_lvgl_initialized = true;
ESP_LOGI(TAG, "LVGL initialized successfully.");
ESP_LOGI(TAG, "Resolution: %dx%d", LCD_H_RES, LCD_V_RES);
ESP_LOGI(TAG, "Buffer lines: %d", LVGL_BUF_LINES);
ESP_LOGI(TAG, "Heap internal livre: %u", (unsigned)heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
ESP_LOGI(TAG, "Heap PSRAM livre: %u", (unsigned)heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
return ESP_OK;
}

