Status of esp_lcd component

crosser
Posts: 3
Joined: Sun Sep 29, 2024 7:42 pm

Status of esp_lcd component

Postby crosser » Sun Sep 29, 2024 8:34 pm

I was trying to port the driver for RM67162 OLED panel controller that comes with recent lilygo board from adhoc arduino version that lilygo provides to "proper" component that could work with idf's `esp_lcd`, to leverage queued transactions.

Existing and working driver runs transactions using command 0x02 (8 bit) (or 0x32 for pixel data write) and 24 bit "address" that is constructed from a traditional LCD panel command left-shifted by 8 bits, for example `cmd=0x02, addr=0x11<<8` for "sleep out" command. When the command requires parameters, they are passed via `tx_buffer` / `length` fields of the spi transaction structure. To clarify, I modified the code to work without arduino component, under pure idf. It works.

But looking at the code in `components/esp_lcd`, there is no mention of `cmd` and `addr` fields. Command and parameter data seem to be transmitted entirely via the `tx_buffer` pointer, in separate transactions.

As the board does not use DC pin, I can imagine that things _may_ work if you put command and address in the data buffer. So I have two questions:
  • Is `esp_lcd` component "recommended" / "supported"?
  • If it is, what are the rules to convert cmd+addr based transaction to an equivalent buffer-only transaction?
Bootnote: original lilygo driver code taken from their examples here https://github.com/Xinyuan-LilyGO/T-Dis ... n/examples

crosser
Posts: 3
Joined: Sun Sep 29, 2024 7:42 pm

Re: Status of esp_lcd component

Postby crosser » Mon Sep 30, 2024 9:46 pm

I figured it out. Module uses DC-less 16bit transfer mode; and the "command code" which is the same as the "address" is 16bit wide. So without DC pin the "command" expands to 32 bits (four bytes: control byte, hi byte of address, control byte, lo byte of the address).
Pattern modelled after the existing code does not exactly match the datasheet, but hey, I have pretty colours!

So this is what I had to do:

Code: Select all

                        .lcd_cmd_bits = 32,
                        .lcd_param_bits = 8,
and pass 32bit integer as the command to the transactions, patterned as `0x0200xx00`, where xx is the usual LCD command code.

crosser
Posts: 3
Joined: Sun Sep 29, 2024 7:42 pm

Re: Status of esp_lcd component

Postby crosser » Wed Oct 02, 2024 8:09 pm

And just in case somebody needs it, I've put a working example, including the driver, here: https://github.com/crosser/lvgl_esp_lcd

dmitrij999
Posts: 78
Joined: Sat Mar 02, 2019 8:06 pm

Re: Status of esp_lcd component

Postby dmitrij999 » Fri Dec 20, 2024 10:28 pm

Hello!

I'm trying to re-use your component esp_lcd_panel_rm67162 to setup the panel using esp_lvgl_port.
It's needed for me for 2 main goals:
- attach the touch panel to LVGL as well
- make the project the easy-to-integrate the UI from SquareLine Studio

But once the code goes to lvgl_port_add_disp(), the guru meditation error occurs and I have no idea why.

The code:

Code: Select all

#include <stdbool.h>
#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <driver/gpio.h>
#include <driver/spi_master.h>
#include <esp_lcd_types.h>
#include <esp_lcd_panel_io.h>
#include <esp_lcd_panel_vendor.h>
#include <esp_lcd_panel_ops.h>
#include <esp_heap_caps.h>
#include <esp_err.h>
#include <esp_log.h>
#include <esp_timer.h>
#include <driver/i2c.h>
#include "esp_lcd_panel_rm67162.h"
#include "sdkconfig.h"
#include "lvgl.h"
#include "esp_lcd_touch_cst816s.h"
#include "ui/ui.h"
#include "esp_lvgl_port.h"
#include "esp_check.h"
#include <driver/i2c_master.h>

#define TAG "lvgl_esplcd"

#if defined(CONFIG_HWE_DISPLAY_SPI1_HOST)
# define SPIx_HOST SPI1_HOST
#elif defined(CONFIG_HWE_DISPLAY_SPI2_HOST)
# define SPIx_HOST SPI2_HOST
#else
# error "SPI host 1 or 2 must be selected"
#endif

#if defined(CONFIG_HWE_DISPLAY_SPI_MODE0)
# define SPI_MODEx (0)
#elif defined(CONFIG_HWE_DISPLAY_SPI_MODE3)
# define SPI_MODEx (2)
#else
# error "SPI MODE0 or MODE3 must be selected"
#endif

#define SEND_BUF_SIZE ((CONFIG_HWE_DISPLAY_WIDTH * CONFIG_HWE_DISPLAY_HEIGHT \
                * LV_COLOR_FORMAT_GET_SIZE(LV_COLOR_FORMAT_RGB565)) / 10)

#define LV_TICK_PERIOD_MS 1


/* LCD settings */
#define APP_LCD_LVGL_FULL_REFRESH           (0)
#define APP_LCD_LVGL_DIRECT_MODE            (1)
#define APP_LCD_LVGL_AVOID_TEAR             (1)
#define APP_LCD_RGB_BOUNCE_BUFFER_MODE      (1)
#define APP_LCD_DRAW_BUFF_DOUBLE            (0)
#define APP_LCD_DRAW_BUFF_HEIGHT            (100)
#define APP_LCD_RGB_BUFFER_NUMS             (2)
#define APP_LCD_RGB_BOUNCE_BUFFER_HEIGHT    (10)

