cancel
Showing results for 
Search instead for 
Did you mean: 

DMA2D and LTDC double buffering

andrea.l
Associate

Hi everybody,

I'm developing a simple gui on an STM32F429 microcontroller with the use of the STemWin grapich library. Everything is working well till I'm using just one framebuffer; now I would like to use double buffering to optimize the interface and have a better displaying, but something is going wrong, since I can no more see anything shown, just a black screen with backlight.

I read chapter 23 of emWin manual, where Multiple Buffering is described, and I've done changes to my code accordingly: enabled multiple buffering, customized buffer copy callback, manage buffers switching, enabled Window Manager multiple buffering option. To make this changes, I've used the project "STemWin_SampleDemo" of the "STM32F429I_Discovery" board.

DMA2D and LTDC are used.

Is it possible to copy one buffet into another using DMA2D if it's configured as Register-to-Memory? Should I modify its working mode to Memory-to-Memory?

Thanks in advance for any help,

Andrea

5 REPLIES 5
Lopez.Antonio
Associate II

Hello,

I faced the same problem while I was working in an HMI project some time ago.

Add the following statement at the end of the GRAPHICS_Init() function, inside the USER CODE block:

HAL_LTDC_ProgramLineEvent(&hltdc, 0);

I already reported this in the STM32 discussion forum:

STM32F7 CubeMX issues

Regards

andrea.l
Associate

Hi @Community member​, many thanks for your reply. We're not using CubeMX and the instruction you suggest is already implemented, so I think I'm making some mistakes somehwere else.

Here a snippet of the relevant code functions

#define NUM_BUFFERS  2 // Number of multiple buffers to be used
#define NUM_VSCREENS 1 // Number of virtual screens to be used
#define GUI_NUM_LAYERS 1
 
 
// LINE EVENT CALLBACK
void HAL_LTDC_LineEvenCallback(LTDC_HandleTypeDef *hltdc)
{
  uint32_t Addr;
  uint32_t layer;
 
  for (layer = 0; layer < GUI_NUM_LAYERS; layer++) 
  {
    if (layer_prop[layer].pending_buffer >= 0) 
    {
      /* Calculate address of buffer to be used  as visible frame buffer */
      Addr = layer_prop[layer].address + layer_prop[layer].xSize * layer_prop[layer].ySize * layer_prop[layer].pending_buffer * layer_prop[layer].BytesPerPixel;
      HAL_LTDC_SetAddress(hltdc, layer_prop[layer].address, layer);
	  
      __HAL_LTDC_RELOAD_IMMEDIATE_CONFIG(hltdc);
	  
      /* Notify STemWin that buffer is used */
      GUI_MULTIBUF_ConfirmEx(layer, layer_prop[layer].pending_buffer);
 
      /* Clear pending buffer flag of layer */
      layer_prop[layer].pending_buffer = -1;
    }
  }
  
  HAL_LTDC_ProgramLineEvent(hltdc, 0);
}
 
 
// CONFIG
void LCD_X_Config(void) 
{
  uint32_t i;
  
  LCD_LL_Init();
  
  /* At first initialize use of multiple buffers on demand */
#if (NUM_BUFFERS > 1)
    for (i = 0; i < GUI_NUM_LAYERS; i++) 
    {
      GUI_MULTIBUF_ConfigEx(i, NUM_BUFFERS);
    }
#endif
 
  /* Set display driver and color conversion for 1st layer */
  GUI_DEVICE_CreateAndLink(DISPLAY_DRIVER_0, COLOR_CONVERSION_0, 0, 0);
  
  /* Set size of 1st layer */
  if (LCD_GetSwapXYEx(0)) {
    LCD_SetSizeEx (0, YSIZE_PHYS, XSIZE_PHYS);
    LCD_SetVSizeEx(0, YSIZE_PHYS * NUM_VSCREENS, XSIZE_PHYS);
  } else {
    LCD_SetSizeEx (0, XSIZE_PHYS, YSIZE_PHYS);
    LCD_SetVSizeEx(0, XSIZE_PHYS, YSIZE_PHYS * NUM_VSCREENS);
  }
  
  /*Initialize GUI Layer structure */
  layer_prop[0].address = LCD_LAYER0_FRAME_BUFFER;
#if (GUI_NUM_LAYERS > 1)
  layer_prop[1].address = LCD_LAYER1_FRAME_BUFFER;     
#endif
    
   /* Setting up VRam address and custom functions for CopyBuffer-, CopyRect- and FillRect operations */
  for (i = 0; i < GUI_NUM_LAYERS; i++) 
  {
    layer_prop[i].pColorConvAPI = (LCD_API_COLOR_CONV *)apColorConvAPI[i];
    
    layer_prop[i].pending_buffer = -1;
    
    /* Set VRAM address */
    LCD_SetVRAMAddrEx(i, (void *)(layer_prop[i].address));
    
    /* Remember color depth for further operations */
    layer_prop[i].BytesPerPixel = LCD_GetBitsPerPixelEx(i) >> 3;
    
    /* Set custom functions for several operations */
    LCD_SetDevFunc(i, LCD_DEVFUNC_COPYBUFFER, (void(*)(void))CUSTOM_CopyBuffer);
    LCD_SetDevFunc(i, LCD_DEVFUNC_COPYRECT,   (void(*)(void))CUSTOM_CopyRect);
    
    /* Filling via DMA2D does only work with 16bpp or more */
    if (LCD_LL_GetPixelformat(i) <= LTDC_PIXEL_FORMAT_ARGB4444) 
    {
      LCD_SetDevFunc(i, LCD_DEVFUNC_FILLRECT, (void(*)(void))CUSTOM_FillRect);
      LCD_SetDevFunc(i, LCD_DEVFUNC_DRAWBMP_8BPP, (void(*)(void))BSP_LCD_DrawBitmap8bpp);
    }
    
	/* Here goes color configs */
  }
}
 
