Flickering of objects with double buffering

Description

Hello, I’ve got an issues where my widgets are flickering like in the attached video.

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

STM32H743IIT6 + SDRAM + LTDC Screen (custom board) with ChibiOS and GCC

What LVGL version are you using?

Current master (9.3)

What do you want to achieve?

Remove the flickering

What have you tried so far?

Using direct render mode and double buffering, additionally ported all RTOS functions to ChibiOS

Code to reproduce

Add a code snippet which can run in the simulator. It should contain only the relevant code that compiles without errors when separated from your main code base.

The code block(s) should be formatted like:

uint8_t frame_buffer[600 * 1024 * 2] __attribute__((section(".sdram"), aligned(32)));
uint8_t frame_buffer_optional[600 * 1024 * 2] __attribute__((section(".sdram"), aligned(32)));
uint8_t view_buffer[600 * 1024 * 2] __attribute__((section(".sdram"), aligned(32)));

static THD_WORKING_AREA(waLV_TIMER_HANDLER, 4096);
static THD_FUNCTION(LV_TIMER_HANDLER, arg)
{
    (void)arg;
    chRegSetThreadName("LV_TIMER_HANDLER");
    uint32_t sleep = 0;
    while (true)
    {
        sleep = lv_timer_handler();
        if (sleep == LV_NO_TIMER_READY)
            sleep = LV_DEF_REFR_PERIOD;
        if (sleep <= 0)
            sleep = 1;
        chThdSleepMilliseconds(sleep);
    }
}

static THD_WORKING_AREA(waLV_TICK, 2048);
static THD_FUNCTION(LV_TICK, arg)
{
    (void)arg;
    chRegSetThreadName("LV_TICK_HANDLER");
    while (true)
    {
        lv_tick_inc(10);
        chThdSleepMilliseconds(10);
    }
}
static THD_WORKING_AREA(waLV_GFX, 4096);
static THD_FUNCTION(LV_GFX, arg)
{
    (void)arg;
    chRegSetThreadName("LV_GFX_HANDLER");
    lv_lock();
    ui_init();
    lv_unlock();
    float test = 0;
    bool n = false;
    while (true)
    {
        lv_lock();
        for (int i = 0; i < 8; i++)
        {
            update_values(test, 0, i);
        }
        if(!n)
        {
            test += 0.5f;
        }
        else if (n)
        {
            test -= 0.5f;
        }
        if (test >= 100)
        {
            n = true;
        }
        else if (test <= 0)
        {
            n = false;
        }
        lv_unlock();
        chThdSleepMilliseconds(10);
    }
}

void startLVGL()
{
    palSetPad(GPIOH, 6U);
    ltdcInit();
    ltdcStart(&LTDCD1, &ltdc_cfg);
    dma2dInit();
    dma2dStart(&DMA2DD1, &dma2d_cfg);
    lv_init();
    lv_display_t *disp = lv_st_ltdc_create_direct(view_buffer, frame_buffer_optional, 0);
    chThdCreateStatic(waLV_TIMER_HANDLER, sizeof(waLV_TIMER_HANDLER), NORMALPRIO, LV_TIMER_HANDLER, NULL);
    chThdCreateStatic(waLV_TICK, sizeof(waLV_TICK), NORMALPRIO, LV_TICK, NULL);
    chThdCreateStatic(waLV_GFX, sizeof(waLV_GFX), NORMALPRIO, LV_GFX, NULL);
}

Screenshot and/or video

Did you tried single buffer or smaller buffer or use samller buffer in internal ram?
I meet this issue before i think its probably associated about ram band width.
How many bit did you SDRAM have?

Single buffering is way worse, the flickering happens on way more objects.
As for the question about SDRAM im unsure what you mean exactly, its a w9825g6kh-5

//Edit
Played a bit with timings but best I’ve got stable was 120Mhz/CL2 anything above would hardfault.

//Edit 2
Tried now partial mode with two full sized buffers and the issue is gone, however a new artifact appeared on the top of the screen shown on video below. Tried playing around with the size of the buffers but this didnt make the artifact to dissapear.

I think maybe the speed of SDRAM its not enough for LTDC DRIVER and LVGL drawing. If you draw whole screen at a time it probably cause the issue。

I meet this issue in STM32H7 with 1 16 bit SDRAM W9825G6KH,
Then i tried another board witch its also STM32H7 but have 2 W9825G6KH (32 bit).and the issue its gone. so i think its about the speed of SDRAM.

About you new issue, try to move you buffer base address to another place?
it seens like apart of you code its reuse this area ram.

Actually nothing in the sdram (where the frame buffers are) is set via addresses. The linker script has a section for the sdram and the arrays are normally defined in the code.

Unfortunately after trying all my ideas, I’ve run out of them and have no idea how to resolve the bug with the line at start of the screen.

This only happens in partial mode (with both DMA2D and without).

Use linker section seems be no good idea. Read LTDC appnote about condition for start frames align and optimalization of banks etc.
I pref reserve in linker space for buffers as exclude area. And use direct area address . Is pixel in top bad area static or changing?
Introduction to LCD-TFT display controller (LTDC) on STM32 MCUs - Application note

The pixels in the top row are chaning according to the sliders / arc they are not static.
In terms of the declaration of the frame buffers I dont see an issue where something should try accesing their memory space if they are declared in my way.

uint8_t frame_buffer_optional[600 * 1024 * 2] __attribute__((section(".sdram")));
uint8_t view_buffer[600 * 1024 * 2] __attribute__((section(".sdram")));

Read appnote chapter 5 … And show flush code

In regards of the app note I haven’t seen amything that I would be doing differentely.

