2025-07-09 8:17 AM
Hi everyone,
I’m setting up LVGL on a custom PCB using an STM32H747BIT MCU. The display is connected via LVDS, and I’m using DMA2D for transferring the framebuffer to the screen.
The issue I’m facing is that the DMA2D transfer complete callback is never triggered, and as a result, lv_disp_flush_ready() is not called and the buffer is not pushed to the display.
Below is the relevant initialization code:
lv_init();
lv_tick_set_cb(HAL_GetTick); //FIXME: per ThreadX fer servir tx_time_get
disp = lv_display_create(LV_DISPLAY_HORIZONTAL_RES, LV_DISPLAY_VERTICAL_RES);
lv_display_set_buffers(disp, buf1, NULL, sizeof(buf1), LV_DISPLAY_RENDER_MODE_PARTIAL);
hdma2d.XferCpltCallback = disp_flush_complete;
lv_display_set_flush_cb(disp, my_flush_cb);
The my_flush_cb() function is as follows:
void my_flush_cb(lv_display_t * display, const lv_area_t * area, uint8_t * px_map)
{
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 * LV_DISPLAY_HORIZONTAL_RES + area->x1);
DMA2D->OOR = LV_DISPLAY_HORIZONTAL_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;
}
And the DMA2D completion callback:
static void
disp_flush_complete (DMA2D_HandleTypeDef *hdma2d)
{
lv_disp_flush_ready(disp);
}
And finally, this is the main loop where I call the LVGL handler:
while (1)
{
uint32_t time_till_next = lv_timer_handler();
if(time_till_next == LV_NO_TIMER_READY)
time_till_next = LV_DEF_REFR_PERIOD; // Fallback delay
HAL_Delay(time_till_next); // Delay to reduce CPU usage
}
At this point, I’m just trying to get LVGL to show something on the screen using DMA2D. If anyone has suggestions or sees something I’m missing in the configuration, I’d really appreciate the help.
Thanks!
2025-07-15 2:05 AM
Have you initialized LTDC peripheral properly ? What is your start framebuffer address in LTDC layer ? Is it same as the buf1 which you have passed in lv_display_set_buffers ?
2025-07-15 5:44 AM
Hi,
I believe I’ve correctly initialized the LTDC. Please find the initialization code below:
void MX_LTDC_Init(void)
{
/* USER CODE BEGIN LTDC_Init 0 */
/* USER CODE END LTDC_Init 0 */
LTDC_LayerCfgTypeDef pLayerCfg = {0};
/* USER CODE BEGIN LTDC_Init 1 */
/* USER CODE END LTDC_Init 1 */
hltdc.Instance = LTDC;
hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;
hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;
hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;
hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
hltdc.Init.HorizontalSync = 1;
hltdc.Init.VerticalSync = 1;
hltdc.Init.AccumulatedHBP = 89;
hltdc.Init.AccumulatedVBP = 12;
hltdc.Init.AccumulatedActiveW = 1369;
hltdc.Init.AccumulatedActiveH = 812;
hltdc.Init.TotalWidth = 1440;
hltdc.Init.TotalHeigh = 823;
hltdc.Init.Backcolor.Blue = 0;
hltdc.Init.Backcolor.Green = 0;
hltdc.Init.Backcolor.Red = 0;
if (HAL_LTDC_Init(&hltdc) != HAL_OK)
{
Error_Handler();
}
pLayerCfg.WindowX0 = 0;
pLayerCfg.WindowX1 = 1280;
pLayerCfg.WindowY0 = 0;
pLayerCfg.WindowY1 = 800;
pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB888;
pLayerCfg.Alpha = 255;
pLayerCfg.Alpha0 = 0;
pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
pLayerCfg.FBStartAdress = 0x30000000;
pLayerCfg.ImageWidth = 0;
pLayerCfg.ImageHeight = 0;
pLayerCfg.Backcolor.Blue = 0;
pLayerCfg.Backcolor.Green = 0;
pLayerCfg.Backcolor.Red = 0;
if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN LTDC_Init 2 */
/* USER CODE END LTDC_Init 2 */
However, the address of buf1 doesn’t match the address currently used by the LTDC.
Best regards.
2025-07-15 9:12 PM
Well it should be same as your LTDC layer start framebuffer address. I am also working on similar project. I have declared buf1 globally and LTDC layer 1 configuration I have done it right after setting up my display buffer.
void lvgl_display_init(void) {
/* display initialization */
disp = lv_display_create(MY_DISP_HOR_RES, MY_DISP_VER_RES);
lv_display_set_buffers(disp, (void*) buf1, NULL, sizeof(buf1),
LV_DISPLAY_RENDER_MODE_PARTIAL);
LTDC_Init();
LTDC_LayerCfgTypeDef pLayerCfg = { 0 };
pLayerCfg.WindowX0 = 0;
pLayerCfg.WindowX1 = 540;
pLayerCfg.WindowY0 = 0;
pLayerCfg.WindowY1 = 380;
pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565;
pLayerCfg.Alpha = 255;
pLayerCfg.Alpha0 = 0;
pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_PAxCA;
pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_PAxCA;
pLayerCfg.FBStartAdress = (uint32_t)buf1;
pLayerCfg.ImageWidth = 540;
pLayerCfg.ImageHeight = 380;
pLayerCfg.Backcolor.Blue = 0;
pLayerCfg.Backcolor.Green = 0;
pLayerCfg.Backcolor.Red = 0;
if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0) != HAL_OK) {
Error_Handler();
}
// Initialize LVGL DMA2D
//lv_draw_dma2d_init();
/* interrupt callback for DMA2D transfer */
hdma2d.XferCpltCallback = disp_flush_complete;
lv_display_set_flush_cb(disp, disp_flush);
}