STM32LTDC: Support double buffering
This commit is contained in:
parent
5e3159064a
commit
08c2358542
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
```
|
||||
|
Loading…
Reference in New Issue
Block a user