SPI
The following scenario shows, generally, the steps involved in creating a TouchGFX driver when selecting "Custom" display interface in the TouchGFX generator using SPI. The steps involved are identical to that of other display interfaces where the MCU lacks an embedded display controller.
Once the SPI peripheral is configured in STM32CubeMX the TouchGFX Generator can be used to generate a HAL, using a "custom" display interface, that allows developers to transfer the updated parts of the framebuffer to a connected display using a proprietary driver that developers write themselves. Figure X shows a TouchGFX Generator configuration in which selecting Custom display interface will allow the generated code to support the transfer of the updated parts of the framebuffer to the display.
Generally, for displays with embedded GRAM such as 8080 or SPI displays, the driver is structured as follows:
- Based on area of the framebuffer to be redrawn, move the "display cursor" to a place in GRAM that matches this.
- Prepare to write incoming pixel data to GRAM.
- Send pixel data.
Transfering the framebuffer
When an area of the framebuffer has been updated, the TouchGFX Engine will call HAL::flushFrameBuffer
.
React to signal (S?) from TouchGFX Engine.
void TouchGFXHAL::flushFrameBuffer(const Rect& rect)
{
/* Set Cursor */
__ST7789H2_SetDisplayWindow(rect.x, rect.y, rect.width, rect.height);
/* Prepare to write to LCD RAM */
ST7789H2_WriteReg(ST7789H2_WRITE_RAM, (uint8_t*)NULL, 0);
/* Send Pixels */
this->copyFrameBufferBlockToLCD(rect);
}
The following function __ST7789H2_SetDisplayWindow
sets the x
and y
coordinates for the virtual "cursor" in GRAM by writing to specific registers, which is usual for displays using GRAM.
extern "C"
void __ST7789H2_SetDisplayWindow(uint16_t Xpos, uint16_t Ypos, uint16_t Width, uint16_t Height)
{
uint8_t parameter[4];
/* CASET: Column Address Set */
parameter[0] = 0x00;
parameter[1] = Xpos;
parameter[2] = 0x00;
parameter[3] = Xpos + Width - 1;
ST7789H2_WriteReg(ST7789H2_CASET, parameter, 4);
/* RASET: Row Address Set */
parameter[0] = 0x00;
parameter[1] = Ypos;
parameter[2] = 0x00;
parameter[3] = Ypos + Height - 1;
ST7789H2_WriteReg(ST7789H2_RASET, parameter, 4);
}
The following function TouchGFXHAL::copyFrameBufferBlockToLCD
is a private function that sends one line of the updated area (Rect
) at a time, ensuring to progress the framebuffer pointer accordingly.
void TouchGFXHAL::copyFrameBufferBlockToLCD(const Rect rect)
{
__IO uint16_t* ptr;
uint32_t height;
// Use default implementation (CPU copy!).
// This can be accelerated using regular DMA hardware
for (height = 0; height < rect.height ; height++)
{
ptr = getClientFrameBuffer() + rect.x + (height + rect.y) * BSP_LCD_GetXSize();
LCD_IO_WriteMultipleData((uint16_t*)ptr, rect.width);
}
}
Returning from flushFrameBuffer
Once the function returns, TouchGFX Engine returns to its. If developers desire to use DMA to perform these transfers, then they must ensure that HAL::flushFrameBuffer(Rect& rect)
does not return by waiting on a semaphore that is signaled by a DMA Completed interrupt.