cancel
Showing results for 
Search instead for 
Did you mean: 

TouchGFX: advanceFrameBufferToRect() undefined reference

Sandro_K
Associate II

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!

 

7 REPLIES 7
GaetanGodart
ST Employee

Hello @Sandro_K ,

 

Have you looked at the TouchGFX SPI guide ?

 

Regards,

Gaetan Godart
Software engineer at ST (TouchGFX)
Sandro_K
Associate II

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.

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,

Gaetan Godart
Software engineer at ST (TouchGFX)

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_K
Associate II

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

>What do you have in your project?

Maybe there is a difference in the STM32F7 library?

TouchGFXGeneratedHAL.cpp:

 

inline uint8_t* TouchGFXGeneratedHAL::advanceFrameBufferToRect(uint8_t* fbPtr, const touchgfx::Rect& rect) const
{
    //       Advance vertically                   Advance horizontally
    fbPtr += rect.y * lcd().framebufferStride() + rect.x * 2;
    return fbPtr;
}

 

TouchGFXGeneratedHAL.hpp:

 

    /**
     *
     * @fn inline uint8_t* TouchGFXGeneratedHAL::advanceFrameBufferToRect(uint8_t* fbPtr, const touchgfx::Rect& rect) const;
     *
     * @brief This function calculates the offset in the framebuffer address according to "rect" coordinates.
     *
     *        This function is typically for users who need to transfer framebuffer data to
     *        a display from within flushFrameBuffer(Rect& rect). While HAL::lockFrameBuffer()
     *        returns a pointer to the current drawing framebuffer, users must manually calculate
     *        the offset from that pointer to the Rect to transfer. This function will advance the offset
     *        in the framebuffer equal to the rect's upper left corner (x, y).
     *
     *
     *  fbPtr Pointer to the start of the framebuffer, coordinates (0, 0)
     *  rect The area of the screen expressed in absolute coordinates, which has to be transformed to address.
     *
     */
    inline uint8_t* advanceFrameBufferToRect(uint8_t* fbPtr, const touchgfx::Rect& rect) const;

 

 

 

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