Skip to main content
Graduate
July 9, 2025
Solved

DMA2D callback not triggered when using LVGL on STM32H747 with LVDS display

  • July 9, 2025
  • 1 reply
  • 844 views

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!

    This topic has been closed for replies.
    Best answer by JonConesa

    Hi @VedantK33

     

    I just found the issue, it was due to a bad initialization of the MPU. Thanks for your help!

     

    Best regards. 

    1 reply

    Visitor II
    July 15, 2025

    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 ?

     

    JonConesaAuthor
    Graduate
    July 15, 2025

    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.

    Visitor II
    July 16, 2025

    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);
    }