Compare commits

...

7 Commits

Author SHA1 Message Date
Joel Bodenmann 08c2358542 STM32LTDC: Support double buffering 2021-08-18 16:38:23 +02:00
Joel Bodenmann 5e3159064a Fix typo in readme 2021-08-17 15:56:06 +02:00
Joel Bodenmann 8804f3a381 STM32LTDC: Fix misspelled macro (internally only) 2021-08-17 15:55:02 +02:00
Joel Bodenmann 7f78be1183 STM32LTDC: Rename user config options for consistency & add docs 2021-08-17 14:23:57 +02:00
Joel Bodenmann 91247717a0 STM32LTDC: Expose more options to board file 2021-08-17 13:40:57 +02:00
Joel Bodenmann 2de054b530 STM32LTDC: Remove obsolete LTDC_NO_CLOCK_INIT define 2021-08-17 13:29:49 +02:00
Joel Bodenmann 94fd096fdd STM32LTDC: Rename ALLOW_2ND_LAYER to LTDC_USE_2ND_LAYER 2021-08-17 13:26:54 +02:00
8 changed files with 188 additions and 84 deletions

View File

@ -23,8 +23,6 @@ static const SPIConfig spi_cfg = {
((1 << 3) & SPI_CR1_BR) | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_MSTR
};
#define ALLOW_2ND_LAYER GFXON
static const ltdcConfig driverCfg = {
240, 320,
10, 2,
@ -47,7 +45,7 @@ static const ltdcConfig driverCfg = {
0xFF, // alpha
LTDC_LEF_ENABLE // flags
},
#if ALLOW_2ND_LAYER
#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

@ -19,8 +19,6 @@
#include "stm32f4xx_hal.h"
#include "stm324x9i_eval_sdram.h"
#define ALLOW_2ND_LAYER GFXON
// Panel parameters
// This panel is a AMPIRE640480 panel.
@ -48,7 +46,7 @@ static const ltdcConfig driverCfg = {
LTDC_LEF_ENABLE // Layer configuration flags
},
#if ALLOW_2ND_LAYER
#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

@ -36,8 +36,6 @@
#define AFRH AFR[1]
#endif
#define ALLOW_2ND_LAYER GFXON
static const ltdcConfig driverCfg = {
480, 272, // Width, Height (pixels)
41, 10, // Horizontal, Vertical sync (pixels)
@ -62,7 +60,7 @@ static const ltdcConfig driverCfg = {
LTDC_LEF_ENABLE // Layer configuration flags
},
#if ALLOW_2ND_LAYER
#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)
@ -406,7 +404,7 @@ static GFXINLINE void init_ltdc_clock(void)
RCC->APB2ENR |= RCC_APB2ENR_LTDCEN;
}
#if LTDC_USE_DMA2D
#if STM32LTDC_USE_DMA2D
static GFXINLINE void init_dma2d_clock(void)
{
// Enable DMA2D clock

View File

@ -8,8 +8,6 @@
#ifndef _GDISP_LLD_BOARD_H
#define _GDISP_LLD_BOARD_H
#define ALLOW_2ND_LAYER GFXON
static const ltdcConfig driverCfg = {
480, 272, // Width, Height (pixels)
41, 10, // Horizontal, Vertical sync (pixels)
@ -34,7 +32,7 @@ static const ltdcConfig driverCfg = {
LTDC_LEF_ENABLE // Layer configuration flags
},
#if ALLOW_2ND_LAYER
#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)
@ -60,7 +58,7 @@ static GFXINLINE void init_ltdc_clock(void)
// Setup LTDC clock and enable the peripheral
}
#if LTDC_USE_DMA2D
#if STM32LTDC_USE_DMA2D
static GFXINLINE void init_dma2d_clock(void)
{
// Setup DMA2D clock and enable the peripheral

View File

@ -12,6 +12,10 @@
#define GDISP_DRIVER_VMT GDISPVMT_STM32LTDC
#include "gdisp_lld_config.h"
#include "../../../src/gdisp/gdisp_driver.h"
#include "stm32_ltdc.h"
#if STM32LTDC_USE_DMA2D
#include "stm32_dma2d.h"
#endif
#if defined(GDISP_SCREEN_HEIGHT) || defined(GDISP_SCREEN_HEIGHT)
#if GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_DIRECT
@ -23,31 +27,38 @@
#undef GDISP_SCREEN_HEIGHT
#endif
#ifndef LTDC_USE_DOUBLE_BUFFERING
#define LTDC_USE_DOUBLE_BUFFERING GFXOFF
#ifndef STM32LTDC_DMA_CACHE_FLUSH
#define STM32LTDC_DMA_CACHE_FLUSH GFXOFF
#endif
#ifndef LTDC_USE_DMA2D
#define LTDC_USE_DMA2D GFXOFF
#ifndef STM32LTDC_USE_DMA2D
#define STM32LTDC_USE_DMA2D GFXOFF
#endif
#ifndef LTDC_NO_CLOCK_INIT
#define LTDC_NO_CLOCK_INIT GFXOFF
#ifndef STM32LTDC_USE_LAYER2
#define STM32LTDC_USE_LAYER2 GFXOFF
#endif
#ifndef LTDC_DMA_CACHE_FLUSH
#define LTDC_DMA_CACHE_FLUSH GFXOFF
#ifndef STM32LTDC_USE_RGB565
#define STM32LTDC_USE_RGB565 GFXOFF
#endif
#include "stm32_ltdc.h"
// 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
#if LTDC_USE_DMA2D
#include "stm32_dma2d.h"
// 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)
#undef LTDC_DMA_CACHE_FLUSH
#define LTDC_DMA_CACHE_FLUSH GFXON
#undef STM32LTDC_DMA_CACHE_FLUSH
#define STM32LTDC_DMA_CACHE_FLUSH GFXON
#endif
#endif
typedef struct ltdcLayerConfig {
// Frame
LLDCOLOR_TYPE* frame; // Frame buffer address
@ -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
@ -111,7 +122,7 @@ typedef struct ltdcConfig {
/* Driver local routines. */
/*===========================================================================*/
#define PIXIL_POS(g, x, y) ((y) * ((ltdcLayerConfig *)g->priv)->pitch + (x) * LTDC_PIXELBYTES)
#define PIXEL_POS(g, x, y) ((y) * ((ltdcLayerConfig *)g->priv)->pitch + (x) * LTDC_PIXELBYTES)
#define PIXEL_ADDR(g, pos) ((LLDCOLOR_TYPE *)((gU8 *)((ltdcLayerConfig *)g->priv)->frame+pos))
/*===========================================================================*/
@ -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);
@ -233,7 +245,7 @@ LLDSPEC gBool gdisp_lld_init(GDisplay* g) {
_ltdc_init();
// Initialise DMA2D
#if LTDC_USE_DMA2D
#if STM32LTDC_USE_DMA2D
dma2d_init();
#endif
@ -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;
}
@ -288,23 +303,23 @@ LLDSPEC void gdisp_lld_draw_pixel(GDisplay* g) {
switch(g->g.Orientation) {
case gOrientation0:
default:
pos = PIXIL_POS(g, g->p.x, g->p.y);
pos = PIXEL_POS(g, g->p.x, g->p.y);
break;
case gOrientation90:
pos = PIXIL_POS(g, g->p.y, g->g.Width-g->p.x-1);
pos = PIXEL_POS(g, g->p.y, g->g.Width-g->p.x-1);
break;
case gOrientation180:
pos = PIXIL_POS(g, g->g.Width-g->p.x-1, g->g.Height-g->p.y-1);
pos = PIXEL_POS(g, g->g.Width-g->p.x-1, g->g.Height-g->p.y-1);
break;
case gOrientation270:
pos = PIXIL_POS(g, g->g.Height-g->p.y-1, g->p.x);
pos = PIXEL_POS(g, g->g.Height-g->p.y-1, g->p.x);
break;
}
#else
pos = PIXIL_POS(g, g->p.x, g->p.y);
pos = PIXEL_POS(g, g->p.x, g->p.y);
#endif
#if LTDC_USE_DMA2D
#if STM32LTDC_USE_DMA2D
while(DMA2D->CR & DMA2D_CR_START);
#endif
@ -326,23 +341,23 @@ LLDSPEC gColor gdisp_lld_get_pixel_color(GDisplay* g) {
switch(g->g.Orientation) {
case gOrientation0:
default:
pos = PIXIL_POS(g, g->p.x, g->p.y);
pos = PIXEL_POS(g, g->p.x, g->p.y);
break;
case gOrientation90:
pos = PIXIL_POS(g, g->p.y, g->g.Width-g->p.x-1);
pos = PIXEL_POS(g, g->p.y, g->g.Width-g->p.x-1);
break;
case gOrientation180:
pos = PIXIL_POS(g, g->g.Width-g->p.x-1, g->g.Height-g->p.y-1);
pos = PIXEL_POS(g, g->g.Width-g->p.x-1, g->g.Height-g->p.y-1);
break;
case gOrientation270:
pos = PIXIL_POS(g, g->g.Height-g->p.y-1, g->p.x);
pos = PIXEL_POS(g, g->g.Height-g->p.y-1, g->p.x);
break;
}
#else
pos = PIXIL_POS(g, g->p.x, g->p.y);
pos = PIXEL_POS(g, g->p.x, g->p.y);
#endif
#if LTDC_USE_DMA2D
#if STM32LTDC_USE_DMA2D
while(DMA2D->CR & DMA2D_CR_START);
#endif
@ -396,12 +411,33 @@ 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
#if LTDC_USE_DMA2D
#if LTDC_DMA_CACHE_FLUSH
#if STM32LTDC_USE_DMA2D
#if STM32LTDC_DMA_CACHE_FLUSH
#if defined(__CC_ARM)
#define __ugfxDSB() __dsb(0xF)
#else // GCC like
@ -440,33 +476,33 @@ LLDSPEC gColor gdisp_lld_get_pixel_color(GDisplay* g) {
switch(g->g.Orientation) {
case gOrientation0:
default:
pos = PIXIL_POS(g, g->p.x, g->p.y);
pos = PIXEL_POS(g, g->p.x, g->p.y);
lineadd = g->g.Width - g->p.cx;
shape = (g->p.cx << 16) | (g->p.cy);
break;
case gOrientation90:
pos = PIXIL_POS(g, g->p.y, g->g.Width-g->p.x-g->p.cx);
pos = PIXEL_POS(g, g->p.y, g->g.Width-g->p.x-g->p.cx);
lineadd = g->g.Height - g->p.cy;
shape = (g->p.cy << 16) | (g->p.cx);
break;
case gOrientation180:
pos = PIXIL_POS(g, g->g.Width-g->p.x-g->p.cx, g->g.Height-g->p.y-g->p.cy);
pos = PIXEL_POS(g, g->g.Width-g->p.x-g->p.cx, g->g.Height-g->p.y-g->p.cy);
lineadd = g->g.Width - g->p.cx;
shape = (g->p.cx << 16) | (g->p.cy);
break;
case gOrientation270:
pos = PIXIL_POS(g, g->g.Height-g->p.y-g->p.cy, g->p.x);
pos = PIXEL_POS(g, g->g.Height-g->p.y-g->p.cy, g->p.x);
lineadd = g->g.Height - g->p.cy;
shape = (g->p.cy << 16) | (g->p.cx);
break;
}
#else
pos = PIXIL_POS(g, g->p.x, g->p.y);
pos = PIXEL_POS(g, g->p.x, g->p.y);
lineadd = g->g.Width - g->p.cx;
shape = (g->p.cx << 16) | (g->p.cy);
#endif
#if LTDC_DMA_CACHE_FLUSH
#if STM32LTDC_DMA_CACHE_FLUSH
{
// This is slightly less than optimal as we flush the whole line in the source and destination image
// instead of just the cx portion but this saves us having to iterate over each line.
@ -529,9 +565,9 @@ LLDSPEC gColor gdisp_lld_get_pixel_color(GDisplay* g) {
gU32 srcstart, dststart;
srcstart = LTDC_PIXELBYTES * ((gU32)g->p.x2 * g->p.y1 * + g->p.x1) + (gU32)g->p.ptr;
dststart = (gU32)PIXEL_ADDR(g, PIXIL_POS(g, g->p.x, g->p.y));
dststart = (gU32)PIXEL_ADDR(g, PIXEL_POS(g, g->p.x, g->p.y));
#if LTDC_DMA_CACHE_FLUSH
#if STM32LTDC_DMA_CACHE_FLUSH
{
// This is slightly less than optimal as we flush the whole line in the source and destination image
// instead of just the cx portion but this saves us having to iterate over each line.

View File

@ -14,29 +14,32 @@
/* Driver hardware support. */
/*===========================================================================*/
#define LTDC_USE_DMA2D GFXON
#define GDISP_HARDWARE_DRAWPIXEL GFXON
#define GDISP_HARDWARE_PIXELREAD GFXON
#define GDISP_HARDWARE_CONTROL GFXON
// 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(GDISP_LTDC_USE_RGB565) && GDISP_LTDC_USE_RGB565
#if defined(STM32LTDC_USE_RGB565) && STM32LTDC_USE_RGB565
#define GDISP_LLD_PIXELFORMAT GDISP_PIXELFORMAT_RGB565
#if GDISP_TOTAL_DISPLAYS > 1
#error "LTDC: You must use RGB888 pixel format with LTDC when using dual layers as only RGB888 currently supports using alpha"
#error "GDISP - STM32LTDC: You must use RGB888 pixel format with LTDC when using dual layers as only RGB888 currently supports using alpha"
#endif
#else
#define GDISP_LLD_PIXELFORMAT GDISP_PIXELFORMAT_RGB888
#endif
/*===========================================================================*/
/* Don't change stuff below this line. Please. */
/*===========================================================================*/
#if LTDC_USE_DMA2D
// 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

@ -0,0 +1,84 @@
# Usage
To use this driver:
1. Add in your `gfxconf.h`:
a) `#define GFX_USE_GDISP GFXON`
2. To your makefile add the following lines:
```
include $(GFXLIB)/gfx.mk
include $(GFXLIB)/drivers/gdisp/STM32LTDC/driver.mk
```
3. Add a `board_STM32LTDC.h` to you project directory (or board directory)
based on one of the templates.
# Configuration
Configuration options available in `gfxconf.h`:
| Option | Default | Description |
| --- | --- | --- |
| `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. 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
To use the 2nd LTDC layer, set `STM32LTDC_USE_LAYER2` to `GFXON` in `gfxconf.h`.
The 2nd layer is exposed as a separate display. Use `gdispGetDisplay()` to retrieve the individual layers.
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;
}
```

View File

@ -1,11 +0,0 @@
To use this driver:
1. Add in your gfxconf.h:
a) #define GFX_USE_GDISP GFXON
2. To your makefile add the following lines:
include $(GFXLIB)/gfx.mk
include $(GFXLIB)/drivers/gdisp/STM32LTDC/driver.mk
3. Add a board_STM32LTDC.h to you project directory (or board directory)
based on one of the templates.