diff --git a/boards/base/STM32F429i-Discovery/chibios/board_STM32LTDC.h b/boards/base/STM32F429i-Discovery/chibios/board_STM32LTDC.h index f599c81e..cdcb69f9 100644 --- a/boards/base/STM32F429i-Discovery/chibios/board_STM32LTDC.h +++ b/boards/base/STM32F429i-Discovery/chibios/board_STM32LTDC.h @@ -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 diff --git a/boards/base/STM32F439i-Eval/CubeHal/board_STM32LTDC.h b/boards/base/STM32F439i-Eval/CubeHal/board_STM32LTDC.h index c78cc982..2a423761 100644 --- a/boards/base/STM32F439i-Eval/CubeHal/board_STM32LTDC.h +++ b/boards/base/STM32F439i-Eval/CubeHal/board_STM32LTDC.h @@ -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) diff --git a/boards/base/STM32F746-Discovery/board_STM32LTDC.h b/boards/base/STM32F746-Discovery/board_STM32LTDC.h index 2c28103a..0d1214c3 100644 --- a/boards/base/STM32F746-Discovery/board_STM32LTDC.h +++ b/boards/base/STM32F746-Discovery/board_STM32LTDC.h @@ -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) diff --git a/drivers/gdisp/STM32LTDC/board_STM32LTDC_template.h b/drivers/gdisp/STM32LTDC/board_STM32LTDC_template.h index 91623334..70b7c024 100644 --- a/drivers/gdisp/STM32LTDC/board_STM32LTDC_template.h +++ b/drivers/gdisp/STM32LTDC/board_STM32LTDC_template.h @@ -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) diff --git a/drivers/gdisp/STM32LTDC/gdisp_lld_STM32LTDC.c b/drivers/gdisp/STM32LTDC/gdisp_lld_STM32LTDC.c index 56a716b2..278a2aea 100644 --- a/drivers/gdisp/STM32LTDC/gdisp_lld_STM32LTDC.c +++ b/drivers/gdisp/STM32LTDC/gdisp_lld_STM32LTDC.c @@ -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 diff --git a/drivers/gdisp/STM32LTDC/gdisp_lld_config.h b/drivers/gdisp/STM32LTDC/gdisp_lld_config.h index 75190eb8..1ea27d9a 100644 --- a/drivers/gdisp/STM32LTDC/gdisp_lld_config.h +++ b/drivers/gdisp/STM32LTDC/gdisp_lld_config.h @@ -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 diff --git a/drivers/gdisp/STM32LTDC/readme.md b/drivers/gdisp/STM32LTDC/readme.md index e1e9baf9..d9395ede 100644 --- a/drivers/gdisp/STM32LTDC/readme.md +++ b/drivers/gdisp/STM32LTDC/readme.md @@ -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; +} +```