跳轉到主要內容

使用串列快閃記憶體存儲圖像和字體

本節將討論如何使用串列快閃記憶體(或其他未映射記憶體)來存儲圖像和字體。 這裡介紹的技術在STM32G0和其他配備極小RAM的設備上特別有用。

有關部分影像緩衝區(通常與串列快閃記憶體一起使用)的介紹,請參閱文章“使用部分影像緩衝區降低記憶體使用”。 另請參閱文章使用非記憶體映射存儲圖像,瞭解如何將點陣圖從未映射Flash快取到RAM。

配置

為了讓TouchGFX應用使用串列快閃記憶體,您必須更改TouchGFX Generator的配置,啟用“Additional Features”中的“External Data Reader”。

其它功能:數據讀取器

啟用此功能後,TouchGFX Generator更改配置,以便使用LCD16bppSerialFlash LCD類。 它還生成touchgfx::FlashDataReader的子類:

TouchGFXConfiguration.cpp
static TouchGFXDataReader dataReader;
static LCD16bppSerialFlash display(dataReader);
static ApplicationFontProvider fontProvider;
...
void touchgfx_init()
{
...
hal.setDataReader(&dataReader);
fontProvider.setFlashReader(&dataReader);
...
}

此程式碼創建TouchGFXDataReader類的實例,並將該實例傳遞給顯示器物件、HAL物件和ApplicationFontProvider物件。 這三個物件將使用dataReader物件訪問串列快閃記憶體中的資料。 這些資料可以是圖像和字體資料。

系統程式師必須完成TouchGFXDataReader類的實現,才能真正從快閃記憶體中讀取資料。

實現

TouchGFXDataReader類實現touchgfx::FlashDataReader介面。 該介面有下列4種方法,需要在特定硬體上實現。

include/touchgfx/hal/FlashDataReader.hpp
    bool addressIsAddressable(const void* address)
void copyData(const void* src, void* dst, uint32_t bytes)
void startFlashLineRead(const void* src, uint32_t bytes)
const uint8_t* waitFlashReadComplete()

LCD16bppSerialFlash類使用addressIsAddressable法來決定某些資料是否可以直接讀取(即位於內部RAM或內部快閃記憶體中)或是否應通過dataReader物件讀取。

copyData*法用於從快閃記憶體同步複製資料到RAM。 當不對資料進行進一步處理時,常使用此函數。 如 將立體影像複製到影像緩衝時。

當需要從快閃記憶體獲取多行資料時,使用startFlashLineRead法。 startFlashLineRead法開始資料讀取。 此方法可以開始非同步讀取,並且應在開始讀取後立即返回。 waitFlashReadComplete法應等待讀取完成,並返回指向保存資料的緩衝區的指標。

LCD16bppSerialFlash可以在處理之前讀取的資料時發起一次快閃記憶體讀取(在某些情況下)。 這意味著dataReader中需要至少兩個緩衝區來實現。

TouchGFX Generator生成分別屬於兩個類的FlashDataReaderTouchGFXGeneratedDataReaderTouchGFXDataReaderTouchGFXGeneratedDataReader是二者的子類,包含默認實現。 如果該執行不合適,應用程式師可以更改TouchGFXDataReader類中虛擬函數的動作。

TouchGFXGeneratedDataReader實現呼叫C函數來完成工作。 這些應用由系統程式師實現。

TouchGFX/target/generated/TouchGFXGeneratedDataReader.cpp
extern "C" __weak void DataReader_WaitForReceiveDone();
extern "C" __weak void DataReader_ReadData(uint32_t address24, uint8_t* buffer, uint32_t length);
extern "C" __weak void DataReader_StartDMAReadData(uint32_t address24, uint8_t* buffer, uint32_t length);

void TouchGFXGeneratedDataReader::startFlashLineRead(const void* src, uint32_t bytes)
{
/* Start transfer using DMA */
DataReader_StartDMAReadData((uint32_t)src, (readToBuffer1 ? buffer1 : buffer2), bytes);
}

實現位於MB1642BDataReader.c文件中:

Core/Src/MB1642BDataReader.c
void DataReader_StartDMAReadData(uint32_t address24, uint8_t* buffer, uint32_t length)
{
readDataDMA(address24, buffer, length);
}

void readDataDMA(uint32_t address24, uint8_t* buffer, uint32_t length)
{
// Pull Flash CS pin low
isReceivingData = 1;
FLASH_CS_GPIO_Port->BRR = FLASH_CS_Pin;

*((__IO uint8_t*)&hspi2.Instance->DR) = CMD_READ;

...
}

此實現特定於快閃記憶體使用的協議以及SPI和CS使用的GPIO。 必須實現TouchGFXGeneratedDataReader類的全部三個C函數才能發揮作用。

圖像

如簡介中所述,LCD16bppSerialFlash類可通過dataReader物件讀取圖像像素。 為此,必須更改連結器腳本,以便將圖像的地址放在內部快閃記憶體範圍之外。

在STM32G071上,我們選擇了位址0x90000000作為串列快閃記憶體的起始位址:

gcc/STM32G071RBTX_FLASH.ld
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 36K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128K
SPI_FLASH (r) : ORIGIN = 0x90000000, LENGTH = 8M
}

/* Sections */
SECTIONS
{
...

ExtFlashSection :
{
*(ExtFlashSection ExtFlashSection.*)
*(.gnu.linkonce.r.*)
. = ALIGN(0x4);
} >SPI_FLASH

FontFlashSection :
{
*(FontFlashSection FontFlashSection.*)
*(.gnu.linkonce.r.*)
. = ALIGN(0x4);
} >SPI_FLASH
}

這會將ExtFlashSectionFontFlashSection放入0x90000000地址範圍。

剩餘的任務是用Flasher工具將資料寫入外部快閃記憶體。

關於STM32CubeProgrammer的寫入快閃記憶體載入器的簡要說明,可以在以下文檔的第2.3.3節中找到:UM2237 STMCubeProgrammer用戶手冊

字體資料

以上連結器腳本將字體像素資料和字體字元的中繼資料(寬和高)放入外部快閃記憶體(兩類資料都在FontFlashSection中)。 在螢幕上繪製字元時,也通過dataReader物件讀取此資料

如果您未使用“未映射存儲格式”,則必須更改連結器腳本和檔案include/touchgfx/hal/Config.hpp,以便將字體字元中繼資料移動到內部快閃記憶體。

參見未映射存儲中的字體一文獲取關於字體格式的更多資訊。