ESP-S3 issue with horizontal banding on LCD ili9488

I’ve ported my code to compile and run on a ESP32 S3
Using LVGL 8.1, ESP-IDF, VS Code Insiders

I have tried in two ways:
1). Using a modified lv_port_esp32 that will compile with LVGL8.1 and also a modified driver of ili9488.
2). Using ESP-LCD with a driver that I wrote for the ili9488, and LVGL8.1.

When I compile and run both 1 and 2 on an ESP32 using the same LCD (BuyDisplay) 480 X 320
and driven using SPI, it works perfectly.

The same code running on the S3 results in Banding of the LCD.
The LCD properly initializes, but I suspect something to do with the SPI bus on the S3 that results in this artefact.
I’ve been at it for days, but just cannot resolve the issue.

I’m hoping someone can perhaps point me in the right direction, or at least give some sort of advice.

The screen appears as follows

Also notice that small areas that are updated are rendered perfectly, eg memory and FPS, and also the time. See video attached as a zip file. (1.9 MB)

I hope that somebody will have the solution, or point me in the correct direction

I have altered the flush routine, to only display the i.e… 480 X 40 buffer to the first 0,479,0,39 memory region, and alternately flush to the next consecutive memory area 0,479,40,79 and instead of them being directly below each other on the screen, there is a empty band of 40 lines in-between.
This only happens with S3 port.
See image below.
Hoping somebody will have an idea, as to the cause.


Just guessing but it seems like the SPI can’t send that large buffers and the end of the data is lost.

You can try it out by simply lowering the draw buffer size.

Thx @kisvegabor . As simple as that. Reduced from 40 to 20 lines, and sorted.

Actually that worked but just didn’t make sense why it should be an issue on the ESP32-S3, and not on the S3.

Kept digging and in my S3 port I forgot to increase the spi_max_transfer_size to to actual screen pixels * 3 for the ili9488 and still had it at * 2, but the ili9488 only supports 18bit color for spi and thus needs 3 * bytes.

.max_transfer_sz = LCD_WIDTH * LCD_HEIGHT * sizeof(uint8_t) * 3

Im actually thinking of getting another lcd display with capacitive touch in 3.5" 320*480 that actually supports 16bit color RGB565, as that will save me a lot of DMA memory, required for the conversion from 16 bit to 18bits. Currently using ER-TFTM035-6 with Capacitive Touch and SPI.

Glad the you have found the root of the issue! :slight_smile:

Actually I’m embarrassed to say that that was not the solution. I was so excited, I didn’t notice that I was flashing the ESP32. Well when I eventually noticed, I had to reduce the the buffer line height back to 20, as in the code below.

When I set to theme dark mode, I uncovered another possibly related problem.

static void loadassist_lv_port_disp_init(void)
    static lv_disp_draw_buf_t draw_buf_dsc; // contains internal graphic buffer(s) called draw buffer(s)
    size_t disp_buf_height = 20;

    /* Using static space for display buffer */
     * @brief OK 2022-01-06 After >16 hours of debugging and changing parameters,
     * it appears that on the S3, if the disp_buf_height is set to 40, only every second
     * buffer is transmitted to the LCD RAM. Setting to 20 solves the banding issue.
     * Thx to Kisvegabor for pointing that out.
    static lv_color_t p_disp_buf1[LCD_WIDTH * 20];
    static lv_color_t p_disp_buf2[LCD_WIDTH * 20];

    /* Initialize display buffer */
    lv_disp_draw_buf_init(&draw_buf_dsc, p_disp_buf1, p_disp_buf2, LCD_WIDTH * disp_buf_height);

    ESP_LOGI(TAG, "Register display driver to LVGL");
    disp_drv.full_refresh = 0;
    /*Set the resolution of the display*/
    disp_drv.hor_res = LCD_WIDTH;
    disp_drv.ver_res = LCD_HEIGHT;
    /* Used to copy the buffer's content to the display */
    disp_drv.flush_cb = disp_flush;
    disp_drv.draw_buf = &draw_buf_dsc;
    /* Use lcd_trans_done_cb to inform the graphics library that flush already done */
    loadassist_lcd_set_cb(loadassist_lv_port_flush_ready, NULL);
    /*Finally register the driver*/

