STM32LTDC: Support double buffering

develop
Joel Bodenmann 2021-08-18 16:38:23 +02:00
parent 5e3159064a
commit 08c2358542
7 changed files with 113 additions and 22 deletions

View File

@ -45,7 +45,7 @@ static const ltdcConfig driverCfg = {
0xFF, // alpha
LTDC_LEF_ENABLE // flags
},
#if STM32LTDC_USE_LAYER2
#if STM32LTDC_USE_LAYER2 || STM32LTDC_USE_DOUBLEBUFFERING
{ // Foreground layer config (if turned on)
(LLDCOLOR_TYPE *)(SDRAM_BANK_ADDR+(240 * 320 * LTDC_PIXELBYTES)), // Frame buffer address
240, 320, // width, height

View File

@ -46,7 +46,7 @@ static const ltdcConfig driverCfg = {
LTDC_LEF_ENABLE // Layer configuration flags
},
#if STM32LTDC_USE_LAYER2
#if STM32LTDC_USE_LAYER2 || STM32LTDC_USE_DOUBLEBUFFERING
{ // Foreground layer config (if turned on)
(LLDCOLOR_TYPE *)(SDRAM_DEVICE_ADDR+(640 * 480 * LTDC_PIXELBYTES)), // Frame buffer address
640, 480, // Width, Height (pixels)

View File

@ -60,7 +60,7 @@ static const ltdcConfig driverCfg = {
LTDC_LEF_ENABLE // Layer configuration flags
},
#if STM32LTDC_USE_LAYER2
#if STM32LTDC_USE_LAYER2 || STM32LTDC_USE_DOUBLEBUFFERING
{ // Foreground layer config (if turned on)
(LLDCOLOR_TYPE *)(SDRAM_DEVICE_ADDR+(480 * 272 * LTDC_PIXELBYTES)), // Frame buffer address
480, 272, // Width, Height (pixels)

View File

@ -32,7 +32,7 @@ static const ltdcConfig driverCfg = {
LTDC_LEF_ENABLE // Layer configuration flags
},
#if STM32LTDC_USE_LAYER2
#if STM32LTDC_USE_LAYER2 || STM32LTDC_USE_DOUBLEBUFFERING
{ // Foreground layer config (if turned on)
(LLDCOLOR_TYPE *)(SDRAM_DEVICE_ADDR+(480 * 272 * LTDC_PIXELBYTES)), // Frame buffer address
480, 272, // Width, Height (pixels)

View File

@ -40,6 +40,17 @@
#define STM32LTDC_USE_RGB565 GFXOFF
#endif
// Prevent usage of 2nd layer and double buffering at the same time.
// See readme.md for more inforamtion.
#if STM32LTDC_USE_LAYER2 && STM32LTDC_USE_DOUBLEBUFFERING
#error "GDISP - STM32LTDC: Cannot use 2nd LTDC layer and double buffering at the same time. See the driver's readme.md for more information."
#endif
// Double buffering requires GDISP_NEED_CONTROL for the buffer swap command
#if STM32LTDC_USE_DOUBLEBUFFERING && !GDISP_NEED_CONTROL
#error "GDISP - STM32LTDC: Double buffering requires GDISP_NEED_CONTROL."
#endif
// Force DMA cache flushing on certain platforms/systems.
#if STM32LTDC_USE_DMA2D
#if defined(STM32F7) || defined(STM32H7) || defined(STM32F746xx)
@ -73,8 +84,8 @@ typedef struct ltdcConfig {
gCoord hsync, vsync; // Horizontal and Vertical sync pixels
gCoord hbackporch, vbackporch; // Horizontal and Vertical back porch pixels
gCoord hfrontporch, vfrontporch; // Horizontal and Vertical front porch pixels
gU32 syncflags; // Sync flags
gU32 bgcolor; // Clear screen color RGB888
gU32 syncflags; // Sync flags
gU32 bgcolor; // Clear screen color RGB888
ltdcLayerConfig bglayer; // Background layer config
ltdcLayerConfig fglayer; // Foreground layer config
@ -225,7 +236,8 @@ LLDSPEC gBool gdisp_lld_init(GDisplay* g) {
g->board = 0;
switch(g->controllerdisplay) {
case 0: // Display 0 is the background layer
// Display 0 is the background layer
case 0:
// Init the board
init_board(g);
@ -250,23 +262,26 @@ LLDSPEC gBool gdisp_lld_init(GDisplay* g) {
break;
case 1: // Display 1 is the foreground layer
if (!(driverCfg.fglayer.layerflags & LTDC_LEF_ENABLE))
return gFalse;
// Load the foreground layer
_ltdc_layer_init(LTDC_Layer2, &driverCfg.fglayer);
_ltdc_reload();
// Display 1 is the foreground layer or the 2nd buffer for double buffering
case 1:
g->priv = (void *)&driverCfg.fglayer;
// Finish Init the board
post_init_board(g);
#if STM32LTDC_USE_LAYER2
if (!(driverCfg.fglayer.layerflags & LTDC_LEF_ENABLE))
return gFalse;
// Load the foreground layer
_ltdc_layer_init(LTDC_Layer2, &driverCfg.fglayer);
_ltdc_reload();
// Finish Init the board
post_init_board(g);
#endif
break;
default: // There is only 1 LTDC in the CPU and only the 2 layers in the LTDC.
// There is only 1 LTDC in the CPU and only the 2 layers in the LTDC.
default:
return gFalse;
}
@ -396,6 +411,27 @@ LLDSPEC gColor gdisp_lld_get_pixel_color(GDisplay* g) {
set_backlight(g, (unsigned)g->p.ptr);
g->g.Backlight = (unsigned)g->p.ptr;
return;
#if STM32LTDC_USE_DOUBLEBUFFERING
case STM32LTDC_CONTROL_SHOW_BUFFER:
{
// Wait for end-of-line interrupt
// We use simple polling here as end-of-line interrupts are very frequent and usually happen in sub-millisecond intervals.
while (LTDC->ISR & LTDC_ISR_LIF);
// Update framebuffer address in LTDC register
// As we currently only support one layer when doublebuffering is enabled, this change happens only to layer 1.
LTDC_Layer1->CFBAR = (gU32)(((ltdcLayerConfig*)g->priv)->frame) & LTDC_LxCFBAR_CFBADD;
// Reload after LTDC config register modifications
_ltdc_reload();
return;
}
#endif
default:
return;
}
}
#endif

View File

@ -20,7 +20,6 @@
// Both these pixel formats are supported - pick one.
// RGB565 obviously is faster and uses less RAM but with lower color resolution than RGB888
#if defined(STM32LTDC_USE_RGB565) && STM32LTDC_USE_RGB565
#define GDISP_LLD_PIXELFORMAT GDISP_PIXELFORMAT_RGB565
#if GDISP_TOTAL_DISPLAYS > 1
@ -30,11 +29,16 @@
#define GDISP_LLD_PIXELFORMAT GDISP_PIXELFORMAT_RGB888
#endif
/*===========================================================================*/
/* Don't change stuff below this line. Please. */
/*===========================================================================*/
// LLD command to swap buffers if double buffering is enabled.
#if STM32LTDC_USE_DOUBLEBUFFERING
#define STM32LTDC_CONTROL_SHOW_BUFFER (GDISP_CONTROL_LLD+0)
#endif
// Adjust driver config if DMA2D is enabled.
#if STM32LTDC_USE_DMA2D
// DMA2D supports accelerated fills
#define GDISP_HARDWARE_FILLS GFXON

View File

@ -20,7 +20,8 @@ Configuration options available in `gfxconf.h`:
| --- | --- | --- |
| `STM32LTDC_DMA_CACHE_FLUSH` | `GFXOFF` | Whether to flush the DMA cache on DMA2D operations. This will be turned on automatically on certian platforms/systems. |
| `STM32LTDC_USE_DMA2D` | `GFXON` | Whether to use the DMA2D peripheral for hardware acceleration. |
| `STM32LTDC_USE_LAYER2` | `GFXOFF` | Whether to use the 2nd LTDC layer. |
| `STM32LTDC_USE_LAYER2` | `GFXOFF` | Whether to use the 2nd LTDC layer. See the corresponding section below. |
| `STM32LTDC_USE_DOUBLEBUFFERING` | `GFXOFF` | Whether to use double buffering. See the corresponding section below. |
| `STM32LTDC_USE_RGB565` | `GFXOFF` | Whether to use RGB565 instead of RGB888. |
# 2nd layer
@ -31,3 +32,53 @@ The 2nd layer is exposed as a separate display. Use `gdispGetDisplay()` to retri
For more information, see:
- https://wiki.ugfx.io/index.php/Multiple_displays#Example_-_Same_controller
- https://wiki.ugfx.io/index.php/Multiple_displays#Access_the_displays
# Double buffering
Double buffering can be enabled by setting `STM32LTDC_USE_DOUBLEBUFFERING` to `GFXON`.
When double buffering is enabled, the 2nd LTDC layer cannot be used. While this limitation is easy to remove from a software perspective, there is little benefit in doing so. Double buffering requires, as the name implies, twice the memory. If the 2nd layer would be used together with double buffering strategy, a total of four full framebuffers would be required. Years of real-world experience shows that there's practically never enough memory bandwidth available to do this.
To use double buffering in an application, retrieve the two buffers via `gdispGetDisplay()`.
Whenever a buffer swap is necessary, use `gdispGControl(g, STM32LTDC_CONTROL_SHOW_BUFFER, NULL);` where `g` is the buffer to be shown.
Simple example:
```c
#include "gfx.h"
static GDisplay* _display1;
static GDisplay* _display2;
// Requests a buffer swap on the driver level
static void _setActiveBuffer(GDisplay* g)
{
gdispGControl(g, STM32LTDC_CONTROL_SHOW_BUFFER, NULL);
}
int main(void)
{
// Initialize uGFX library
gfxInit();
// Get the two buffers
_display1 = gdispGetDisplay(0);
if (!_display1)
gfxHalt("could not get display 1");
_display2 = gdispGetDisplay(1);
if (!_display2)
gfxHalt("could not get display 2");
// Render to each buffer
gdispGClear(_display1, GFX_BLUE);
gdispGClear(_display2, GFX_RED);
// Switch between buffers
while (gTrue) {
gfxSleepMilliseconds(800);
_setActiveBuffer(_display1);
gfxSleepMilliseconds(800);
_setActiveBuffer(_display2);
}
return 0;
}
```