Lvgl with monochrome display

Description

I’m driving a monochrome display (e-ink) with lvgl and esp32. After first initialisation it displays stuff exactly how I want to, but it does not want to fully refresh afterwards. When I update anything in LVGL after the first image has been drawn, it starts to do something - it will partially draw a random noise to a part of the screen, but it will never affect the overall image, so it will mostly stay the same and never update.

What MCU/Processor/Board and compiler are you using?

ESP32s3, esp-idf v5.0

What LVGL version are you using?

8.3.0

My code is fairly long, but I don’t really suspect the code being at fault here (if I change a display to a standard LCD + different drivers it works exactly how I want to). I think it has to something with updating lv_tick_task() or lv_task_handler(), but I cannot really find any clear explanation if I for example have to set a longer update period to accommodate the e-ink (with SSD1680 display driver), or something like that. My code that does that is here: (it is a mashup of a code from GitHub - AramVartanyan/lvgl_test: Test the lvgl_esp32_drivers)

static const char *LVGL_TAG = "LVGL Handler";

#define LV_TICK_PERIOD_MS 1

static void lv_tick_task(void *arg);
static void _lvg_init(void *pvParameter);

SemaphoreHandle_t xGuiSemaphore;


void lvgl_init()
{
    xTaskCreatePinnedToCore(_lvg_init, "lvgl_init_task", 4096*2, NULL, 0, NULL, 1);
}


static void _lvg_init(void *pvParameter) {

    (void) pvParameter;
    xGuiSemaphore = xSemaphoreCreateMutex();

    lv_init();

    /* Initialize SPI or I2C bus used by the drivers */
    lvgl_driver_init();

    lv_color_t* buf1 = heap_caps_malloc(DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf1 != NULL);


    static lv_color_t *buf2 = NULL;

    static lv_disp_draw_buf_t disp_buf;

    uint32_t size_in_px = DISP_BUF_SIZE;

    #if defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820         \
        || defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A    \
        || defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D     \
        || defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1680     \
        || defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1306

        /* Actual size in pixels, not bytes. */
        size_in_px *= 8;
    #endif

    /* Initialize the working buffer depending on the selected display.
     * NOTE: buf2 == NULL when using monochrome displays. */
    lv_disp_draw_buf_init(&disp_buf, buf1, buf2, size_in_px);

    lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.ver_res = 128;
    disp_drv.hor_res = 296;
    disp_drv.flush_cb = disp_driver_flush;

    /* When using a monochrome display we need to register the callbacks:
     * - rounder_cb
     * - set_px_cb */
    #ifdef CONFIG_LV_TFT_DISPLAY_MONOCHROME
        disp_drv.rounder_cb = disp_driver_rounder;
        disp_drv.set_px_cb = disp_driver_set_px;
    #endif

    disp_drv.draw_buf = &disp_buf;
    // lv_disp_drv_register(&disp_drv);
    lv_disp_t *disp = lv_disp_drv_register(&disp_drv); //lvgl v8+

    /* Create and start a periodic timer interrupt to call lv_tick_inc */
    const esp_timer_create_args_t periodic_timer_args = {
        .callback = &lv_tick_task,
        .name = "periodic_gui"
    };
    
    esp_timer_handle_t periodic_timer;
    // esp_timer_handle_t periodic_timer = NULL; //lvgl v8+
    ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, LV_TICK_PERIOD_MS * 1000));
    // ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, LV_TICK_PERIOD_MS * 200));
    

    xSemaphoreGive(guiTaskSemaphore);

    while (1) {
        /* Delay 1 tick (assumes FreeRTOS tick is 10ms */
        vTaskDelay(pdMS_TO_TICKS(10));

        /* Try to take the semaphore, call lvgl related function on success */
        if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY)) {
            lv_task_handler();
            xSemaphoreGive(xGuiSemaphore);
       }
    }

    /* A task should NEVER return */
    free(buf1);
    #ifndef CONFIG_LV_TFT_DISPLAY_MONOCHROME
        free(buf2);
    #endif
        vTaskDelete(NULL);
}

static void lv_tick_task(void *arg) {
    (void) arg;
    // ESP_LOGI(LVGL_TAG, "tick inc");
    lv_tick_inc(LV_TICK_PERIOD_MS);
}

thank you very much, I can add anything if it will help to solve this

I had the same static image on the right of my SSD1603 display. It was because the maximum horizontal resoluation/ maximum vertical resolution was configured too high for SSD1603. once I set the config parameters using idf.py menuconfig to those that matched my screen it displayed the image successfully.

I’m sure you’ve solved this by now, but for others who might be looking here for ideas …
You don’t specify which eInk display you used, but I guess it is a Waveshare 2.9" monochrome. To the best of my knowledge those displays are portrait oriented, thus you reversed ver_res and hor_res. If I’m correct, that would explain that you see garbage in a portion of the display.
Here are the Waveshare specs: https://files.waveshare.com/upload/e/e6/2.9inch_e-Paper_Datasheet.pdf. Unfortunately Waveshare is not very clear about horizontal and vertical with any of their displays!

Also for the benefit of others, devices with SSD1681 controller are also compatible with this code. SSD1681 only handles max dimension of 200, SSD1680 is for displays where either dimension is greater than 200.