Double buffering in external SRAM

Description

I have the need to set up double buffering in external SRAM. At this stage, I am using a draw_pixel function in my flush cb, this is low, so I want a way to draw a buffer, and then switch between the two buffers (one being displayed, one being drawn)

What MCU/Processor/Board and compiler are you using?

I am using an STM32 F769 on a custom board. I have external SRAM connected to it (IS61WV102416BLL). I then have a DSI to LVDS bridge as a display controller (SN65DSI83-Q1).

What would be the best way to get the most out of LVGL?

I assume that I should set up two buffers in the external SRAM (this memory can be directly accessed by the STM32 by using the appropriate address), so how would I go about creating these buffers in the SRAM and giving them to LVGL?

Then, I also assume there has to be some way of switching between buffers in my SRAM. It is my understanding that LVGL handles this internally (one buffer is drawn at a time while the other is being displayed) but how would I go about telling my display which buffer to read? I am assuming it needs to know:
LVGL is drawing buffer 1, so I should display buffer 2.
LVGL is drawing buffer 2, so I should display buffer 1.

I have limited understanding of how the display controller works, I have been developing for only a year now, and haven’t got the grasp of how these components work together on a low level.

What LVGL version are you using?

8+ (master, latest)

What do you want to achieve?

I want to get the most out of LVGL, possibly by implementing double buffering in external SRAM.

What have you tried so far?

At this stage, I have enabled double buffering, but my flush cb is very primitive:

In my init:

	/*A static or global variable to store the buffers*/
	static lv_disp_draw_buf_t My_Display_buf;

	/*Static or global buffer(s). The second buffer is optional*/
	static lv_color_t buf_1[LCDSize_Horizontal * 10];
	static lv_color_t buf_2[LCDSize_Horizontal * 10];

	/*Initialize `disp_buf` with the buffer(s). With only one buffer use NULL instead buf_2 */
	lv_disp_draw_buf_init(&My_Display_buf, buf_1, buf_2, LCDSize_Horizontal*10);

	/*Once the buffer initialization is ready a lv_disp_drv_t display driver needs to be initialized with lv_disp_drv_init(&disp_drv)*/
	static lv_disp_drv_t LCD_Driver;
	MyLCDDisplay = LVGLDisplayDriverInit(&LCD_Driver, &My_Display_buf);
	/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one
	 *`put_px` is just an example, it needs to implemented by you.*/
	int32_t x, y;
	for(y = area->y1; y <= area->y2; y++)
	{
		for(x = area->x1; x <= area->x2; x++) {
			LVGL_DrawPixel(x, y, *color_p);
			color_p++;
		}
	}

	/* IMPORTANT!!!
	 * Inform the graphics library that you are ready with the flushing*/
	lv_disp_flush_ready(disp_drv);

lv_disp_t* LVGLDisplayDriverInit(lv_disp_drv_t* disp_drv, lv_disp_draw_buf_t* disp_buf)
{
	//Initialise the driver
	lv_disp_drv_init(disp_drv);

	/*Set the resolution of the display*/
	disp_drv->hor_res = LCDSize_Horizontal;
	disp_drv->ver_res = LCDSize_Vertical;

	/*Used to copy the buffer's content to the display*/
	disp_drv->flush_cb = My_Flush_Callback;

	/*Set a display buffer*/
	disp_drv->draw_buf = disp_buf;

	/*Finally register the driver*/
	return lv_disp_drv_register(disp_drv);
}

This is my draw pixel fn

/**
 * @brief  Draws a pixel on LCD.
 */
void LVGL_DrawPixel(uint16_t Xpos, uint16_t Ypos, lv_color_t _color)
{
	if(LCD_Settings.BPP*(Ypos*LCDControl_GetXSize() + Xpos) < LCD_Settings.Screensize_X*LCD_Settings.Screensize_Y*LCD_Settings.BPP)
	{
		uint8_t B = _color.ch.blue;
		uint8_t G = _color.ch.green;
		uint8_t R = _color.ch.red;

		/* Write data value to all SDRAM memory */
		*(__IO uint16_t*) (hltdc.LayerCfg[ActiveLayer].FBStartAdress + (LCD_Settings.BPP*(Ypos*LCDControl_GetXSize() + Xpos))) = RGBConvert(R,G,B);
	}
}

I read the fn brief for lv_disp_draw_buf_init, where the following is stated

 * @param buf2 Optionally specify a second buffer to make image rendering and image flushing
 *             (sending to the display) parallel.
 *             In the `disp_drv->flush` you should use DMA or similar hardware to send
 *             the image to the display in the background.
 *             It lets LVGL to render next frame into the other buffer while previous is being
 * sent. Set to `NULL` if unused.

But I am still unsure of how to tell my display which buffer to display. I am also unsure of how to create the buffers at a specific address in the SRAM.

Any advice on where to look would be greatly appreciated.

If you use true double buffering (two fullscreen buffers instead of two partial-screen buffers), I believe LVGL will give you the buffer pointer in flush_cb, and you just have to change the LTDC’s framebuffer address to that value.