Issue with DMA2D Flush Not Triggering Interrupt on Custom STM32H747 PCB

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.

you mix HAL with bare metal, then ofcourse HAL callbacks not work ok.