diff --git a/docs/src/details/integration/chip_vendors/stm32/ltdc.rst b/docs/src/details/integration/chip_vendors/stm32/ltdc.rst index ffbffcab6c..efbfea4899 100644 --- a/docs/src/details/integration/chip_vendors/stm32/ltdc.rst +++ b/docs/src/details/integration/chip_vendors/stm32/ltdc.rst @@ -65,8 +65,9 @@ LTDC layer to create the display for. For the best visial results, ``optional_other_full_size_buffer`` should be used if enough memory is available. Single-buffered mode is what you should use -if memory is very scarce. If there is almost enough memory for double-buffered -direct mode, but not quite, then use partial render mode. +if memory is very scarce. Chips with a CPU data cache have unavoidable visual +artifacts when using single-buffered direct mode. If there is almost enough +memory for double-buffered direct mode, but not quite, then use partial render mode. To clarify what ``my_ltdc_framebuffer_address`` exactly is, it's the value of ``pLayerCfg.FBStartAdress`` when the LTDC layer is configured using the STM32 HAL, @@ -118,6 +119,26 @@ the display width is 800, the display height is 480, and ``1K`` means 1024 bytes FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 4096K } +If the framebuffer is not reserved in the linker script or the framebuffer is not set during +board initialization, you may absolutely set it at runtime before creating the LVGL display. + +.. code-block:: c + + /* like this */ + HAL_LTDC_SetAddress(&hltdc, (uint32_t)my_ltdc_framebuffer_address, my_ltdc_layer_index); + + /* and then... */ + + lv_display_t * disp; + disp = lv_st_ltdc_create_direct(my_ltdc_framebuffer_address, + optional_other_full_size_buffer, + my_ltdc_layer_index); + /* or */ + disp = lv_st_ltdc_create_partial(partial_buf1, + optional_partial_buf2, + partial_buf_size, + my_ltdc_layer_index); + Display Rotation **************** diff --git a/src/drivers/display/st_ltdc/lv_st_ltdc.c b/src/drivers/display/st_ltdc/lv_st_ltdc.c index 10599d1529..06b56d63bf 100644 --- a/src/drivers/display/st_ltdc/lv_st_ltdc.c +++ b/src/drivers/display/st_ltdc/lv_st_ltdc.c @@ -49,6 +49,7 @@ static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_m static void flush_wait_cb(lv_display_t * disp); static lv_color_format_t get_lv_cf_from_layer_cf(uint32_t cf); static void reload_event_callback(LTDC_HandleTypeDef * hltdc); +static void clean_dcache(void); #if LV_ST_LTDC_USE_DMA2D_FLUSH static void transfer_complete_callback(DMA2D_HandleTypeDef * hdma2d); @@ -144,7 +145,15 @@ static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_m g_data.disp_flushed_in_flush_cb[layer_idx] = false; if(disp->render_mode == LV_DISPLAY_RENDER_MODE_DIRECT) { - if(lv_display_is_double_buffered(disp) && lv_display_flush_is_last(disp)) { + bool flush_is_last = lv_display_flush_is_last(disp); + if(flush_is_last) { + /* there is no ideal time to clean the cache (if present) + for **single-buffered** direct mode because the active buffer is drawn to + while LTDC is scanning it. Clean it in the last flush, at least, + but not every flush because it's expensive for not much visual improvement. */ + clean_dcache(); + } + if(flush_is_last && lv_display_is_double_buffered(disp)) { HAL_LTDC_SetAddress_NoReload(&hltdc, (uint32_t)px_map, layer_idx); g_data.layer_interrupt_is_owned[layer_idx] = true; HAL_LTDC_Reload(&hltdc, LTDC_RELOAD_VERTICAL_BLANKING); @@ -172,6 +181,8 @@ static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_m lv_display_rotation_t rotation = lv_display_get_rotation(disp); if(rotation == LV_DISPLAY_ROTATION_0) { #if LV_ST_LTDC_USE_DMA2D_FLUSH + clean_dcache(); + 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->PixelFormat); @@ -194,12 +205,14 @@ static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_m fb_p += fb_stride; px_map_p += area_stride; } + clean_dcache(); 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); + clean_dcache(); g_data.disp_flushed_in_flush_cb[layer_idx] = true; } } @@ -242,6 +255,15 @@ static void reload_event_callback(LTDC_HandleTypeDef * hltdc) } } +static void clean_dcache(void) +{ +#if defined(__CORTEX_M) && __CORTEX_M == 7 + SCB_CleanDCache(); +#elif defined(__CORTEX_A) && __CORTEX_A == 7 + L1C_CleanDCacheAll(); +#endif +} + #if LV_ST_LTDC_USE_DMA2D_FLUSH static void transfer_complete_callback(DMA2D_HandleTypeDef * hdma2d) {