跳轉到主要內容

影像緩衝區策略

本節介紹如何配置TouchGFX Generator,以產生基於以下影像緩衝區策略的TouchGFX HAL:

  • 單一影像緩衝區
  • 雙影像緩衝區
  • 部份影像緩衝區

點擊這裡閱讀更多關於影像緩衝區的內容。

所有影像緩衝區選項

單一影像緩衝區

選取單一影像緩衝區(Single Frame Buffer)作為緩衝策略,開發人員可以讓編譯器於內部RAM中配置影像緩衝區所需的記憶體,或者也可以自行在內部RAM中指定影像緩衝區的存取位置。

依配置 (By Allocation)

選擇依配置(By Allocation)時,TouchGFX Generator將根據圖形的大小和像素位元深度來配置影像緩衝陣列。

單一影像緩衝區,依配置

產生用於設定HAL的程式碼,並將該陣列用作影像緩衝區。

TouchGFXGeneratedHAL.cpp
namespace {
// Use the section "TouchGFX_Framebuffer" in the linker script
// to specify the placement of the buffer
LOCATION_PRAGMA("TouchGFX_Framebuffer")
uint32_t frameBuf[(480 * 272 * 2 + 3) / 4] LOCATION_ATTRIBUTE("TouchGFX_Framebuffer");
}

void TouchGFXGeneratedHAL::initialize()
{
HAL::initialize();

setFrameBufferStartAddresses((void*)frameBuf, (void*)0, (void*)0);
}

依位址

當為影像緩衝區的位置選擇依位址時,TouchGFX Generator將在HAL初始化階段使用所指定的起始位址。

單一影像緩衝區,依位址

TouchGFXGeneratedHAL.cpp
void TouchGFXGeneratedHAL::initialize()
{
HAL::initialize();

setFrameBufferStartAddresses((void*)0xC0000000, (void*)0, (void*)0);
}

雙影像緩衝

在雙影像緩衝區 (Double Frame Buffer) 配置中,TouchGFX Generator將根據所選取的影像緩衝區策略和顯示介面在HAL中產生用於影像緩衝區切換的程式碼。 在主事件循環中TouchGFX引擎將會用到與影像緩衝區相連的記憶體介面。

依位址

選擇依位址時,TouchGFX Generator將在HAL初始化階段使用兩個指定的起始位址。

雙影像緩衝區,依位址

TouchGFXGeneratedHAL.cpp
void TouchGFXGeneratedHAL::initialize()
{
HAL::initialize();

setFrameBufferStartAddresses((void*)0xC0000000, (void*)0xC003FC00, (void*)0);
}
Tip
將RGB並列顯示(LTDC)作為顯示介面時,起始位址將從LTDC圖層 (layer) 的設定中繼承。
Tip
在“按分配(By Allocation)”模式下使用平行RGB (LTDC) 作為顯示介面時,生成的TouchGFX HAL將在運行時自動配置LTDC層色彩影像緩衝起始位址,因此不應在LTDC配置中設定數值。

依配置 (By Allocation)

選擇依配置(By Allocation) 時,TouchGFX Generator將根據圖形的大小和位元深度來配置陣列大小,這與使用單一影像緩衝區完全一樣,差別只在於配置了兩個相同大小的陣列。

單一影像緩衝區,依配置

TouchGFXGeneratedHAL.cpp
namespace {
// Use the section "TouchGFX_Framebuffer" in the linker to specify the placement of the buffer
LOCATION_PRAGMA("TouchGFX_Framebuffer")
uint32_t frameBuf[(480 * 272 * 2 + 3) / 4 * 2] LOCATION_ATTRIBUTE("TouchGFX_Framebuffer");
}

void TouchGFXGeneratedHAL::initialize()
{
HAL::initialize();

setFrameBufferStartAddresses((void*)frameBuf, (void*)(frameBuf + sizeof(frameBuf)/(sizeof(uint32_t)*2)), (void*)0);
}

局部影像緩衝區

採用部分影像緩衝區策略時,TouchGFX不使用與顯示尺寸相同的影像緩衝區。 相反,用戶可以選擇具有給定尺寸的若干塊作為影像緩衝區。 TouchGFX利用影像緩衝區分配器(Frame Buffer Allocator)來控制這些塊的使用。

使用此策略時,無需提供指向外部記憶體(影像緩衝區所在位置)的指標,也不需要在內部記憶體中分配影像緩衝區陣列。

有關影像緩衝區的概念,請參見有關影像緩衝區的文章。

Tip
通常,類似STM32G0的小型微控制器沒有足夠的內部RAM來容納影像緩衝區。 因此「局部緩衝 (Partial Buffer)」就是這類低成本MCU的完美方案

局部影像緩衝區

由於局部緩衝區通常僅用於無TFT控制器且內部RAM很小的低成本MCU,因此局部緩衝區策略就期望開發人員能完成將緩衝內容傳輸到顯示器的實作。 參見FMC/SPI方案,以瞭解如何將像傳送到不具TFT控制器的MCU所連接的序列傳輸顯示器。

為了在TouchGFX裡使用局部影像緩衝策略連結顯示幕,開發人員需要實作以下兩個功能。 下面顯示的程式碼由TouchGFX/target/generated/TouchGFXGeneratedHAL.cpp中的CubeMX生成,它定義了TouchGFX 引擎和顯示驅動程式之間的介面。

TouchGFXGeneratedHAL.cpp
/* ******************************************************
* 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) 然後使用者必須傳輸參數表示的資料。
*/

extern "C" int touchgfxDisplayDriverTransmitActive();
extern "C" int touchgfxDisplayDriverShouldTransferBlock(uint16_t bottom);
extern "C" void touchgfxDisplayDriverTransmitBlock(const uint8_t* pixels, uint16_t x, uint16_t y, uint16_t w, uint16_t h);
extern "C" void touchgfxSignalVSync(void);

對於TouchGFX Designer中提供的STM32G071 TouchGFX開發板設定,這些功能在Core/Src/MB1642BDisplayDriver.c中實現。(此處僅顯示一個):

Core/Src/MB1642BDisplayDriver.c
int touchgfxDisplayDriverTransmitActive(void)
{
return IsTransmittingBlock_;
}

採用這種策略,CubeMX還在TouchGFX/target/generated/TouchGFXGeneratedHAL.cpp中的唯讀TouchGFXGeneratedHAL類中生成TouchGFXGeneratedHAL::flushFrameBuffer(Rect&)

Note
此flushFrameBuffer(Rect&) 函數通常用於無TFT控制器的MCU。 當使用局部緩衝區時,TouchGFX Generator可以為此緩衝區策略產生一個對應的操作方法之實作定義。
TouchGFXGeneratedHAL.cpp
void TouchGFXGeneratedHAL::flushFrameBuffer(const touchgfx::Rect& rect)
{
HAL::flushFrameBuffer(rect);
// Try transmitting a block
PartialFrameBufferManager::tryTransmitBlock();
}