#define EXAMPLE_LCD_COLOR_SPACE     (ESP_LCD_COLOR_SPACE_BGR)
#define EXAMPLE_LCD_BITS_PER_PIXEL  (16)
#define EXAMPLE_LCD_DRAW_BUFF_DOUBLE (1)
#define EXAMPLE_LCD_DRAW_BUFF_HEIGHT (50)
#define EXAMPLE_LCD_BL_ON_LEVEL     (1)


#define I2C_MASTER_FREQ_HZ          400000                     /*!< I2C master clock frequency */

SemaphoreHandle_t xGuiSemaphore;

// static SemaphoreHandle_t touch_mux;
// esp_lcd_panel_io_handle_t io_handle;
// esp_lcd_panel_io_handle_t io_touch_handle;
// esp_lcd_touch_handle_t tp;

// static lv_indev_t *lvgl_touch_indev = NULL;

static esp_lcd_panel_handle_t lcd_panel = NULL;

static i2c_master_bus_handle_t my_bus = NULL;
static esp_lcd_panel_io_handle_t touch_io_handle = NULL;
static esp_lcd_touch_handle_t touch_handle = NULL;

esp_lcd_panel_io_handle_t io_handle = NULL;

/* LVGL display and touch */
static lv_display_t * lvgl_disp = NULL;
static lv_indev_t * lvgl_touch_indev = NULL;

extern void example_lvgl_demo_ui(lv_display_t *disp);

static bool IRAM_ATTR color_trans_done(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
{
	lv_display_t *disp = (lv_display_t*)user_ctx;
	lv_display_flush_ready(disp);
	// Whether a high priority task has been waken up by this function
	return false; 
}

static void lv_tick_task(void *arg) {
	lv_tick_inc(LV_TICK_PERIOD_MS);
}

static void disp_flush(lv_display_t *disp_drv, const lv_area_t *area,
		uint8_t *px_map)
{
	esp_lcd_panel_handle_t panel_handle =
		(esp_lcd_panel_handle_t)lv_display_get_user_data(disp_drv);
	ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle,
			area->x1, area->y1,
			area->x2 + 1, area->y2 + 1,
			(uint16_t *) px_map));
}


static esp_err_t app_amoled_init(esp_lcd_panel_handle_t *lp, esp_lcd_panel_io_handle_t * io_handle)
{
    esp_err_t ret = ESP_OK;

	ESP_LOGI(TAG, "Power up AMOLED");
	ESP_ERROR_CHECK(gpio_set_direction(CONFIG_HWE_DISPLAY_PWR,
				GPIO_MODE_OUTPUT));
	ESP_ERROR_CHECK(gpio_set_level(CONFIG_HWE_DISPLAY_PWR,
				CONFIG_HWE_DISPLAY_PWR_ON_LEVEL));
	vTaskDelay(pdMS_TO_TICKS(500));

	ESP_LOGI(TAG, "Initialize SPI bus");
	ESP_ERROR_CHECK(spi_bus_initialize(SPIx_HOST,
		& (spi_bus_config_t) {
			.data0_io_num = CONFIG_HWE_DISPLAY_SPI_D0,
			.data1_io_num = CONFIG_HWE_DISPLAY_SPI_D1,
			.sclk_io_num = CONFIG_HWE_DISPLAY_SPI_SCK,
			.data2_io_num = CONFIG_HWE_DISPLAY_SPI_D2,
			.data3_io_num = CONFIG_HWE_DISPLAY_SPI_D3,
			.max_transfer_sz = SEND_BUF_SIZE + 8,
			.flags = SPICOMMON_BUSFLAG_MASTER
				| SPICOMMON_BUSFLAG_GPIO_PINS
				| SPICOMMON_BUSFLAG_QUAD,
		},
		SPI_DMA_CH_AUTO
	));
	ESP_LOGI(TAG, "Attach panel IO handle to SPI");
	
	ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(
		(esp_lcd_spi_bus_handle_t)SPIx_HOST,
	       	& (esp_lcd_panel_io_spi_config_t) {
			    .cs_gpio_num = CONFIG_HWE_DISPLAY_SPI_CS,
			    .pclk_hz = CONFIG_HWE_DISPLAY_SPI_FREQUENCY,
			    .lcd_cmd_bits = 32,
			    .lcd_param_bits = 8,
#if defined(CONFIG_HWE_DISPLAY_SPI_SPI)
			    .spi_mode = 0,
#elif defined(CONFIG_HWE_DISPLAY_SPI_QSPI)
			    .spi_mode = 0,
			    .flags.quad_mode = 1,
#elif defined(CONFIG_HWE_DISPLAY_SPI_OSPI)
			    .spi_mode = 3,
			    .flags.octal_mode = 1,
#else
# error "SPI single, quad and octal modes are supported"
#endif
			    .trans_queue_depth = 17,
		    },
	        io_handle
	));

    assert(io_handle != NULL);

	ESP_LOGI(TAG, "Attach vendor specific module");

    esp_lcd_panel_dev_config_t panel_config =  {
        .reset_gpio_num = CONFIG_HWE_DISPLAY_RST,
        .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
        .bits_per_pixel = 16,
    };
    

	ESP_ERROR_CHECK(esp_lcd_new_panel_rm67162(
		*io_handle,
		&panel_config,
		lp
	));

	assert(lp != NULL);

	ESP_LOGI(TAG, "Init panel");
	ESP_GOTO_ON_ERROR(esp_lcd_panel_init(*lp), err, TAG, "Init panel failed");

	ESP_LOGI(TAG, "Reset panel");
	ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(*lp), err, TAG, "Cannot reset AMOLED panel");

	ESP_LOGI(TAG, "Turn on the screen");
	ESP_GOTO_ON_ERROR(esp_lcd_panel_disp_on_off(*lp, true), err, TAG, "Cannot turn on display");
	
	ESP_GOTO_ON_ERROR(esp_lcd_panel_invert_color(*lp, true), err, TAG, "Cannot invert panel colors");
	// Rotate 90 degrees clockwise:
	ESP_GOTO_ON_ERROR(esp_lcd_panel_swap_xy(*lp, true), err, TAG, "Cannot swap xy");
	ESP_GOTO_ON_ERROR(esp_lcd_panel_mirror(*lp, true, false), err, TAG, "Cannot mirror the panel");
	
	ESP_LOGI(TAG, "Turn on backlight");
	ESP_ERROR_CHECK(gpio_set_level(CONFIG_HWE_DISPLAY_PWR,
				CONFIG_HWE_DISPLAY_PWR_ON_LEVEL));

	// ESP_ERROR_CHECK(esp_lcd_panel_io_register_event_callbacks(
	// 	io_handle,
	// 	&(esp_lcd_panel_io_callbacks_t) {
	// 		color_trans_done
	// 	},
	//        	disp));

