跳轉到主要內容

FMC與SPI顯示介面

下面的場景(通常)顯示將像素轉移到連接到FMC或通過SPI連接的LCD的步驟;兩個方法共用一些元素。 本節描述的方案以ST7789H2LCD控制器為例。

一旦根據STM32CubeMX中的開發件規範配置FMC或SPI,TouchGFX Generator可以用於生成TouchGFX HAL,允許開發人員編寫程式碼,將應用程式影像緩衝器的更新部分傳輸到連接的顯示終端。

下圖顯示了選擇客製化顯示介面時的TouchGFXGenerator設定。 該配置會向TouchGFXGenerator指示,開發人員希望完成相關顯示配置後手動將像素從影像緩衝區傳輸到顯示器,並生成用於完成該操作的處理器。

TouchGFXGenerator設定

Tip
對於通過SPI連接的顯示器,必須選擇自訂顯示介面

通常,對於內嵌GRAM的顯示器,用戶在生成的TouchGFX HAL中編寫的程式碼應該執行以下步驟:

  1. 根據要重繪的影像緩衝區,將「顯示游標」和「活動視窗」移動到GRAM中與此區域對應的位置。
  2. 準備將傳入的像素資料寫入GRAM。
  3. 傳送像素資料。

傳輸影像緩衝區內容

在影像緩衝區內容被更新後,TouchGFXEngine會呼叫HAL::flushFrameBuffer(Rectr)。 當開發人員必須實現程式碼以手動將像素轉移到顯示器(例如,用到SPI和FMC)時,該函數可以被覆寫。 我們將看到,通過FMC存儲塊傳遞像素的函數是由TouchGFX Generator生成的。

Note
本節中顯示的ST7789H2驅動程式碼將在開發板搭建階段時開發,一旦驅動成功工作,就可以通過TouchGFXGenerator複製到HAL類別中。

驅動程式必須能夠將像素傳輸到顯示器,並可以控制顯示器的記憶體寫入位置。 如需進一步的詳細資訊,請查看顯示器的資料手冊。

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 - User defined function */
this->copyFrameBufferBlockToLCD(rect);
}

以下ST7789H2_SetDisplayWindow函數用以寫入特定暫存器來設定GRAM中虛擬「游標」的xy座標,對於使用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);
}

在本例中,以下TouchGFXHAL::copyFrameBufferBlockToLCD函數是一個私有函數,該函數一次發送一行更新區域(Rect),以確保相應地推進影像緩衝器指標。

void TouchGFXHAL::copyFrameBufferBlockToLCD(const Rect& rect)
{
__IO uint16_t* ptr;
uint32_t height;

// This can be accelerated using regular DMA hardware
for (height = 0; height < rect.height ; height++)
{
ptr = getClientFrameBuffer() + rect.x + (height + rect.y) * HAL::DISPLAY_WIDTH;
LCD_IO_WriteMultipleData((uint16_t*)ptr, rect.width);
}
}

TouchGFXGenerator將產生一個函數advanceFrameBufferToRect,並根據Rect在影像緩衝區中的位置來更新ptr指標,而不用手動更新ptr指標。

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;
}

FMC

TouchGFX Generator也支援FMC顯示介面,如果至少一個FMC Bank配置正確。 本例中,TouchGFX Generator生成的程式碼與自訂顯示介面的程式碼類似,除了生成了LCD_IO_WriteMultipleData函數,與連接到顯示器上的FMC存儲塊進行交互。 重新查看前面為copyFrameBufferBlockToLCD函數提供的程式碼,您將看到它使用了生成的函數。

Tip
對於SPI和FMC顯示介面,開發人員將修改flushFrameBuffer() 函數為:1) 設置游標 2) 準備寫入GRAM 3) 通過自訂SPI顯示驅動或通過生成的FMC Bank函數傳輸像素。
    __weak void LCD_IO_WriteMultipleData(uint16_t* pData, uint32_t Size)
{
uint32_t i;

for (i = 0; i < Size; i++)
{
FMC_BANK1_WriteData(pData[i]);
}
}

