Yes sure, actually, i was asking this question because i'm trying to work with an LCD screen with 565 RGB and i have some problems with the screen, so i did some tests with the memory to see if i understood well.
I thought: if i'm working with SPRAM at 80Mhz with 8 channels, it means 80MB/s, also, i read that the SPRAM in my esp32-s3-wroom1-n16r8 is DDR, so it should be 160MB/s.
I will paste here lcd.h and lcd.c.
header:
Code: Select all
#pragma once
#include "lvgl.h"
#include "utils.h"
#include "esp_timer.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_rgb.h"
#include "esp_lcd_touch.h"
#include "esp_lcd_touch_gt911.h"
#include "esp_lvgl_port.h"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define SETTE_POLLICI 0
#define DIECI_POLLICI 1
#define SELECTED_TFT DIECI_POLLICI
#if SELECTED_TFT == SETTE_POLLICI
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (10 * 1000 * 1000)
#define EXAMPLE_LCD_H_RES 800
#define EXAMPLE_LCD_V_RES 480
#define HSYNC_BACK_PORCH 43
#define HSYNC_FRONT_PORCH 8
#define HSYNC_PULSE_WIDTH 4
#define VSYNC_BACK_PORCH 12
#define VSYNC_FRONT_PORCH 37
#define VSYNC_PULSE_WIDTH 37
#define MS_BETWEEN_FRAMES 32
#elif SELECTED_TFT == DIECI_POLLICI
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (16 * 1000 * 1000)
#define EXAMPLE_LCD_H_RES 1024
#define EXAMPLE_LCD_V_RES 600
#define HSYNC_BACK_PORCH 160
#define HSYNC_FRONT_PORCH 16
#define HSYNC_PULSE_WIDTH 1
#define VSYNC_BACK_PORCH 23
#define VSYNC_FRONT_PORCH 12
#define VSYNC_PULSE_WIDTH 1
#define MS_BETWEEN_FRAMES 38 // lo schermo può andare a 60 fps a livello teorico
#endif
#define EXAMPLE_PIN_NUM_HSYNC 6
#define EXAMPLE_PIN_NUM_VSYNC 7
#define EXAMPLE_PIN_NUM_DE 15
#define EXAMPLE_PIN_NUM_PCLK 21
#define EXAMPLE_PIN_NUM_DATA0 42 // B3
#define EXAMPLE_PIN_NUM_DATA1 44 // B4
#define EXAMPLE_PIN_NUM_DATA2 43 // B5
#define EXAMPLE_PIN_NUM_DATA3 2 // B6
#define EXAMPLE_PIN_NUM_DATA4 1 // B7
#define CONFIG_EXAMPLE_DOUBLE_FB 0
#define CONFIG_EXAMPLE_AVOID_TEAR_EFFECT_WITH_SEM 0
#define EXAMPLE_PIN_NUM_DATA5 14 // G2
#define EXAMPLE_PIN_NUM_DATA6 46 // G3
#define EXAMPLE_PIN_NUM_DATA7 38 // G4
#define EXAMPLE_PIN_NUM_DATA8 39 // G5
#define EXAMPLE_PIN_NUM_DATA9 40 // G6
#define EXAMPLE_PIN_NUM_DATA10 41 // G7
#define EXAMPLE_PIN_NUM_DATA11 47 // R3
#define EXAMPLE_PIN_NUM_DATA12 48 // R4
#define EXAMPLE_PIN_NUM_DATA13 45 // R5
#define EXAMPLE_PIN_NUM_DATA14 0 // R6
#define EXAMPLE_PIN_NUM_DATA15 17 // R7
#define EXAMPLE_PIN_NUM_DISP_EN -1
// The pixel number in horizontal and vertical
#define EXAMPLE_LVGL_TICK_PERIOD_MS 2
void increase_lvgl_tick(void *arg);
void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map);
bool example_on_vsync_event(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *event_data, void *user_data);
void lcd_init();
void lvgl_task(void *pvParameters);
source:
Code: Select all
#include "lcd.h"
#include "i2c.h"
static const char *TAG = "LCD SCREEN";
#include "driver/gpio.h"
#include "utils.h"
// we use two semaphores to sync the VSYNC event and the LVGL task, to avoid potential tearing effect
#if CONFIG_EXAMPLE_AVOID_TEAR_EFFECT_WITH_SEM
SemaphoreHandle_t sem_vsync_end;
SemaphoreHandle_t sem_gui_ready;
#endif
bool example_on_vsync_event(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *event_data, void *user_data)
{
BaseType_t high_task_awoken = pdFALSE;
#if CONFIG_EXAMPLE_AVOID_TEAR_EFFECT_WITH_SEM
if (xSemaphoreTakeFromISR(sem_gui_ready, &high_task_awoken) == pdTRUE) {
xSemaphoreGiveFromISR(sem_vsync_end, &high_task_awoken);
}
#endif
return high_task_awoken == pdTRUE;
}
void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
int offsetx1 = area->x1;
int offsetx2 = area->x2;
int offsety1 = area->y1;
int offsety2 = area->y2;
#if CONFIG_EXAMPLE_AVOID_TEAR_EFFECT_WITH_SEM
xSemaphoreGive(sem_gui_ready);
xSemaphoreTake(sem_vsync_end, portMAX_DELAY);
#endif
// pass the draw buffer to the driver
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
lv_disp_flush_ready(drv);
}
void increase_lvgl_tick(void *arg)
{
/* Tell LVGL how many milliseconds has elapsed */
lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
}
void lcd_init()
{
static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
static lv_disp_drv_t disp_drv; // contains callback functions
#if CONFIG_EXAMPLE_AVOID_TEAR_EFFECT_WITH_SEM
ESP_LOGI(TAG, "Create semaphores");
sem_vsync_end = xSemaphoreCreateBinary();
assert(sem_vsync_end);
sem_gui_ready = xSemaphoreCreateBinary();
assert(sem_gui_ready);
#endif
ESP_LOGI(TAG, "Install RGB LCD panel driver");
esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_rgb_panel_config_t panel_config = {
.data_width = 16,
.clk_src = LCD_CLK_SRC_PLL240M,
.disp_gpio_num = EXAMPLE_PIN_NUM_DISP_EN,
.pclk_gpio_num = EXAMPLE_PIN_NUM_PCLK,
.vsync_gpio_num = EXAMPLE_PIN_NUM_VSYNC,
.hsync_gpio_num = EXAMPLE_PIN_NUM_HSYNC,
.de_gpio_num = EXAMPLE_PIN_NUM_DE,
.data_gpio_nums = {
EXAMPLE_PIN_NUM_DATA0,
EXAMPLE_PIN_NUM_DATA1,
EXAMPLE_PIN_NUM_DATA2,
EXAMPLE_PIN_NUM_DATA3,
EXAMPLE_PIN_NUM_DATA4,
EXAMPLE_PIN_NUM_DATA5,
EXAMPLE_PIN_NUM_DATA6,
EXAMPLE_PIN_NUM_DATA7,
EXAMPLE_PIN_NUM_DATA8,
EXAMPLE_PIN_NUM_DATA9,
EXAMPLE_PIN_NUM_DATA10,
EXAMPLE_PIN_NUM_DATA11,
EXAMPLE_PIN_NUM_DATA12,
EXAMPLE_PIN_NUM_DATA13,
EXAMPLE_PIN_NUM_DATA14,
EXAMPLE_PIN_NUM_DATA15,
},
.timings = {
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
.h_res = EXAMPLE_LCD_H_RES,
.v_res = EXAMPLE_LCD_V_RES,
// The following parameters should refer to LCD spec
.hsync_back_porch = HSYNC_BACK_PORCH,
.hsync_front_porch = HSYNC_FRONT_PORCH,
.hsync_pulse_width = HSYNC_PULSE_WIDTH,
.vsync_back_porch = VSYNC_BACK_PORCH,
.vsync_front_porch = VSYNC_FRONT_PORCH,
.vsync_pulse_width = VSYNC_PULSE_WIDTH,
},
.flags.fb_in_psram = true,
#if CONFIG_EXAMPLE_DOUBLE_FB
.flags.double_fb = true,
#else
.flags.double_fb = false,
#endif
};
ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
ESP_LOGI(TAG, "Register event callbacks");
esp_lcd_rgb_panel_event_callbacks_t cbs = {
.on_vsync = example_on_vsync_event,
};
ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(panel_handle, &cbs, &disp_drv));
ESP_LOGI(TAG, "Initialize RGB LCD panel");
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
// LIVELLO LVGL IN POI, DUNQUE CONFIGURO IL TOUCH
ESP_LOGI(TAG, "Initialize LVGL library");
lv_init();
void *buf1 = NULL;
void *buf2 = NULL;
ESP_LOGI(TAG, "Allocate separate LVGL draw buffers from PSRAM");
/*
*/
#if CONFIG_EXAMPLE_DOUBLE_FB
ESP_LOGI(TAG, "Use frame buffers as LVGL draw buffers");
ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 2, &buf1, &buf2));
// initialize LVGL draw buffers
lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES);
#else
gpio_set_level(GPIO_NUM_8, 1);
buf1 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), MALLOC_CAP_SPIRAM);
assert(buf1);
gpio_set_level(GPIO_NUM_8, 0);
buf2 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), MALLOC_CAP_SPIRAM);
assert(buf2);
// initialize LVGL draw buffers
lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * 100);
#endif
ESP_LOGI(TAG, "Register display driver to LVGL");
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = EXAMPLE_LCD_H_RES;
disp_drv.ver_res = EXAMPLE_LCD_V_RES;
disp_drv.flush_cb = example_lvgl_flush_cb;
disp_drv.draw_buf = &disp_buf;
disp_drv.user_data = panel_handle;
#if CONFIG_EXAMPLE_DOUBLE_FB
disp_drv.full_refresh = true; // the full_refresh mode can maintain the synchronization between the two frame buffers
#else
disp_drv.full_refresh = false;
#endif
lv_disp_t *disp = lv_disp_drv_register(&disp_drv);
esp_lcd_touch_handle_t tp;
/* Initialize touch */
const esp_lcd_touch_config_t tp_cfg = {
.x_max = EXAMPLE_LCD_H_RES,
.y_max = EXAMPLE_LCD_V_RES,
.rst_gpio_num = -1,
.int_gpio_num = -1,
.levels = {
.reset = 0,
.interrupt = 0,
},
.flags = {
.swap_xy = 0,
.mirror_x = 0,
.mirror_y = 0,
},
};
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
const esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)I2C_NUM_0, &tp_io_config, &tp_io_handle));
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &tp));
assert(tp);
/* Add touch input (for selected screen) */
const lvgl_port_touch_cfg_t touch_cfg = {
.disp = disp,
.handle = tp,
};
lvgl_port_add_touch(&touch_cfg);
ESP_LOGI(TAG, "Install LVGL tick timer");
// Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
const esp_timer_create_args_t lvgl_tick_timer_args = {
.callback = &increase_lvgl_tick,
.name = "lvgl_tick"
};
esp_timer_handle_t lvgl_tick_timer = NULL;
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));
//ESP_LOGI(TAG, "Display LVGL Scatter Chart");
}
void lvgl_task(void *pvParameters) {
for (;;) {
// raise the task priority of LVGL and/or reduce the handler period can improve the performance
vTaskDelay(pdMS_TO_TICKS(MS_BETWEEN_FRAMES));
// The task running lv_timer_handler should have lower priority than that running `lv_tick_inc`
lv_timer_handler();
}
}
As you can see, i allocate the memory i need with heap_caps_malloc, when i'm not using double frame buffer, with this configuration, i can go at high fps and the screen movements seems smooth (even with 1024x600), as long as you change only a fer pixels at a time.
When i need to change the whole screen or i have large widgets where is needed to change a lot of pixels, the problems start to arise.
I doubt that i'm not using correctly the GDMA, or maybe LVGL is not using the GDMA when it accesses the memory.
A help to understand the whole mechanism would be really appreciated! Thank you for your time and your patience.