err:
    if (*lp)
    {
        esp_lcd_panel_del(*lp);
    }
    return ret;

}


static esp_err_t app_touch_init(i2c_master_bus_handle_t *bus,
                                esp_lcd_panel_io_handle_t *tp_io,
                                esp_lcd_touch_handle_t *tp)
{
    if (!*bus)
    {
        ESP_LOGI(TAG, "creating i2c master bus");
        const i2c_master_bus_config_t i2c_conf = {
            .i2c_port = -1,
            .sda_io_num = CONFIG_LCD_TOUCH_SDA,
            .scl_io_num = CONFIG_LCD_TOUCH_SCL,
            .clk_source = I2C_CLK_SRC_DEFAULT,
            .glitch_ignore_cnt = 7,
            .flags.enable_internal_pullup = 1,
        };
        ESP_RETURN_ON_ERROR(i2c_new_master_bus(&i2c_conf, bus),
                            TAG, "failed to create i2c master bus");
    }

    if (!*tp_io)
    {
        ESP_LOGI(TAG, "creating touch panel io");
        esp_lcd_panel_io_i2c_config_t tp_io_cfg =
            ESP_LCD_TOUCH_IO_I2C_CST816S_CONFIG();
        tp_io_cfg.scl_speed_hz = I2C_MASTER_FREQ_HZ;
        ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c_v2(*bus, &tp_io_cfg, tp_io),
                            TAG, "Failed to crate touch panel io");
    }

    const esp_lcd_touch_config_t tp_cfg = {
        .x_max = CONFIG_HWE_DISPLAY_WIDTH,
        .y_max = CONFIG_HWE_DISPLAY_HEIGHT,
        .rst_gpio_num = GPIO_NUM_NC,
        .int_gpio_num = CONFIG_LCD_TOUCH_INT,
    };

    return esp_lcd_touch_new_i2c_cst816s(*tp_io, &tp_cfg, tp);
}

static esp_err_t app_lvgl_init(esp_lcd_panel_handle_t lp, esp_lcd_panel_io_handle_t * io_handle, esp_lcd_touch_handle_t tp,
                               lv_display_t **lv_disp, lv_indev_t **lv_touch_indev) {
    ESP_LOGI(TAG, "Initialize LVGL");
    const lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
    ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_cfg), TAG, "LVGL port initialization failed");

    const lvgl_port_display_cfg_t disp_cfg = {
        .io_handle = *io_handle,
        .panel_handle = lp,
        .buffer_size = SEND_BUF_SIZE,
        .double_buffer = true,
        .hres = CONFIG_HWE_DISPLAY_WIDTH,
        .vres = CONFIG_HWE_DISPLAY_HEIGHT,
        // .monochrome = true,
#if LVGL_VERSION_MAJOR >= 9
        .color_format = LV_COLOR_FORMAT_RGB565,
#endif
        .rotation = {
            .swap_xy = true,
            .mirror_x = true,
            .mirror_y = false,
        },
        .flags = {
#if LVGL_VERSION_MAJOR >= 9
            .swap_bytes = true,
#endif
            .sw_rotate = false,
        }
    };

    *lv_disp = lvgl_port_add_disp(&disp_cfg);

    return ESP_OK;
}


void app_main(void)
{
	// ESP_LOGI(TAG, "Launching gui task");
	/* Pinned to core 1. Core 0 will run bluetooth/wifi jobs. */
	// xTaskCreatePinnedToCore(gui_task, "gui", 4096*2, NULL, 0, NULL, 1);
	// xTaskCreatePinnedToCore(touch_task, "touch", 4096, NULL, 0, NULL, 1);

	esp_err_t ret = ESP_OK;

    xGuiSemaphore = xSemaphoreCreateMutex();

    ESP_ERROR_CHECK(app_amoled_init(&lcd_panel, &io_handle));
	assert(lcd_panel != NULL);
	assert(io_handle != NULL);
    ESP_ERROR_CHECK(app_touch_init(&my_bus, &touch_io_handle, &touch_handle));
	assert(touch_handle != NULL);
	assert(touch_io_handle != NULL);
    ESP_ERROR_CHECK(app_lvgl_init(lcd_panel, &io_handle, touch_handle, &lvgl_disp, &lvgl_touch_indev));

}

