Skip to main content

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:

Configuring font layout

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 FormatUnmapped Storage Format
GlyphNodes in glyphs_verdana_20_4bppInternalExternal
Pixel data in unicodes_verdana_20_4bpp_0ExternalExternal
Unicodes in unicodelist_verdana_20_4bppnot usedInternal

Example

Below is a screen shot of an application using the new font layout:

Example application with 4000 Chinese characters

This application runs on a STM32G071 Nucleo board with a MB1642A display module:

Example application running on STM32G071 Nucleo

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):

TableLocationSize
GlyphNodesExternal SPI flash57.372 bytes
Pixel patternsExternal SPI flash3.116.296 bytes
Unicode listInternal flash8.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).