Skip to main content
Associate III
February 24, 2025
Solved

TouchGFX: advanceFrameBufferToRect() undefined reference

  • February 24, 2025
  • 3 replies
  • 1273 views

Hello,

I have a STM32F777 with ST7789 Display controller over SPI.

To write the Framebuffer i have the following code:

 

void TouchGFXHAL::flushFrameBuffer(const touchgfx::Rect& rect)
{
 // Get Framebuffer Address
 uint8_t* frameBufferAddress = (uint8_t*)HAL::lockFrameBuffer();

 // Calc start address
 uint8_t* fbPtr = advanceFrameBufferToRect(frameBufferAddress, rect

 // Set window
 DisplaySetWindow(rect.x, rect.y, rect.width, rect.height);

 // Start DMA Transfer
 DisplaySendDataDMA(fbPtr, rect.width * rect.height * 2); 

 // Wait for DMA Transfer end
 if (osSemaphoreWait(DisplayTransferSemHandle, osWaitForever) == pdTRUE)
 {
 	HAL::unlockFrameBuffer();
 HAL::flushFrameBuffer(rect); 
 }
}

 

I can't build the code because the function advanceFrameBufferToRect() is defined as inline but without implementation in the header. The implementation in the source file is not accesible with the inline statement.

How can I fix this error? Is the code above correct?

Thanks for your reply!

 

Best answer by Flemming Gram CHRISTENSEN

Hello Sandro

It looks like a mistake on the Generator. Maybe the function was only intended to be used in TouchGFXGeneratedHAL.cpp. I see no reason for that. We will change it in next release...

Meanwhile, I think you should just copy the function implementation to TouchGFXHAL.cpp.

Another thing:

When you copy pixels to the display like above, you are never drawing pixels and transmitting at the same time. This hurts performance. Try change the code so wait only if it is still running:

if (dmatransferrunning) {  wait on transfer semaphore }
setDisplayWindow(...)
sendPixelDataDMA(...)
return; // leave DMA running.

In endFrame you must also wait for the transfer to complete.

 

One more thing:

With above code, you send pixels to the display uncoordinated with the display refresh. This can give tearing on the display if you have animations.
It is a better solution to use Partial Framebuffer for GRAM displays or otherwise coordinate the transmission.

But, if you do not see any problems in your application (it depends on the graphics you are drawing), then stick with what you have (simple).

A few articles for reference:

https://support.touchgfx.com/docs/basic-concepts/framebuffer#framebuffer-strategies

Best Regards

3 replies

GaetanGodart
Technical Moderator
February 25, 2025

Hello @Sandro_K ,

 

Have you looked at the TouchGFX SPI guide ?

 

Regards,

Sandro_KAuthor
Associate III
February 25, 2025

Thanks for your reply. Yes, i have looked at the SPI guide but there ist not much information.

In the TouchGFXHAL.cpp file at function flushFrameBuffer(...)  there is some information:

 // Calling parent implementation of flushFrameBuffer(const touchgfx::Rect& rect).
 //
 // To overwrite the generated implementation, omit the call to the parent function
 // and implement the needed functionality here.
 // Please note, HAL::flushFrameBuffer(const touchgfx::Rect& rect) must
 // be called to notify the touchgfx framework that flush has been performed.
 // To calculate the start address of rect,
 // use advanceFrameBufferToRect(uint8_t* fbPtr, const touchgfx::Rect& rect)
 // defined in TouchGFXGeneratedHAL.cpp

I tried to implement these instructions. So I need the function advanceFrameBufferToRect(). 

And there comes the point. The function prototype is specified as inline function in the header file of the library. The implementation is in the .cpp file but this is not working because the function is defined as inline. I think there is a bug in the library. 

Either the function is implemented directly as an inline function in the header or the keyword inline is omitted.

GaetanGodart
Technical Moderator
February 25, 2025

Well you can check what you have in TouchGFXGeneratedHAL.hpp.
Personally, on the STM32C071 TBS which use an SPI display I have that:

GaetanGodart_0-1740498288293.png

The inline is not omitted and the function is not implemented in the hpp.

What do you have in your project?

 

Here is an example of implementation of flushFrameBuffer.

 

In the code you shared, on line 7, why is there no closing parenthesis and semicolon?

uint8_t* fbPtr = advanceFrameBufferToRect(frameBufferAddress, rect

 

 

Regards,

Sandro_KAuthor
Associate III
February 26, 2025

This is my complete code:

 

void TouchGFXHAL::flushFrameBuffer(const touchgfx::Rect& rect)
{
 // Calling parent implementation of flushFrameBuffer(const touchgfx::Rect& rect).
 //
 // To overwrite the generated implementation, omit the call to the parent function
 // and implement the needed functionality here.
 // Please note, HAL::flushFrameBuffer(const touchgfx::Rect& rect) must
 // be called to notify the touchgfx framework that flush has been performed.
 // To calculate the start address of rect,
 // use advanceFrameBufferToRect(uint8_t* fbPtr, const touchgfx::Rect& rect)
 // defined in TouchGFXGeneratedHAL.cpp

 // Get framebuffer
 uint8_t* frameBufferAddress = (uint8_t*)HAL::lockFrameBuffer();

 // calculate start address
 uint8_t* fbPtr = advanceFrameBufferToRect(frameBufferAddress, rect);

 // Set the region on the display
 DisplaySetWindow(rect.x, rect.y, rect.width, rect.height);

 // Start DMA transfer
 DisplaySendDataDMA(fbPtr, rect.width * rect.height * 2); // 2 Bytes per Pixel (RGB565)

 // Wait for DMA transfer end
 if(osSemaphoreAcquire(DisplayTransferSemHandle, osWaitForever) == pdTRUE)
 {
 	// DMA transfer finished
 	HAL::unlockFrameBuffer(); 
 HAL::flushFrameBuffer(rect); 
 }
}

 

The closing parenthesis and semicolon was a copy & paste error. 

But with the function above i get the following error:

error touchgfx.png

How can the code work if it is implemented as inline without implementation in the .hpp file? This is against the cpp definitions.

(I use STM32CubeIDE 1.17.0)

Sandro_KAuthor
Associate III
February 26, 2025

See also: Inline Functions, C++ FAQ

 

 

How do you tell the compiler to make a member function inline? 
The declaration of an inline member function looks just like the declaration of a non-inline member function:

class Fred {
public:
 void f(int i, char c);
};
But when you define an inline member function (the {...} part), you prepend the member function’s definition with the keyword inline, and you (almost always) put the definition into a header file:

inline
void Fred::f(int i, char c)
{
 // ...
}
The reason you (almost always) put the definition (the {...} part) of an inline function in a header file is to avoid “unresolved external” errors from the linker. That error will occur if you put the inline function’s definition in a .cpp file and if that function is called from some other .cpp file.

 

 

  The hint in the article to the unresolved externals is exactly the error that occurs in my project