Logs:

Code: Select all

SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce2820,len:0x1918
load:0x403c8700,len:0x4
load:0x403c8704,len:0xe5c
load:0x403cb700,len:0x303c
entry 0x403c893c
I (26) boot: ESP-IDF v5.3.1 2nd stage bootloader
I (27) boot: compile time Dec 21 2024 00:07:23
I (27) boot: Multicore bootloader
I (30) boot: chip revision: v0.2
I (34) qio_mode: Enabling QIO for flash chip WinBond
I (39) boot.esp32s3: Boot SPI Speed : 80MHz
I (44) boot.esp32s3: SPI Mode       : QIO
I (49) boot.esp32s3: SPI Flash Size : 16MB
I (54) boot: Enabling RNG early entropy source...
I (59) boot: Partition Table:
I (63) boot: ## Label            Usage          Type ST Offset   Length
I (70) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (77) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (85) boot:  2 factory          factory app      00 00 00010000 00100000
I (92) boot: End of partition table
I (96) esp_image: segment 0: paddr=00010020 vaddr=3c060020 size=1b288h (111240) map
I (122) esp_image: segment 1: paddr=0002b2b0 vaddr=3fc95d00 size=02f30h ( 12080) load
I (124) esp_image: segment 2: paddr=0002e1e8 vaddr=40374000 size=01e30h (  7728) load
I (129) esp_image: segment 3: paddr=00030020 vaddr=42000020 size=56b5ch (355164) map
I (190) esp_image: segment 4: paddr=00086b84 vaddr=40375e30 size=0fe78h ( 65144) load
I (210) boot: Loaded app from partition at offset 0x10000
I (210) boot: Disabling RNG early entropy source...
I (222) octal_psram: vendor id    : 0x0d (AP)
I (222) octal_psram: dev id       : 0x02 (generation 3)
I (223) octal_psram: density      : 0x03 (64 Mbit)
I (227) octal_psram: good-die     : 0x01 (Pass)
I (232) octal_psram: Latency      : 0x01 (Fixed)
I (238) octal_psram: VCC          : 0x01 (3V)
I (243) octal_psram: SRF          : 0x01 (Fast Refresh)
I (249) octal_psram: BurstType    : 0x01 (Hybrid Wrap)
I (255) octal_psram: BurstLen     : 0x01 (32 Byte)
I (260) octal_psram: Readlatency  : 0x02 (10 cycles@Fixed)
I (266) octal_psram: DriveStrength: 0x00 (1/1)
I (271) esp_psram: Found 8MB PSRAM device
I (276) esp_psram: Speed: 40MHz
I (280) cpu_start: Multicore app
I (1015) esp_psram: SPI SRAM memory test OK
I (1023) cpu_start: Pro cpu start user code
I (1023) cpu_start: cpu freq: 240000000 Hz
I (1024) app_init: Application information:
I (1027) app_init: Project name:     lvgl_esplcd
I (1032) app_init: App version:      b115599-dirty
I (1037) app_init: Compile time:     Dec 21 2024 00:06:38
I (1043) app_init: ELF file SHA256:  bb04b6940...
I (1049) app_init: ESP-IDF:          v5.3.1
I (1054) efuse_init: Min chip rev:     v0.0
I (1059) efuse_init: Max chip rev:     v0.99 
I (1064) efuse_init: Chip rev:         v0.2
I (1068) heap_init: Initializing. RAM available for dynamic allocation:
I (1076) heap_init: At 3FCA9790 len 0003FF80 (255 KiB): RAM
I (1082) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM
I (1088) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
I (1094) heap_init: At 600FE100 len 00001EE8 (7 KiB): RTCRAM
I (1101) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator
I (1109) spi_flash: detected chip: winbond
I (1113) spi_flash: flash io: qio
I (1117) sleep: Configure to isolate all GPIO pins in sleep state
I (1124) sleep: Enable automatic switching of GPIO sleep configuration
I (1131) main_task: Started on CPU0
I (1141) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations
I (1141) main_task: Calling app_main()
I (1151) lvgl_esplcd: Power up AMOLED
I (1651) lvgl_esplcd: Initialize SPI bus
I (1651) lvgl_esplcd: Attach panel IO handle to SPI
I (1651) gpio: GPIO[0]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 
I (1651) lvgl_esplcd: Attach vendor specific module
I (1661) lvgl_esplcd: Init panel
I (1901) lvgl_esplcd: Reset panel
I (2401) lvgl_esplcd: Turn on the screen
I (2401) lvgl_esplcd: Turn on backlight
I (2401) gpio: GPIO[17]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 
I (2401) lvgl_esplcd: creating i2c master bus
I (2411) gpio: GPIO[3]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 
I (2421) gpio: GPIO[2]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 
I (2431) lvgl_esplcd: creating touch panel io
I (2431) gpio: GPIO[21]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:2 
I (2441) CST816S: IC id: 181
I (2441) lvgl_esplcd: Initialize LVGL
I (2451) LVGL: Starting LVGL task
Guru Meditation Error: Core  0 panic'ed (InstructionFetchError). Exception was unhandled.

