cancel
Showing results for 
Search instead for 
Did you mean: 

How to transfer buffer to SPI LCD? This isn't it.

Richard Lowe
Senior III

I'm really in need of some direction on how to get TouchGFX working on cheap LCD displays.

I'm using a SPI based LCD in a RGB565 format. I've tested the write commands with images, fonts, etc. but TouchGFX is not transferring correctly.

Here is my understanding of the engine and how to use it:

Implemented a flushFrameBuffer method to transfer the buffer to the display:

void TouchGFXHAL::flushFrameBuffer(const touchgfx::Rect& rect)
{
    // Calling parent implementation of flushFrameBuffer(const touchgfx::Rect& rect).
    //
    // To overwrite the generated implementation, omit call to parent function
    // and implemented 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 he start address of rect,
    // use advanceFrameBufferToRect(uint8_t* fbPtr, const touchgfx::Rect& rect)
    // defined in TouchGFXGeneratedHAL.cpp
	uint8_t *fbPtr = reinterpret_cast<uint8_t*>(HAL::frameBuffer0);
	fbPtr += rect.y * lcd().framebufferStride() + rect.x * 2;
 
	ST7735_TouchFrameBuffer(rect.x, rect.y, rect.width, rect.height, fbPtr);
}

The engine is being queued every 30ms.

extern "C"
void TE_Handler(void)
{
	/* VSync has occurred, increment TouchGFX engine vsync counter */
	touchgfx::HAL::getInstance()->vSync();
	/* VSync has occurred, signal TouchGFX engine */
	touchgfx::OSWrappers::signalVSync();
}

Tried several different ideas on transferring the buffer. From the documentation, I've implemented the following:

void ST7735_TouchFrameBuffer(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t *data)
{
	 __IO uint16_t* ptr;
	 uint32_t height;
 
	 ST7735_Select();
	 ST7735_SetAddressWindow(x, y, x + w - 1, y + h - 1);
 
	 for (height = 0; height < h ; height++)
	 {
		 ptr = data + x + (height + y) * 128;
		 ST7735_WriteData((uint8_t*) ptr, sizeof(uint16_t) * w);
	 }
 
	 ST7735_Unselect();
}

And what I get is:

0693W00000Y8oKSQAZ.jpg 

3 REPLIES 3
MM..1
Chief II

Simply you need understand more steps

  1. How LCD you use WxHxRGBtype vs speed of transfer data = how size?
  2. SPI displays have own memory then most effective is use partial buffer strategy = how strategy ?
/* ******************************************************
 * Functions required by Partial Frame Buffer Strategy
 * ******************************************************
 *
 *  int touchgfxDisplayDriverTransmitActive() must return whether or not data is currently being transmitted, over e.g. SPI.
 *  void touchgfxDisplayDriverTransmitBlock(const 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 touchgfxDisplayDriverTransmitBlock() has successfully sent a block.
 *  E.g. if using DMA to transfer the block, this could be called in the "Transfer Completed" interrupt handler.
 *
 */
void TouchGFXGeneratedHAL::flushFrameBuffer(const touchgfx::Rect& rect)
{
    HAL::flushFrameBuffer(rect);
    // Once flushFrameBuffer() is called by the framework a block is already 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 (!touchgfxDisplayDriverTransmitActive())
    {
        touchgfx::Rect r;
        // Get pointer to block buffer and coordinates of the rect
        const uint8_t* pixels = frameBufferAllocator->getBlockForTransfer(r);
        // Start transmission of the block
        touchgfxDisplayDriverTransmitBlock((uint8_t*)pixels, r.x, r.y, r.width, r.height);
    }
}

howto get pixels data in generated code

and transfer block in your code

volatile uint8_t IsTransmittingBlock_;
void UNIMM_dmacallback(void)
{
	IsTransmittingBlock_ = 0;
	DisplayDriver_TransferCompleteCallback();
}
 
void Display_Bitmap(const uint16_t *bitmap, uint16_t posx, uint16_t posy, uint16_t sizex, uint16_t sizey)
{
  IsTransmittingBlock_ = 1;
  GC9A01A_SetWindow(posx, posy, posx+sizex-1, posy+sizey-1);
 // for (uint32_t i = 0; i < sizey; i++)
//	  GC9A01A_RamWrite(bitmap+(i*sizex), sizex);
//  IsTransmittingBlock_ = 0;
  // Signal Transfer Complete to TouchGFX
//  DisplayDriver_TransferCompleteCallback();
//DMA test
  HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET);
  SPI_send_dma(GC9A01A_SPI_periph, 1, (uint8_t *)bitmap, sizex*sizey, UNIMM_dmacallback);
}

I'm using a single frame buffer. Display resolution is 160 x 128 which is around 40kb for a single frame buffer. I'm working on a L4 MCU that has 96kb of sram. My thinking was that a single frame buffer would be faster and easier to implement. Is that not correct?

40kb frame buffer

SPI bus is 15MHz

Transfer of the whole buffer is less than the timer callback.

from your data refresh is around 45Hz cca 22ms (for animation) TGFX is based on 60Hz

If you dont use DMA then your MCU do 22ms only SPI transfer

Yes you can use single buffer if used ram is ok. Generate code for custom driver and implement described func in target files. I mean here TGFX too use rect for send data, only diff from partial is , that rect can be full screen.

And in your code primary error i see this

uint8_t *fbPtr = reinterpret_cast<uint8_t*>(HAL::frameBuffer0);
fbPtr += rect.y * lcd().framebufferStride() + rect.x * 2;