下圖顯示了一個有效的16位(必需的)FMC存儲塊2的配置(兩者都可以使用)。

FMC Bank配置

一旦實現了有效配置,可以在TouchGFX Generator中選擇該存儲塊。 驗證您的MCU的FMC存儲塊暫存器的起始位址。

FMC介面選擇

TouchGFX Generator驗證FMC存儲塊的配置,並報告它可能發現的任何問題。

FMC Configuration錯誤

從HAL::flushFrameBuffer()回傳

在函數返回後,TouchGFXEngine繼續繪製剩餘的內容。 如果開發人員希望使用DMA將像素傳輸到顯示器,他們必須透過等待DMA完成中斷所發出的semaphore訊號來確保HAL::flushFrameBuffer(Rect& rect)不會立即傳回。

以下的偽程式範例示範了在使用DMA的情況下如何建構HAL::flushFrameBuffer()。 此程式碼使用了FreeRTOS的screen_frame_buffer_sem

void TouchGFXHAL::flushFrameBuffer(const touchgfx::Rect& rect)
{
uint16_t* fb = HAL::lockFrameBuffer();

//Prepare display: Set cursor, write to display gram as described previously in this scenario

//Try to take a display semaphore - Always free at this point
xSemaphoreTake(screen_frame_buffer_sem, portMAX_DELAY);

//Set up DMA
screenDMAEnable();

// Wait for the DMA transfer to complete
xSemaphoreTake(screen_frame_buffer_sem, portMAX_DELAY);

//Unlock framebuffer and give semaphore back
HAL::unlockFrameBuffer();
xSemaphoreGive(screen_frame_buffer_sem);
}
Caution
由TouchGFX Generator生成的FMC程式碼不使用DMA。

TouchGFX驅動程式/撕裂效果信號

從上面的TouchGFXGenerator配置中可以看出,應用中的「滴答計時源」也被設為「客制」,對於不內置TFT控制器的MCU來說,這也算是常見設置。

如抽象層架構部分所述,通常在發出顯示訊號時,用OSWrappers::signalVSync()來解除TouchGFXEngine主迴圈阻塞。

對有串列或8080顯示介面的顯示器,內建顯示控制器通常會產生一個週期性撕裂效果(TE)信號,該訊號可以連接到MCU上的GPIO。 在這種情況下,通常將MCU配置為當GPIO收到該信號時觸發中斷。 然後,該「撕裂效果」中斷將解除對TouchGFXEngine主循環的阻塞,以便渲染下一幀。 請記住將GPIO配置為輸入,並在STM32CubeMX中使能該腳的外部中斷。

extern "C"
void TE_Handler(void)
{
...
/* Unblock TouchGFX Engine Main Loop to render next frame */
OSWrappers::signalVSync();
...
}

結論

開發人員可以透過TouchGFXGenerator選擇客製化顯示介面,並自行編寫程式碼實現將像素從應用程式的影像緩衝區傳輸到顯示器之目的。

TouchGFXGenerator將產生functionTouchGFXHAL::flushFrameBuffer(Rect& rect)函數,當渲染完成影像緩衝區的一塊區域後,TouchGFX會自動呼叫該函數,同時,開發人員將更新過的影像緩衝區資料透過FMC、SPI或其他途徑傳送到顯示器。 無論如何,在這兩種情況下都必須完成以下步驟:

  1. 根據要重繪的影像緩衝區,將「顯示游標」和「活動視窗」移動到GRAM中與此區域對應的位置。
  2. 準備將傳入的像素資料寫入GRAM。
  3. 傳送像素資料。 如果是FMC顯示介面,該函數就是為您生成的,可以在flushFrameBuffer(rect & rect)中使用((參見本文前面的內容)。

選擇客製化FMC顯示介面也需要開發人員實現客製的TouchGFX滴答計時驅動,該驅動程式發出OSWrappers::signalVSync()信號,以解除對TouchGFXEngine主迴圈的阻塞。 通常,與不帶TFT控制器的MCU一起使用的顯示器會提供連接至MCU的撕裂效果信號。