Core  0 register dump:
PC      : 0x3fcb6d70  PS      : 0x00060430  A0      : 0x8200b236  A1      : 0x3fcacd80  
A2      : 0x3fcb6d58  A3      : 0x00000001  A4      : 0x3fc9a2f4  A5      : 0x3fcb8cc0  
A6      : 0x00000000  A7      : 0x000000a0  A8      : 0x8203bc7c  A9      : 0x3fcacd30  
A10     : 0x3fcb6d58  A11     : 0x00000001  A12     : 0x00000035  A13     : 0x3fcb8cc0  
A14     : 0x000001e0  A15     : 0x3c093d18  SAR     : 0x00000008  EXCCAUSE: 0x00000002  
EXCVADDR: 0x3fcb6d70  LBEG    : 0x400570e8  LEND    : 0x400570f3  LCOUNT  : 0x00000000  
0x400570e8: memset in ROM
0x400570f3: memset in ROM



Backtrace: 0x3fcb6d6d:0x3fcacd80 |<-CORRUPTED




ELF file SHA256: bb04b6940

Rebooting...
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0xc (RTC_SW_CPU_RST),boot:0x29 (SPI_FAST_FLASH_BOOT)
Saved PC:0x40375c14
0x40375c14: esp_restart_noos at /home/dmitrij999/esp/esp-idf-v5.3.1/components/esp_system/port/soc/esp32s3/system_internal.c:158

SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce2820,len:0x1918
load:0x403c8700,len:0x4
load:0x403c8704,len:0xe5c
load:0x403cb700,len:0x303c
entry 0x403c893c

dmitrij999
Posts: 78
Joined: Sat Mar 02, 2019 8:06 pm

Re: Status of esp_lcd component

Postby dmitrij999 » Fri Dec 20, 2024 11:49 pm

I figured out why guru meditation occured

I forgot to do return before err label in app_amoled_init(), that's why lcd_panel handler was creating then destroying, and LVGL saw incorrect link.

For now I'm figuring out with LVGL interface and displaying itself. Now init procedures goes OK and LVGL interface initiates but I see no picture on the screen.

dmitrij999
Posts: 78
Joined: Sat Mar 02, 2019 8:06 pm

Re: Status of esp_lcd component

Postby dmitrij999 » Sat Dec 21, 2024 10:33 am

UPD: I managed to show the LVGL interface, but there are 2 things:
- colors are being displayed incorrectly
- when I'm trying to interact with interface thru touch, I can see that render time comes to 0 ms, fells like interface is freezing, but there is no switch reaction

Code:

Code: Select all

#include <stdbool.h>
#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <driver/gpio.h>
#include <driver/spi_master.h>
#include <esp_lcd_types.h>
#include <esp_lcd_panel_io.h>
#include <esp_lcd_panel_vendor.h>
#include <esp_lcd_panel_ops.h>
#include <esp_heap_caps.h>
#include <esp_err.h>
#include <esp_log.h>
#include <esp_timer.h>
#include <driver/i2c.h>
#include "esp_lcd_panel_rm67162.h"
#include "sdkconfig.h"
#include "lvgl.h"
#include "esp_lcd_touch_cst816s.h"
#include "ui/ui.h"
#include "esp_lvgl_port.h"
#include "esp_check.h"
#include <driver/i2c_master.h>

#define TAG "lvgl_esplcd"

#if defined(CONFIG_HWE_DISPLAY_SPI1_HOST)
# define SPIx_HOST SPI1_HOST
#elif defined(CONFIG_HWE_DISPLAY_SPI2_HOST)
# define SPIx_HOST SPI2_HOST
#else
# error "SPI host 1 or 2 must be selected"
#endif

#if defined(CONFIG_HWE_DISPLAY_SPI_MODE0)
# define SPI_MODEx (0)
#elif defined(CONFIG_HWE_DISPLAY_SPI_MODE3)
# define SPI_MODEx (2)
#else
# error "SPI MODE0 or MODE3 must be selected"
#endif

#define SEND_BUF_SIZE ((CONFIG_HWE_DISPLAY_WIDTH * CONFIG_HWE_DISPLAY_HEIGHT \
                * LV_COLOR_FORMAT_GET_SIZE(LV_COLOR_FORMAT_RGB565)) / 10)

#define LV_TICK_PERIOD_MS 1


/* LCD settings */
#define APP_LCD_LVGL_FULL_REFRESH           (0)
#define APP_LCD_LVGL_DIRECT_MODE            (1)
#define APP_LCD_LVGL_AVOID_TEAR             (1)
#define APP_LCD_RGB_BOUNCE_BUFFER_MODE      (1)
#define APP_LCD_DRAW_BUFF_DOUBLE            (0)
#define APP_LCD_DRAW_BUFF_HEIGHT            (100)
#define APP_LCD_RGB_BUFFER_NUMS             (2)
#define APP_LCD_RGB_BOUNCE_BUFFER_HEIGHT    (10)

#define EXAMPLE_LCD_COLOR_SPACE     (ESP_LCD_COLOR_SPACE_BGR)
#define EXAMPLE_LCD_BITS_PER_PIXEL  (16)
#define EXAMPLE_LCD_DRAW_BUFF_DOUBLE (1)
#define EXAMPLE_LCD_DRAW_BUFF_HEIGHT (50)
#define EXAMPLE_LCD_BL_ON_LEVEL     (1)


