跳转到主要内容

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

本节将讨论如何将所有图像链接到二进制文件,这样可以放入非内存映射闪存,以及如何在运行时结合位图缓存来使用该文件。 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

We can see here that the total size of the images is 0x23ec0 = 147,136 bytes. 保存位图的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

If your available RAM is big enough to hold all the images (in the above example that is more than 147,136 bytes) then you do not need to use the bitmap cache to copy the images.

策略如下:

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

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

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