Skip to main content

Framebuffer Strategies

This section shows how to configure the TouchGFX Generator to generate a TouchGFX HAL that uses one of the following Frame Buffer strategies:

  • Single
  • Double
  • Partial

All framebuffer options

Single Frame Buffer

Choosing Single Buffer as the buffering strategy developers are able to let the compiler allocate memory for the framebuffer in internal RAM but can also choose a specific location for the buffer.

By Allocation

When choosing By Allocation TouchGFX Generator will allocate an array based on the dimensions and bitdepth of the application.

Single framebuffer, by allocation

Code is generated to configure the HAL to use this array as the framebuffer.

TouchGFXGeneratedHAL.cpp
namespace {
// Use the section "TouchGFX_Framebuffer" in the linker script
// to specify the placement of the buffer
LOCATION_PRAGMA("TouchGFX_Framebuffer")
uint32_t frameBuf[(480 * 272 * 2 + 3) / 4] LOCATION_ATTRIBUTE("TouchGFX_Framebuffer");
}

void TouchGFXGeneratedHAL::initialize()
{
HAL::initialize();

setFrameBufferStartAddresses((void*)frameBuf, (void*)0, (void*)0);
}

By Address

When choosing By Address for the location of the framebuffer TouchGFX Generator will use the specified Start Addresses during HAL initialization.

Single framebuffer, by address

TouchGFXGeneratedHAL.cpp
void TouchGFXGeneratedHAL::initialize()
{
HAL::initialize();

setFrameBufferStartAddresses((void*)0xC0000000, (void*)0, (void*)0);
}

Double Frame Buffer

In a double frame buffer configuration, code to swap farmebuffers will be generated in the HAL by TouchGFX Generator depending on the selected Framebuffer strategy and display interface. This memory interface to frame buffer location is used by the TouchGFX Engine during the main event loop.

By Address

When choosing By Address TouchGFX Generator will use the two specified Start Addresses during HAL initialization.

Double framebuffer, by address

TouchGFXGeneratedHAL.cpp
void TouchGFXGeneratedHAL::initialize()
{
HAL::initialize();

setFrameBufferStartAddresses((void*)0xC0000000, (void*)0xC003FC00, (void*)0);
}
Tip
When using Parallel RGB (LTDC) as display interface, the start address will be inherited from the LTDC Layer settings.

By Allocation

When choosing By Allocation TouchGFX Generator will allocate an array based on the dimensions and bitdepth of the application, exactly as with a Single Frame Buffer, only twice the size.

Single framebuffer, by allocation

TouchGFXGeneratedHAL.cpp
namespace {
// Use the section "TouchGFX_Framebuffer" in the linker to specify the placement of the buffer
LOCATION_PRAGMA("TouchGFX_Framebuffer")
uint32_t frameBuf[(480 * 272 * 2 + 3) / 4 * 2] LOCATION_ATTRIBUTE("TouchGFX_Framebuffer");
}

void TouchGFXGeneratedHAL::initialize()
{
HAL::initialize();

setFrameBufferStartAddresses((void*)frameBuf, (void*)(frameBuf + sizeof(frameBuf)/(sizeof(uint32_t)*2)), (void*)0);
}

Partial Frame Buffer

Selecting the Partial Buffer strategy allows developers to choose a number of blocks and a size for each of these to be used as frame buffers. This strategy uses what TouchGFX calls a Frame Buffer Allocator and is different from supplying either a pointer to external memory where the frame buffer is located, or allocating a fixed sized array in internal memory.

See the article on Framebuffer for a general overview of the concept of frame buffers.

Tip
Usually, STM32G0 does not have enough internal RAM to fit framebuffer. "Partial Buffer" would be a perfect match for a low cost solution using this MCU.

Partial framebuffer

Since a partial buffering strategy is typically only used with low cost MCU with no TFT controller and little internal RAM the Partial Buffer Strategy expects the developer to implement the transfer of the contents of the framebuffer to the display. See FMC/SPI Scenario for how to transmit pixels to e.g. a serial display on MCUs with no TFT Controller.

In order to synchronize with TouchGFX when using the Partial Framebuffer strategy developers are required to provide implementations for the following two functions. The code displayed below is generated by CubeMX inside TouchGFX/target/generated/TouchGFXGeneratedHAL.cpp and defines the interface from developer to the TouchGFX Engine.

TouchGFXGeneratedHAL.cpp
/* ******************************************************
* Functions required by Partial Frame Buffer Strategy
* ******************************************************
*
* * uint8_t isTransmittingData() must return whether or not data is currently being transmitted, over e.g. SPI.
* * void transmitFrameBufferBlock(uint8_t* pixels, uint16_t x, uint16_t y, uint16_t w, uint16_t h) will be called
* when the framework wants to send a block. The user must then transfer
* the data represented by the arguments.
*
* A user must call touchgfx::startNewTransfer(); once transmitFrameBufferBlock() has succesfully sent a block.
* E.g. if using DMA to transfer the block, this could be called in the "Transfer Completed" interrupt handler.
*
*/
extern "C" void transmitFrameBufferBlock(uint8_t* pixels, uint16_t x, uint16_t y, uint16_t w, uint16_t h);
extern "C" uint8_t isTransmittingData();

The following function is also generated by CubeMX inside the read-only TouchGFXGeneratedHAL class inside TouchGFX/target/generated/TouchGFXGeneratedHAL.cpp.

Note
This flushFrameBuffer() function is generally used for MCUs with no TFT Controller. In the case of Partial Frame Buffers the TouchGFX Generator can generate a definition for this method specifically for that frame buffer strategy.
TouchGFXGeneratedHAL.cpp
void TouchGFXGeneratedHAL::flushFrameBuffer(const touchgfx::Rect& rect)
{
HAL::flushFrameBuffer(rect);

// Once flushFrameBuffer() is called by the framework a block is ready for transfer
// Mark it ready for transfer and transmit it if user defined method
// isTransmittingData() does not return false

// If data is not being transmitted, transfer the data with user defined method
// transmitFrameBufferBlock().
frameBufferAllocator->markBlockReadyForTransfer();
if (!isTransmittingData())
{
touchgfx::Rect r;
const uint8_t* pixels = frameBufferAllocator->getBlockForTransfer(r);
transmitFrameBufferBlock((uint8_t*)pixels, r.x, r.y, r.width, r.height);
}
}