// DISPLAY DRIVER
int LCD_X_DisplayDriver(unsigned LayerIndex, unsigned Cmd, void * pData)
{
  int32_t r = 0;
  uint32_t addr;
  int32_t xPos, yPos;
  uint32_t Color;
    
  switch (Cmd) 
  {
  case LCD_X_INITCONTROLLER: 
    LCD_LL_LayerInit(LayerIndex);
    break;
 
  case LCD_X_SETORG: 
    addr = layer_prop[LayerIndex].address + ((LCD_X_SETORG_INFO *)pData)->yPos * layer_prop[LayerIndex].xSize * layer_prop[LayerIndex].BytesPerPixel;
    HAL_LTDC_SetAddress(&LTDC_handle, addr, LayerIndex);
    break;
 
  case LCD_X_SHOWBUFFER: 
    layer_prop[LayerIndex].pending_buffer = ((LCD_X_SHOWBUFFER_INFO *)pData)->Index;
    break;
 
	/* Here goes other cases */
 
  default:
    r = -1;
  }
  return r;
}
 
// DMA2D COPY BUFFER
static void DMA2D_CopyBuffer(uint32_t LayerIndex, void * pSrc, void * pDst, uint32_t xSize, uint32_t ySize, uint32_t OffLineSrc, uint32_t OffLineDst)
{
  uint32_t PixelFormat;
 
  PixelFormat = LCD_LL_GetPixelformat(LayerIndex);
  DMA2D->CR      = 0x00000000UL | (1 << 9);  
  
  /* Set up pointers */
  DMA2D->FGMAR   = (uint32_t)pSrc;                       
  DMA2D->OMAR    = (uint32_t)pDst;                       
  DMA2D->FGOR    = OffLineSrc;                      
  DMA2D->OOR     = OffLineDst; 
  
  /* Set up pixel format */  
  DMA2D->FGPFCCR = PixelFormat;  
  
  /*  Set up size */
  DMA2D->NLR     = (uint32_t)(xSize << 16) | (U16)ySize; 
  
  DMA2D->CR     |= DMA2D_CR_START;   
 
  /* Wait until transfer is done */
  while (DMA2D->CR & DMA2D_CR_START)
  {
  }
}
 
// CUSTOM COPY BUFFER CALLBACK
static void CUSTOM_CopyBuffer(int32_t LayerIndex, int32_t IndexSrc, int32_t IndexDst) {
  uint32_t BufferSize, AddrSrc, AddrDst;
 
  if( layer_prop[LayerIndex].pending_buffer >= 0 )
  {
      BufferSize = GetBufferSize(LayerIndex);
      AddrSrc    = layer_prop[LayerIndex].address + BufferSize * IndexSrc;
      AddrDst    = layer_prop[LayerIndex].address + BufferSize * IndexDst;
      
      DMA2D_CopyBuffer(LayerIndex, (void *)AddrSrc, (void *)AddrDst, layer_prop[LayerIndex].xSize, layer_prop[LayerIndex].ySize, 0, 0);
      layer_prop[LayerIndex].buffer_index = IndexDst;
  }
}

Thanks

Andrea

Lopez.Antonio
Associate II

Hi Andrea,

The HAL_LTDC_ProgramLineEvent() shown in your code is done inside the Line Event ISR, and is required to enable the IRQ again after the Line Event event was serviced.

But the very first Line Event must be enabled manually in your code, just after GUI_Init() is called. I'm not sure if the next description applies to your custom code, but I did the following in my CubeMX generated code:

At the begining of STemwin_wrapper.c, inserted by CubeMX when you configure STemwin for using two frame buffers,

#define NUM_BUFFERS 2 /* Number of multiple buffers to be used */
 
 

At the end of STemwin_wrapper.c, in function GRAPHICS_Init(),

void GRAPHICS_Init(void)
{
  /* Initialize the GUI */
  GUI_Init();
 
  WM_MULTIBUF_Enable(1);
  /* Enable the multi-buffering functionality */
 
  /* Activate the use of memory device feature */
    /* USER CODE BEGIN WM_SetCreateFlags */
  //WM_SetCreateFlags(WM_CF_MEMDEV);
 
    /* ALM, LTDC end of frame IRQ enable, not done by STM32CubeMX !!! */
    HAL_LTDC_ProgramLineEvent(&hltdc, 0);
 
    /* USER CODE END WM_SetCreateFlags */
}

Please note the WM_MULTIBUF_Enable(1) call too.

I'm afraid I couldn't help you more with code which was not generated by CubeMX, because I'm not a STemwin expert.

Regards.

Many Thanks Lopez.Antonio !!

Hi Andrea,

I'm in the exact situation today that you were back in Feb. 2019 when you created that post. I know a few years have gone by but maybe you still remember the solution to this issue you had with double-buffering with the STM32F429. I'm also using the STemWin grapichs library.

I'm using the exact same code that you have on this post (I beleive I got it from the "STemWin_SampleDemo") as well. Please let me know if you can share the missing/wrong code that is stopping the double buffering from working.

Thanks in advance!
SPA1