static void flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
{
    uint32_t layer_idx = (uint32_t)(uintptr_t)lv_display_get_driver_data(disp);
    g_data.disp_flushed_in_flush_cb[layer_idx] = false;
    ltdc_frame_t *config = LTDCD1.config->bg_laycfg->frame;

    if (disp->render_mode == LV_DISPLAY_RENDER_MODE_DIRECT)
    {
        if (lv_display_is_double_buffered(disp) && lv_display_flush_is_last(disp))
        {
            ltdcBgSetFrameAddress(&LTDCD1, (uint32_t)px_map);
            g_data.layer_interrupt_is_owned[layer_idx] = true;
            ltdcStartReload(&LTDCD1, false);
        }
        else
        {
            g_data.disp_flushed_in_flush_cb[layer_idx] = true;
        }
    }
    else
    {
        LTDCConfig *layer_cfg = &LTDCD1.config;

        lv_color_format_t cf = lv_display_get_color_format(disp);
        int32_t disp_width = disp->hor_res;

        uint8_t *fb = (uint8_t *)layer_cfg->bg_laycfg->frame->bufferp;
        uint32_t px_size = lv_color_format_get_size(cf);
        uint32_t fb_stride = px_size * disp_width;
        lv_area_t rotated_area = *area;
        lv_display_rotate_area(disp, &rotated_area);
        uint8_t *first_pixel = fb + fb_stride * rotated_area.y1 + px_size * rotated_area.x1;

        int32_t area_width = lv_area_get_width(area);
        int32_t area_height = lv_area_get_height(area);

        lv_display_rotation_t rotation = lv_display_get_rotation(disp);
        if (rotation == LV_DISPLAY_ROTATION_0)
        {
#if LV_ST_LTDC_USE_DMA2D_FLUSH
            uint32_t dma2d_input_cf = get_dma2d_input_cf_from_lv_cf(cf);
            uint32_t dma2d_output_cf = get_dma2d_output_cf_from_layer_cf(layer_cfg->bg_laycfg->frame->fmt);

            while (DMA2D->CR & DMA2D_CR_START)
                ;
            DMA2D->FGPFCCR = dma2d_input_cf;
            DMA2D->FGMAR = (uint32_t)px_map;
            DMA2D->FGOR = 0;
            DMA2D->OPFCCR = dma2d_output_cf;
            DMA2D->OMAR = (uint32_t)first_pixel;
            DMA2D->OOR = disp_width - area_width;
            DMA2D->NLR = (area_width << DMA2D_NLR_PL_Pos) | (area_height << DMA2D_NLR_NL_Pos);
            g_data.dma2d_interrupt_owner = layer_idx + 1;
            DMA2D->CR = DMA2D_CR_START | DMA2D_CR_TCIE | (0x1U << DMA2D_CR_MODE_Pos); /* memory-to-memory with PFC */
#else
            uint32_t area_stride = px_size * area_width;
            uint8_t *fb_p = first_pixel;
            uint8_t *px_map_p = px_map;
            for (int i = 0; i < area_height; i++)
            {
                lv_memcpy(fb_p, px_map_p, area_stride);
                fb_p += fb_stride;
                px_map_p += area_stride;
            }
            g_data.disp_flushed_in_flush_cb[layer_idx] = true;
#endif
        }
        else
        {
            uint32_t area_stride = px_size * area_width;
            lv_draw_sw_rotate(px_map, first_pixel, area_width, area_height, area_stride, fb_stride, rotation, cf);
            g_data.disp_flushed_in_flush_cb[layer_idx] = true;
        }
    }
}


and i mean issue is in your code flush and your code dont show how buffers for partial is used. Seems you mix render bfs with frame buffer.

The code provided by me is the official lvgl ltdc driver, its slightly adjusted for the us of ChibiOS since it differs from FreeRTOS or bare HAL

OK you still dont show relevant code
A. Framebuffer full for LTDC is

uint8_t frame_buffer[600 * 1024 * 2] __attribute__((section(".sdram"), aligned(32)));

but framebuffer for LVGL partial cant be same.

What exactly would You like to see?
In terms of flushing its handled by the lvgl driver to which I pass two buffers like the lvgl documentation shows. Unless im forgetting something?

which two buffers?

Im using the way like the documentation shows: STM32 LTDC Driver — LVGL 9.3 documentation

For direct render mode, invoke lv_st_ltdc_create_direct() like this:

void * my_ltdc_framebuffer_address = (void *)0x20000000u;
uint32_t my_ltdc_layer_index = 0; /* typically 0 or 1 */
lv_display_t * disp = lv_st_ltdc_create_direct(my_ltdc_framebuffer_address,
                                               optional_other_full_size_buffer,
                                               my_ltdc_layer_index);
my_ltdc_framebuffer_address **is the framebuffer configured for use by LTDC**.

against partial

For partial render mode, invoke lv_st_ltdc_create_partial() like this:

static uint8_t partial_buf1[65536];
static uint8_t optional_partial_buf2[65536];
uint32_t my_ltdc_layer_index = 0; /* typically 0 or 1 */
lv_display_t * disp = lv_st_ltdc_create_partial(partial_buf1,
                                                optional_partial_buf2,
                                                65536,
                                                my_ltdc_layer_index);
**The driver will use the information in the LTDC layer configuration** to find the layer's framebuffer and flush to it.

then if you read carefully for partial LTDC isnt set with this func and require other config… But primary no same buffer as LTDC.

Thank You indeed, the cause was the same buffer was used for both LTDC and the partial buffer. Now the issue is resolved :slight_smile:

1 Like