#define I2C_MASTER_FREQ_HZ          400000                     /*!< I2C master clock frequency */

SemaphoreHandle_t xGuiSemaphore;

// static SemaphoreHandle_t touch_mux;
// esp_lcd_panel_io_handle_t io_handle;
// esp_lcd_panel_io_handle_t io_touch_handle;
// esp_lcd_touch_handle_t tp;

// static lv_indev_t *lvgl_touch_indev = NULL;

static esp_lcd_panel_handle_t lcd_panel = NULL;

static i2c_master_bus_handle_t my_bus = NULL;
static esp_lcd_panel_io_handle_t touch_io_handle = NULL;
static esp_lcd_touch_handle_t touch_handle = NULL;

esp_lcd_panel_io_handle_t io_handle = NULL;

/* LVGL display and touch */
static lv_display_t * lvgl_disp = NULL;
static lv_indev_t * lvgl_touch_indev = NULL;

extern void example_lvgl_demo_ui(lv_display_t *disp);

static bool IRAM_ATTR color_trans_done(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
{
	lv_display_t *disp = (lv_display_t*)user_ctx;
	lv_display_flush_ready(disp);
	// Whether a high priority task has been waken up by this function
	return false; 
}

void process_coordinates(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num) {
    // ESP_LOGI(TAG, "Touch detected at %u - (%u,%u)", (*point_num), (*x), (*y));
}

// static void lv_tick_task(void *arg) {
// 	lv_tick_inc(LV_TICK_PERIOD_MS);
// }

static void disp_flush(lv_display_t *disp_drv, const lv_area_t *area,
		uint8_t *px_map)
{
	esp_lcd_panel_handle_t panel_handle =
		(esp_lcd_panel_handle_t)lv_display_get_user_data(disp_drv);
	ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(panel_handle,
			area->x1, area->y1,
			area->x2 + 1, area->y2 + 1,
			(uint16_t *) px_map));
}


static esp_err_t app_amoled_init(esp_lcd_panel_handle_t *lp, esp_lcd_panel_io_handle_t * io_handle)
{
    esp_err_t ret = ESP_OK;

	ESP_LOGI(TAG, "Power up AMOLED");
	ESP_ERROR_CHECK(gpio_set_direction(CONFIG_HWE_DISPLAY_PWR,
				GPIO_MODE_OUTPUT));
	ESP_ERROR_CHECK(gpio_set_level(CONFIG_HWE_DISPLAY_PWR,
				CONFIG_HWE_DISPLAY_PWR_ON_LEVEL));
	vTaskDelay(pdMS_TO_TICKS(500));

	ESP_LOGI(TAG, "Initialize SPI bus");
	ESP_ERROR_CHECK(spi_bus_initialize(SPIx_HOST,
		& (spi_bus_config_t) {
			.data0_io_num = CONFIG_HWE_DISPLAY_SPI_D0,
			.data1_io_num = CONFIG_HWE_DISPLAY_SPI_D1,
			.sclk_io_num = CONFIG_HWE_DISPLAY_SPI_SCK,
			.data2_io_num = CONFIG_HWE_DISPLAY_SPI_D2,
			.data3_io_num = CONFIG_HWE_DISPLAY_SPI_D3,
			.max_transfer_sz = SEND_BUF_SIZE + 8,
			.flags = SPICOMMON_BUSFLAG_MASTER
				| SPICOMMON_BUSFLAG_GPIO_PINS
				| SPICOMMON_BUSFLAG_QUAD,
		},
		SPI_DMA_CH_AUTO
	));
	ESP_LOGI(TAG, "Attach panel IO handle to SPI");
	
	ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(
		(esp_lcd_spi_bus_handle_t)SPIx_HOST,
	       	& (esp_lcd_panel_io_spi_config_t) {
			    .cs_gpio_num = CONFIG_HWE_DISPLAY_SPI_CS,
			    .pclk_hz = CONFIG_HWE_DISPLAY_SPI_FREQUENCY,
			    .lcd_cmd_bits = 32,
			    .lcd_param_bits = 8,
#if defined(CONFIG_HWE_DISPLAY_SPI_SPI)
			    .spi_mode = 0,
#elif defined(CONFIG_HWE_DISPLAY_SPI_QSPI)
			    .spi_mode = 0,
			    .flags.quad_mode = 1,
#elif defined(CONFIG_HWE_DISPLAY_SPI_OSPI)
			    .spi_mode = 3,
			    .flags.octal_mode = 1,
#else
# error "SPI single, quad and octal modes are supported"
#endif
			    .trans_queue_depth = 17,
		    },
	        io_handle
	));

    assert(io_handle != NULL);

	ESP_LOGI(TAG, "Attach vendor specific module");

    esp_lcd_panel_dev_config_t panel_config =  {
        .reset_gpio_num = CONFIG_HWE_DISPLAY_RST,
        .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
        .bits_per_pixel = 16,
    };
    

	ESP_ERROR_CHECK(esp_lcd_new_panel_rm67162(
		*io_handle,
		&panel_config,
		lp
	));

	assert(lp != NULL);

	ESP_LOGI(TAG, "Reset panel");
	ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(*lp), err, TAG, "Cannot reset AMOLED panel");

	ESP_LOGI(TAG, "Init panel");
	ESP_GOTO_ON_ERROR(esp_lcd_panel_init(*lp), err, TAG, "Init panel failed");


	ESP_LOGI(TAG, "Turn on the screen");
	ESP_GOTO_ON_ERROR(esp_lcd_panel_disp_on_off(*lp, true), err, TAG, "Cannot turn on display");
	
	// ESP_GOTO_ON_ERROR(esp_lcd_panel_invert_color(*lp, true), err, TAG, "Cannot invert panel colors");
	// Rotate 90 degrees clockwise:
	ESP_GOTO_ON_ERROR(esp_lcd_panel_swap_xy(*lp, true), err, TAG, "Cannot swap xy");
	ESP_GOTO_ON_ERROR(esp_lcd_panel_mirror(*lp, true, false), err, TAG, "Cannot mirror the panel");
	
	ESP_LOGI(TAG, "Turn on backlight");
	ESP_ERROR_CHECK(gpio_set_level(CONFIG_HWE_DISPLAY_PWR,
				CONFIG_HWE_DISPLAY_PWR_ON_LEVEL));

	// ESP_ERROR_CHECK(esp_lcd_panel_io_register_event_callbacks(
	// 	io_handle,
	// 	&(esp_lcd_panel_io_callbacks_t) {
	// 		color_trans_done
	// 	},
	//        	disp));

	return ret;

