跳转到主要内容

使用非内存映射闪存存储图像

本节将讨论如何将所有图像链接到二进制文件,这样可以放入非内存映射闪存,以及如何在运行时结合位图缓存来使用该文件。 TouchGFX不能绘制存储在非内存映射闪存中的位图,但是通过将位图缓存在RAM中,就能在应用中使用位图。

参见缓存位图一文中关于位图缓存的综合讨论。

在本文中,我们假设您设置了位图缓存,并希望将位图存储在非内存映射闪存中。 该闪存可以是USB存储、NAND flash等。

目标是将图像链接到特定地址,将图像复制到文件,并帮助TouchGFX将其从文件复制到缓存。

将闪存中的位图数据复制到缓存

注意,在缓存位图时,TouchGFX会将像素从原始位置复制到位图缓存。

通过从HAL类调用此方法来完成复制:

HAL.hpp
bool HAL::blockCopy(void* RESTRICT dest, const void* RESTRICT src, uint32_t numBytes);

如果位图存储在普通可寻址闪存(如内部闪存或内存映射外部闪存)中,则使用TouchGFX库中提供的普通blockCopy函数即可,您无需任何操作。

另一方面,如果位图存储在不可寻址的存储位置(如文件系统),则普通实现是不够的,您需要提供更新过的版本,以便能够从特定闪存读取。

但是,首先我们需要确保位图已链接到固定地址。

BitmapDatabase表

TouchGFX中的所有位图都会生成.cpp文件,存放到generated/images/src文件夹中。 在这里,位图被表示为字节数组。

与其它源码文件一样,这些位图数组会被C++编译器链接到应用中。

这是一个简单应用的屏幕截图,该应用包含一个按钮和一个Texture Mapper控件,其中Texture Mapper控件用来显示一个旋转的徽标:

按钮和纹理映射器

该应用使用3张图像:Button_Pressed、Button_Released和Logo。

这3张位图被转换为.cpp文件,并链接成应用的一部分。 在名为bitmap_database的表中引用位图。 此表位于文件BitmapDatabase.cpp中。 下表来自上面的示例(删除了一些细节):

BitmapDatabase.cpp
extern const unsigned char _blue_buttons_round_edge_small[];
extern const unsigned char _blue_buttons_round_edge_small_pressed[];
extern const unsigned char _blue_logo_touchgfx_logo[];

const touchgfx::Bitmap::BitmapData bitmap_database[] =
{
{ _blue_buttons_round_edge_small, ... },
{ _blue_buttons_round_edge_small_pressed, ... },
{ _blue_logo_touchgfx_logo, ... }
};

首先声明的数组是包含各位图的像素数据的数组。 这些数组在其他.cpp文件中定义。 bitmap_database数组保存了这些数组的地址。 TouchGFX使用此数组查找位图像素的地址。

当程序员请求缓存位图时,TouchGFX查找闪存中的位图地址(在bitmap_database数组中)并从这里复制数据。

链接脚本修改

链接器为位图选择地址。 此类选择基于位图所在的段。 TouchGFX中的所有位图默认放入ExtFlashSection中。

标准链接脚本(此处是就GCC而言)将此段连同其他只读数据一起放入闪存。

在本例中,我们将图像数据放入ExtFlashSection中,地址为0x24000000。 您可以选择未在其他地方使用的任何地址(不是代码或数据地址空间的一部分)。

首先,除了普通的内部FLASH和RAM,我们定义一个新的存储区(USB闪存地址0x24000000):

STM32F746.ld
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
USB(r) : ORIGIN = 0x24000000, LENGTH = 1M
}

然后,我们通过链接脚本将ExtFlashSection放入USB区:

STM32F746.ld
ExtFlashSection :
{
*(ExtFlashSection ExtFlashSection.*)
} >USB

在链接成功后,可通过检查.map文件(application.map)来检查位图地址。 下面是相关部分:

application.map
ExtFlashSection
0x24000000 0x23ec0
*(ExtFlashSection ExtFlashSection.*)
ExtFlashSection
0x24000000 0x10000 TouchGFX/build/.../Blue_Logo_touchgfx_logo.o
0x24000000 _blue_logo_touchgfx_logo
ExtFlashSection
0x24010000 0x9f60 TouchGFX/build/.../Blue_Buttons_Round_Edge_small.o
0x24010000 _blue_buttons_round_edge_small
ExtFlashSection
0x24019f60 0x9f60 TouchGFX/build/.../Blue_Buttons_Round_Edge_small_pressed.o
0x24019f60 _blue_buttons_round_edge_small_pressed

在这里我们可以看到,图像的总大小为0x23ec0 = 147,136字节。 保存位图的3个数组的地址从0x24000000开始相继排列。

现在,假设您想要将位图数据存到SD卡中。 我们可以用简单的objcopy指令从.elf文件中提取位图的二进制数据:

$ arm-none-eabi-objcopy.exe --dump-section ExtFlashSection=images.bin TouchGFX/build/bin/target.elf
$ ls -l images.bin
-rw-r--r-- 1 christef Administrators 147136 Feb 20 11:56 images.bin

这样可以得到一个只包含图像字节数组的文件(images.bin)。 此文件可以复制到USB闪存、SD卡甚至编程到闪存芯片。

现在要做的是,当TouchGFX请求地址0x24000000以上的数据时,我们从SD卡上的images.bin文件获取数据。

修改BlockCopy函数

记住,在将位图缓存到RAM时,TouchGFX调用HAL::BlockCopy以获取数据。

为了关联上SD卡中的数据,我们可以在特定HAL类中实现新的BlockCopy。 这里我们假设类的名称为TouchGFXHAL(由TouchGFX Generator生成):

TouchGFXHal.hpp
class TouchGFXHAL : public TouchGFXGeneratedHAL
{
public:
...
virtual bool blockCopy(void* RESTRICT dest, const void* RESTRICT src, uint32_t numBytes);
}
TouchGFXHal.cpp
// This function is called whenever a bitmap is cached. 必须从(非内存映射)源//复制多个字节到缓存。
bool TouchGFXHAL::blockCopy(void* RESTRICT dest, const void* RESTRICT src, uint32_t numBytes)
{
// If requested data is located within the memory block we have assigned for ExtFlashSection,
// provide an implementation for data copying.
if (src >= 0x24000000 && src < 0x24100000)
{
void* dataOffset = src - 0x24000000;
// In this example we assume graphics is placed in SD card, and that we have an appropriate function
// for copying data from there.
sdcard_read(dest, dataOffset, numBytes);
return true;
}
else
{
// For all other addresses, just use the default implementation.
// This is important, as blockCopy is also used for other things in the core framework.
return HAL::blockCopy(dest, src, numBytes);
}
}

现在,可以开始从SD卡缓存位图。

如果TouchGFX尝试绘制没有缓存的位图,它将尝试从bitmap_database表中找到的地址读取像素。 此地址将处于范围0x24000000 - 0x24100000以内,直接读取将导致异常。

将图像链接到RAM

如果可用RAM足够大,能够存储所有图像(在上面的例子中,RAM大于147,136字节),则无需使用位图缓存来复制图像。

策略如下:

  • 为图像选择RAM中的固定地址和范围
  • 在链接脚本中删除RAM区中的这一范围
  • 创建具有选定地址和大小的新区IMAGES
  • 将ExtFlashSection放在IMAGES区
  • 链接应用并检查.map文件
  • 从application.elf创建images.bin文件
  • 在启动TouchGFX之前,将整个images.bin文件从SD卡复制到RAM中的选定地址

该解决方案较为简单,但有一些缺点:

  • 可用RAM必须足够大,能够保存所有图像
  • 由于要从SD卡复制,因此启动时间会比较长(兆字节可能需要数秒)