It appears that the last pixel of the flushed buffer area is not set. This results in dots at the far right of the screen spaced at exactly every 20 lines.
Also there are dots, at the bottom right corner of each refreshed smaller area on the screen.

See images:
This problem is present on both the ESP32-S3 and ESP32.
ESP32 (just the 1st flushed buffer. Note last pixel - flush size 40 X 480):

ESP32 (full screen with all the artefacts. Notice the smaller updated area).

And then the ESP32-S3 (The full screen with buffer set to 20 lines, thus dots more frequently spaced).

Flush routine as follows:

static esp_err_t panel_ili9488_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data)
    //!ESP_LOGI(TAG, "Draw the bitmap to the screen.....");
    size_t len = (x_end - x_start) * (y_end - y_start);
    ESP_LOGI(TAG, "LEN: %d x_end: %d x_start: %d y_end: %d y_start: %d", len, x_end, x_start, y_end, y_start);

    //!ESP_LOGI(TAG, "Allocate DMA Memory for the 16Bit to 18Bit conversion %d.....", 3 * len * sizeof(uint8_t));
    lv_color16_t *buffer_16bit = (lv_color16_t *) color_data;
    uint8_t *mybuf;
    do {
	    mybuf = (uint8_t *) heap_caps_malloc(3 * len * sizeof(uint8_t), MALLOC_CAP_DMA);
        if (mybuf == NULL)  ESP_LOGE(TAG, "Could not allocate enough DMA memory for conversion 16bits to 18 bits! %d", 3 * len * sizeof(uint8_t));
    } while (mybuf == NULL);

    uint32_t LD = 0;
    uint32_t j = 0;

    for (uint32_t i = 0; i < len; i++) {
        LD = buffer_16bit[i].full;
        mybuf[j] = (uint8_t) (((LD & 0xF800) >> 8) | ((LD & 0x8000) >> 13));
        mybuf[j] = (uint8_t) ((LD & 0x07E0) >> 3);
        mybuf[j] = (uint8_t) (((LD & 0x001F) << 3) | ((LD & 0x0010) >> 2));

    //!ESP_LOGI(TAG, "Converted 16bits color to 18 bits color!");

    ili9488_panel_t *ili9488 = __containerof(panel, ili9488_panel_t, base);
    assert((x_start < x_end) && (y_start < y_end) && "start position must be smaller than end position");
    esp_lcd_panel_io_handle_t io = ili9488->io;

    x_start += ili9488->x_gap;
    x_end += ili9488->x_gap;
    y_start += ili9488->y_gap;
    y_end += ili9488->y_gap;

    //define an area of frame memory where MCU can access
    esp_lcd_panel_io_tx_param(io, LCD_CMD_CASET, (uint8_t[]) {
        (x_start >> 8) & 0xFF,
        x_start & 0xFF,
        ((x_end - 1) >> 8) & 0xFF,
        (x_end - 1) & 0xFF,
    }, 4);
    esp_lcd_panel_io_tx_param(io, LCD_CMD_RASET , (uint8_t[]) {
        (y_start >> 8) & 0xFF,
        y_start & 0xFF,
        ((y_end - 1) >> 8) & 0xFF,
        (y_end - 1) & 0xFF,
    }, 4);

    // transfer frame buffer
    //ili9488_send_color((void *) mybuf, size * 3);

    //!ESP_LOGI(TAG, "Writing to memory....");
    //size_t len = (x_end - x_start) * (y_end - y_start) * ili9488->bits_per_pixel / 8; This only supports 8 and 16bit SPI
    esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, (void *) mybuf, len * 3); //need 3 X uint8_t for converted 18 bits.

    return ESP_OK;

For the missing last pixel: what if you put together a small image manually (e.g. 2x2 px) and send it manually with your flush_cb?