跳转到主要内容

使用非内存映射闪存存储字体数据

本节将讨论如何使用新的字体布局,以便将几乎所有字体数据存入非内存映射的外部闪存。 这样一来,应用只使用50 kb的内部闪存就可以保存数千字母。

字体布局

对于编译到应用中的字体,TouchGFX支持两种不同的字体布局。 可在TouchGFX Designer的配置选项卡中选择要使用的布局:

配置字体布局

映射存储格式是默认字体布局,当字体数据存放在内存映射闪存(内部或外部QSPI闪存等)中时,应当使用映射存储格式。

非映射存储格式是新的字体布局。 它可以将大多数字体数据存储在非映射闪存中。 通常如SPI闪存,但也可以是任何类型的存储。

映射存储格式

映射存储格式将字体数据保存在两个表中。

第一个表是一个touchgfx::GlyphNode数组。 其中包含各个字符的属性:高、宽和unicode编码等。

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 },
...
}

第二个表(比较大的字体会拆分为多个文件)包含字符的像素样式数据。

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,
...
}

在文本布局过程中,TouchGFX引擎将使用GlyphNodes。 在绘图过程中,由DMA2D或软件程序读取像素数据。

在使用普通LCD类(如LCD16Bpp或LCD24Bpp)的平台上,必须将这些表保存在内部闪存或存储器映射的外部闪存中。

在使用外部非映射闪存的平台上,LCD16BppSerialFlash可从非映射串行闪存读取像素样式数据,但GlyphNodes必须位于内部闪存中。

非映射存储格式

非映射存储格式将字体数据保存在三个表中。 复用了来自映射存储布局的两个表,但增加了第三个表:

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,
....
}

这第三个表只包含字体中出现字符的unicode编码。

在使用此字体布局时,第三个表必须位于内部闪存中,但可以将另外两个表移动到外部闪存中。 由于第三个表对每个字符使用两个字节,而GlyphNode表使用14个字节,因此大大节约了存储空间。 这可以降低对内部闪存的存储需求。

当字体数据位于非映射闪存中时,Mcu不能直接访问它。 因此,我们必须为字体子系统提供对象用于读取数据。
相关代码由TouchGFXGenerator自动生成:

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);
...

如果您不使用生成器,则必须手动操作。

注意,应实现TouchGFXDataReader中的函数,从而实际上从闪存中读取字体数据。

示例

下面是使用新字体布局的应用的屏幕截图:

具有4000个中文字符的应用示例

此应用运行在STM32G071 Nucleo板上,并使用了MB1642A显示模块。

在STM32G071Nucleo板上运行的应用示例

此应用包含4000个中文字符,字体大小为20,每像素为4bit。 STM32G071上总共有128Kb可用存储空间,应用和数据共占用了61Kb, 字体数据分配如下(不包括次要对象):

位置大小
GlyphNodes外部SPI闪存57.372字节
像素模式外部SPI闪存3.116.296字节
Unicode列表内部闪存8.000字节

链接脚本修改

为了正确地使用未映射字体布局,您必须更新链接脚本,以便正确地放置表格。

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

在该链接脚本中,我们将ExtFlashSection(图像和字体像素)和FontFlashSection(GlyphNodes)都放在外部闪存中。 任何其他只读数据都位于内部闪存(ROM_region)中。