Using Non-Memory Mapped Flash for Font Data
In this section we will discuss how to use the unmapped font storage format that will allow you to put almost all font data into unmapped external flash. The effect of using this storage format is that you can have many thousand letters in an application and use only 10 KB of internal flash for font data.
Font Layouts
TouchGFX supports two different font layouts for the fonts compiled into your application. The layout used is selected in TouchGFX Designer in the configurations tab:
The Mapped storage format
is the default font layout and should be
used on systems where fonts are stored in memory mapped flash
(internal or e.g. external QSPI flash).
The Unmapped storage format
is the new font layout. It allows most
of the font data to be stored in unmapped flash. This will typically
be a SPI-flash, but can be any type of storage.
Mapped Storage Format
The mapped storage format keeps the font data in two tables.
The first table is an array of touchgfx::GlyphNode. These contain the properties of the individual characters: height, width, Unicode, and similar.
generated/fonts/src/Table_verdana_20_4bpp.cpp
FONT_TABLE_LOCATION_FLASH_PRAGMA
KEEP extern const touchgfx::GlyphNode glyphs_verdana_20_4bpp[] FONT_TABLE_LOCATION_FLASH_ATTRIBUTE =
{
{ 0, 0x0020, 0, 0, 0, 0, 7, 0, 0, 0x00 },
{ 0, 0x002C, 5, 7, 3, 1, 7, 0, 2, 0x00 },
{ 21, 0x0030, 11, 14, 14, 1, 13, 0, 0, 0x00 },
{ 105, 0x0032, 11, 14, 14, 1, 13, 0, 0, 0x00 },
{ 189, 0x0033, 11, 14, 14, 1, 13, 0, 0, 0x00 },
{ 273, 0x0034, 12, 14, 14, 0, 13, 0, 0, 0x00 },
...
}
The second table (split in multiple files for large fonts) contains the pixel-patterns for the characters.
generated/fonts/src/Font_verdana_20_4bpp_0.cpp
FONT_GLYPH_LOCATION_FLASH_PRAGMA
KEEP extern const uint8_t unicodes_verdana_20_4bpp_0[] FONT_GLYPH_LOCATION_FLASH_ATTRIBUTE =
{
// Unicode: [0x0020]
// (Has no glyph data)
// Unicode: [0x002C]
0x00, 0x87, 0x04, 0x20, 0xFF, 0x03, 0x60, 0xBF, 0x00, 0xA0, 0x5F, 0x00, 0xE0, 0x0D, 0x00, 0xF3,
0x07, 0x00, 0xF6, 0x01, 0x00,
// Unicode: [0x0030]
0x00, 0xA3, 0xFE, 0x9D, 0x01, 0x00, 0x40, 0xFF, 0x9B, 0xFC, 0x1D, 0x00, 0xD0, 0x4F, 0x00, 0x80,
0x9F, 0x00, 0xF3, 0x0B, 0x00, 0x10, 0xEE, 0x00, 0xF7, 0x07, 0x00, 0x00, 0xFB, 0x03, 0xF9, 0x06,
...
}
The GlyphNodes will be used by the TouchGFX engine during text layout. The pixels will be read by the DMA2D or software routines during drawing.
On platforms using the normal LCD classes, e.g. LCD16bpp or LCD24bpp, these tables must be stored in internal flash or memory mapped external flash.
On platforms using the LCD16bppSerialFlash, the rendering software can read the pixels-patterns from unmapped serial flash, but the GlyphNodes table must be in internal flash (it is searched directly).
This layout uses 14 bytes in internal flash for each character.
Unmapped Storage Format
The unmapped storage format splits the font data into three tables. The two tables from the mapped storage layout is reused, but a third table is added:
generated/fonts/src/Table_verdana_20_4bpp.cpp
FONT_SEARCHTABLE_LOCATION_FLASH_PRAGMA
KEEP extern const uint16_t unicodelist_verdana_20_4bpp[] FONT_SEARCHTABLE_LOCATION_FLASH_ATTRIBUTE =
{
0x0020,
0x002E,
0x003F,
0x004E,
0x0054,
....
}
This third table just contains the unicodes present in the font.
When this font layout is used, the third table must be present in internal flash, but both the two other tables can be moved to external flash. This is a considerable saving, as the third table uses two bytes for each character, whereas the GlyphNode table uses 14 bytes. This reduces the storage requirement in internal flash.
Font Data Reader
When the font data is placed in unmapped flash, the mcu cannot access
it directly. We therefore have to provide a flash reader object to the
font subsystem.
The code for this is automatically generated by TouchGFX Generator:
TouchGFXConfiguration.cpp
static TouchGFXDataReader dataReader;
static LCD16bppSerialFlash display(dataReader);
static ApplicationFontProvider fontProvider;
static Texts texts;
static TouchGFXHAL hal(dma, display, tc, 240, 320);
void touchgfx_init()
{
Bitmap::registerBitmapDatabase(BitmapDatabase::getInstance(), BitmapDatabase::getInstanceSize());
TypedText::registerTexts(&texts);
Texts::setLanguage(0);
hal.setDataReader(&dataReader);
fontProvider.setFlashReader(&dataReader);
...
If you are not using the generator, you must do this manually.
Remember to implement the functions in TouchGFXDataReader so data is actually read from your flash.
Summary
Font data is saved in two or three tables. The table below shows the flash placement:
Table (example names) | Mapped Storage Format | Unmapped Storage Format |
---|---|---|
GlyphNodes in glyphs_verdana_20_4bpp | Internal | External |
Pixel data in unicodes_verdana_20_4bpp_0 | External | External |
Unicodes in unicodelist_verdana_20_4bpp | not used | Internal |
Example
Below is a screen shot of an application using the new font layout:
This application runs on a STM32G071 Nucleo board with a MB1642A display module:
In this application we have 4000 Chinese characters in size 20, 4 bits pr pixel. The application and data takes up 61 Kb of 128 Kb available on the STM32G071. The font data is distributed as follows (excluding minor objects):
Table | Location | Size |
---|---|---|
GlyphNodes | External SPI flash | 57.372 bytes |
Pixel patterns | External SPI flash | 3.116.296 bytes |
Unicode list | Internal flash | 8.000 bytes |
Linker Script Modifications
To use the unmapped font layout correctly, you must update your linker script to place the tables correctly.
STM32F746.ld
define symbol __ICFEDIT_region_ROM_start__ = 0x08000000;
define symbol __ICFEDIT_region_ROM_end__ = 0x0801FFFF;
define symbol __ICFEDIT_region_RAM_start__ = 0x20000000;
define symbol __ICFEDIT_region_RAM_end__ = 0x20008FFF;
define symbol __ICFEDIT_region_SERIAL_FLASH_start__ = 0x90000000;
define symbol __ICFEDIT_region_SERIAL_FLASH_end__ = 0x91000000;
place in ROM_region { readonly };
place in RAM_region { readwrite,
block CSTACK, block HEAP };
place in SERIAL_FLASH_region {section ExtFlashSection, section FontFlashSection };
In this linker script we put both the ExtFlashSection (images and font pixels) and FontFlashSection (the GlyphNodes) in the external flash. Any other read-only data is in the internal flash (ROM_region).