err:
    if (*lp)
    {
        esp_lcd_panel_del(*lp);
    }
    return ret;

}


static esp_err_t app_touch_init(i2c_master_bus_handle_t *bus,
                                esp_lcd_panel_io_handle_t *tp_io,
                                esp_lcd_touch_handle_t *tp)
{
    if (!*bus)
    {
        ESP_LOGI(TAG, "creating i2c master bus");
        const i2c_master_bus_config_t i2c_conf = {
            .i2c_port = -1,
            .sda_io_num = CONFIG_LCD_TOUCH_SDA,
            .scl_io_num = CONFIG_LCD_TOUCH_SCL,
            .clk_source = I2C_CLK_SRC_DEFAULT,
            .glitch_ignore_cnt = 7,
            .flags.enable_internal_pullup = 1,
        };
        ESP_RETURN_ON_ERROR(i2c_new_master_bus(&i2c_conf, bus),
                            TAG, "failed to create i2c master bus");
    }

    if (!*tp_io)
    {
        ESP_LOGI(TAG, "creating touch panel io");
        esp_lcd_panel_io_i2c_config_t tp_io_cfg =
            ESP_LCD_TOUCH_IO_I2C_CST816S_CONFIG();
        tp_io_cfg.scl_speed_hz = I2C_MASTER_FREQ_HZ;
        ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c_v2(*bus, &tp_io_cfg, tp_io),
                            TAG, "Failed to crate touch panel io");
    }

    const esp_lcd_touch_config_t tp_cfg = {
        .x_max = CONFIG_HWE_DISPLAY_WIDTH,
        .y_max = CONFIG_HWE_DISPLAY_HEIGHT,
        .rst_gpio_num = GPIO_NUM_NC,
        .int_gpio_num = CONFIG_LCD_TOUCH_INT,
        .flags = {
            .swap_xy = true,
            .mirror_x = true,
            .mirror_y = false,
        },
        .process_coordinates = process_coordinates,
    };

    return esp_lcd_touch_new_i2c_cst816s(*tp_io, &tp_cfg, tp);
}

static esp_err_t app_lvgl_init(esp_lcd_panel_handle_t *lp, esp_lcd_panel_io_handle_t *io_handle, esp_lcd_touch_handle_t *tp,
                               lv_display_t **lv_disp, lv_indev_t **lv_touch_indev) {
    ESP_LOGI(TAG, "Initialize LVGL");
    const lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
    ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_cfg), TAG, "LVGL port initialization failed");

    const lvgl_port_display_cfg_t disp_cfg = {
        .io_handle = *io_handle,
        .panel_handle = *lp,
        .buffer_size = SEND_BUF_SIZE,
        .double_buffer = true,
        .hres = CONFIG_HWE_DISPLAY_WIDTH,
        .vres = CONFIG_HWE_DISPLAY_HEIGHT,
        .monochrome = false,
#if LVGL_VERSION_MAJOR >= 9
        .color_format = LV_COLOR_FORMAT_RGB565,
#endif
        // .rotation = {
        //     .swap_xy = true,
        //     .mirror_x = true,
        //     .mirror_y = false,
        // },
        .flags = {
			.buff_spiram = true,
#if LVGL_VERSION_MAJOR >= 9
            .swap_bytes = false,
#endif
            .sw_rotate = true,
            // .direct_mode = true,
        }
    };

	
    *lv_disp = lvgl_port_add_disp(&disp_cfg);
    // lvgl_disp = lvgl_port_add_disp(&disp_cfg);
	// lvgl_port_lock(0);
	// esp_lcd_panel_io_register_event_callbacks(
	// 	*io_handle,
	// 	&(esp_lcd_panel_io_callbacks_t) {
	// 		color_trans_done
	// 	},
	//        	*lv_disp);
	lv_display_set_user_data(*lv_disp, *lp);
	lv_display_set_flush_cb(*lv_disp, disp_flush);
    // static lv_color_t *buf[2];
	// for (int i = 0; i < 2; i++) {
	// 	buf[i] = heap_caps_malloc(SEND_BUF_SIZE, MALLOC_CAP_DMA);
	// 	assert(buf[i] != NULL);
	// }
	// lv_display_set_buffers(*lv_disp, buf[0], buf[1], SEND_BUF_SIZE,
	// 		LV_DISPLAY_RENDER_MODE_PARTIAL);
    lv_disp_set_rotation(*lv_disp, LV_DISPLAY_ROTATION_90);
	
	
    /* Add touch input (for selected screen) */
    const lvgl_port_touch_cfg_t touch_cfg = {
        .disp = *lv_disp,
        .handle = *tp,
    };
    *lv_touch_indev = lvgl_port_add_touch(&touch_cfg);
    assert(*lv_touch_indev != NULL);
    lv_indev_enable(*lv_touch_indev, true);

    return ESP_OK;
}


