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
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.
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.
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.
TouchGFXGeneratedHAL.cpp
void TouchGFXGeneratedHAL::initialize()
{
HAL::initialize();
setFrameBufferStartAddresses((void*)0xC0000000, (void*)0xC003FC00, (void*)0);
}
Tip
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.
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
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
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);
}
}