Hi,
I’m working with a custom PCB using an STM32H747BIT MCU and encountering an issue with flushing the display buffer.
In the current project setup, I’m only using ThreadX and LVGL v9.1.1. There’s a single thread that initializes LVGL and sets the screen to red. Here’s the code for that thread:
void thread_0_entry(ULONG thread_input)
{
/* This thread simply sits in while-forever-sleep loop. */
lv_init();
lvgl_display_init ();
lv_tick_set_cb(HAL_GetTick);
ui_init();
while(1)
{
lv_timer_handler();
//lv_timer_handler_run_in_period(5);
ui_tick();
}
}
I’m not using DIRECT_MODE, and I’m relying on DMA2D to flush the buffer.
The problem occurs in the function “static void draw_buf_flush(lv_display_t * disp)” in lv_refr.c, specifically when “wait_for_flushing(disp_refr);” is called a second time. It blocks at while(disp->flushing);, which I believe happens because I’m using double buffering: when the second buffer is filled, it waits for the first one to finish flushing. However, the DMA2D transfer complete interrupt never triggers. Here is the IRQ handler:
void DMA2D_IRQHandler(void)
{
/* USER CODE BEGIN DMA2D_IRQn 0 */
/* USER CODE END DMA2D_IRQn 0 */
HAL_DMA2D_IRQHandler(&hdma2d);
/* USER CODE BEGIN DMA2D_IRQn 1 */
/* USER CODE END DMA2D_IRQn 1 */
}
So it seems the DMA2D is either not initialized correctly or not properly linked to the callback.
The DMA2D setup and callback binding happen in the following display initialization function:
void lvgl_display_init (void)
{
/* display initialization */
disp = lv_display_create(MY_DISP_HOR_RES, MY_DISP_VER_RES);
#if DIRECT_MODE
lv_display_set_buffers(disp, (void*) LVGL_BUFFER_1_ADDR_AT_SDRAM, (void*) LVGL_BUFFER_2_ADDR_AT_SDRAM, MY_DISP_HOR_RES * MY_DISP_VER_RES * 2, LV_DISPLAY_RENDER_MODE_DIRECT);
HAL_LTDC_SetAddress(&hltdc, (uint32_t)LVGL_BUFFER_2_ADDR_AT_SDRAM, 0); // start with the second buffer: LVGL will render into the first buffer
#else
__attribute__((section(".my_ram_d2_data"))) static uint8_t buf1[144*1024];
__attribute__((section(".my_ram_d2_data"))) static uint8_t buf2[144*1024];
lv_display_set_buffers(disp, (void*) buf1, buf2, sizeof(buf1), LV_DISPLAY_RENDER_MODE_PARTIAL);
/* interrupt callback for DMA2D transfer */
hdma2d.XferCpltCallback = disp_flush_complete;
// lv_display_set_flush_wait_cb(disp, disp_flush_wait);
// lv_thread_sync_init(&sync);
#endif
lv_display_set_flush_cb(disp, disp_flush);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void disp_flush (lv_display_t * display,
const lv_area_t * area,
uint8_t * px_map)
{
#if DIRECT_MODE
if (lv_display_flush_is_last(disp)) {
SCB_CleanInvalidateDCache();
// wait for VSYNC to avoid tearing
while (!(LTDC->CDSR & LTDC_CDSR_VSYNCS));
// swap framebuffers (NOTE: LVGL will swap the buffers in the background, so here we can set the LCD framebuffer to the current LVGL buffer, which has been just completed)
HAL_LTDC_SetAddress(&hltdc, (uint32_t)(lv_display_get_buf_active(disp)->data), 0);
}
lv_display_flush_ready(disp);
#else
lv_coord_t width = lv_area_get_width(area);
lv_coord_t height = lv_area_get_height(area);
SCB_CleanInvalidateDCache();
DMA2D->CR = 0x0U << DMA2D_CR_MODE_Pos;
DMA2D->FGPFCCR = DMA2D_INPUT_RGB565;
DMA2D->FGMAR = (uint32_t)px_map;
DMA2D->FGOR = 0;
DMA2D->OPFCCR = DMA2D_OUTPUT_RGB565;
DMA2D->OMAR = hltdc.LayerCfg[0].FBStartAdress + 2 * \
(area->y1 * MY_DISP_HOR_RES + area->x1);
DMA2D->OOR = MY_DISP_HOR_RES - width;
DMA2D->NLR = (width << DMA2D_NLR_PL_Pos) | (height << DMA2D_NLR_NL_Pos);
DMA2D->IFCR = 0x3FU;
DMA2D->CR |= DMA2D_CR_TCIE;
DMA2D->CR |= DMA2D_CR_START;
#endif
}
It looks like either the DMA2D interrupt isn’t properly enabled, or something is misconfigured in the DMA2D setup. Any suggestions or debugging tips would be greatly appreciated!
Thanks for the support.