void app_main(void)
{
	// ESP_LOGI(TAG, "Launching gui task");
	/* Pinned to core 1. Core 0 will run bluetooth/wifi jobs. */
	// xTaskCreatePinnedToCore(gui_task, "gui", 4096*2, NULL, 0, NULL, 1);
	// xTaskCreatePinnedToCore(touch_task, "touch", 4096, NULL, 0, NULL, 1);

	esp_err_t ret = ESP_OK;

    xGuiSemaphore = xSemaphoreCreateMutex();

    ESP_ERROR_CHECK(app_amoled_init(&lcd_panel, &io_handle));
	assert(lcd_panel != NULL);
	assert(io_handle != NULL);
    ESP_ERROR_CHECK(app_touch_init(&my_bus, &touch_io_handle, &touch_handle));
	assert(touch_handle != NULL);
	assert(touch_io_handle != NULL);
    ESP_ERROR_CHECK(app_lvgl_init(&lcd_panel, &io_handle, &touch_handle, &lvgl_disp, &lvgl_touch_indev));

	lvgl_port_lock(0);
	ui_init();
	lvgl_port_unlock();
}
The behaviour is on video: https://disk.yandex.ru/i/O65Q1Sw_doC5TA

P.S. I am familiar with color swap code stub, but I'd like to have it executed at LVGL-level to be able to show pictures

Code: Select all

/* Create a pseudo lv_color_t that will produce byte-swapped r5g6b5 */
static lv_color_t c_swap(lv_color_t o)
{
    /*
       RRRrr... GGGggg.. BBbbb...
        ___  __ __  ___
       /   \/     \/   \
       RRRrrGGG gggBBbbb
               X
       gggBBbbb RRRrrGGG
       \___/\__ __/\___/
       r     g-     b---
       |       \        \
       gggBB000 bbbRRR00 rrGGG000
     */
    return (lv_color_t) {
	.red =   ((o.green << 3) & 0b11100000) | ((o.blue >> 3)  & 0b00011000),
	.green = ((o.blue << 2)  & 0b11100000) | ((o.red >> 3)   & 0b00011100),
	.blue =  ((o.red << 3)   & 0b11000000) | ((o.green >> 2) & 0b00111000)
    };
}
UPD2: I changed the color mode from RGB565 to RGB888 and changed color order from RGB to BGR, and colors became correct.
But I still cannot beat the touch events to make it work correctly

dmitrij999
Posts: 78
Joined: Sat Mar 02, 2019 8:06 pm

Re: Status of esp_lcd component

Postby dmitrij999 » Sat Dec 21, 2024 9:00 pm

Finally beat the touch handling :D

Using LVGL indev logs, I found out that touch panel provided wrong coordinates.
And I needed to pick parameters for touch panel for exact mode.
For position as on picture below, the touch config is that:

Code: Select all

    const esp_lcd_touch_config_t tp_cfg = {
        .x_max = CONFIG_HWE_DISPLAY_HEIGHT,
        .y_max = CONFIG_HWE_DISPLAY_WIDTH,
        // .x_max = CONFIG_HWE_DISPLAY_WIDTH,
        // .y_max = CONFIG_HWE_DISPLAY_HEIGHT,
        .rst_gpio_num = GPIO_NUM_NC,
        .int_gpio_num = CONFIG_LCD_TOUCH_INT,
        .flags = {
            .swap_xy = true,
            .mirror_x = false,
            .mirror_y = true,
        },
        .process_coordinates = process_coordinates,
    };
But If I do vice versa, like in code below, it won't work correctly

Code: Select all

    const esp_lcd_touch_config_t tp_cfg = {
        // .x_max = CONFIG_HWE_DISPLAY_HEIGHT,
        // .y_max = CONFIG_HWE_DISPLAY_WIDTH,
        .x_max = CONFIG_HWE_DISPLAY_WIDTH,
        .y_max = CONFIG_HWE_DISPLAY_HEIGHT,
        .rst_gpio_num = GPIO_NUM_NC,
        .int_gpio_num = CONFIG_LCD_TOUCH_INT,
        .flags = {
            .swap_xy = false,
            .mirror_x = false,
            .mirror_y = true,
        },
        .process_coordinates = process_coordinates,
    };
As well, I disabled sw_rotate in lvgl_port config, what forces lvgl_port to rotate display using hardware calls.
2024-12-21 23-53-28.JPG
2024-12-21 23-53-28.JPG (692.87 KiB) Viewed 811 times

Who is online

Users browsing this forum: No registered users and 118 guests