在前面的文章中,我们详细介绍了智能设备时代一种基于面板的嵌入式界面设计范式,并以Arm-2D的场景播放器为例,介绍了小资源环境下具体实现GUI的方式。
许多开发者反馈了一个关键问题:芯片内部Flash空间紧张时,背景图等资源应该存储在哪里?
一般来说,小资源环境是指内部FLASH空间小于64K,或者虽然芯片Flash较大但应用代码已经非常庞大,留给GUI的空间有限的情况。此时,图片资源需要寻找其他存储方案。
很多芯片会提供XIP外设,通过QSPI接口连接外部SPI Flash芯片,并将其内容映射到4G地址空间中。这样就能像访问内部Flash那样使用外部SPI Flash,不仅可以存储数据,甚至还能执行代码。
如果芯片拥有XIP,那么就不属于资源受限环境,因为大容量的片外Flash既常见又廉价。但如果芯片没有XIP,只能通过外设SPI和自定义驱动来访问外部Flash,该如何使用Arm-2D简化GUI开发过程呢?本文将详细解答这个问题。
什么是虚拟资源
Arm-2D几乎所有API的基本操作单位都是"贴图",其数据结构定义如下:
typedef struct arm_2d_tile_t arm_2d_tile_t;
struct arm_2d_tile_t {
implement_ex(struct {
uint8_t bIsRoot : 1;
uint8_t bHasEnforcedColour : 1;
uint8_t bDerivedResource : 1;
uint8_t bVirtualResource : 1;
uint8_t : 4;
uint8_t : 8;
uint8_t : 8;
arm_2d_color_info_t tColourInfo;
}, tInfo);
implement_ex(arm_2d_region_t, tRegion);
union {
arm_2d_tile_t *ptParent;
uint8_t *pchBuffer;
uint16_t *phwBuffer;
uint32_t *pwBuffer;
intptr_t nAddress;
};
};
贴图结构主要包含三部分:属性描述信息tInfo、尺寸位置信息tRegion,以及指针或引用。
贴图分为两种类型:根贴图和子贴图。根贴图直接拥有具体图片资源或显示缓冲区,其tInfo.bIsRoot属性为true,指针直接指向资源数组或显示缓冲区。
普通根贴图要求其指向的图片资源必须存在于4G地址空间中,而保存在外部Flash中的图片资源无法满足这一要求。为此,Arm-2D在基类arm_2d_tile_t的基础上派生出新的类:虚拟资源arm_2d_vres_t,专门用于描述这类无法直接访问的图片资源。
typedef struct arm_2d_vres_t arm_2d_vres_t;
struct arm_2d_vres_t {
implement_ex(arm_2d_tile_t, tTile);
uintptr_t pTarget;
intptr_t (*Load) (uintptr_t pTarget, arm_2d_vres_t *ptVRES, arm_2d_region_t *ptRegion);
void (*Depose) (uintptr_t pTarget, arm_2d_vres_t *ptVRES, intptr_t pBuffer);
};
实际使用中,我们不需要直接与arm_2d_vres_t的内部结构打交道,Arm-2D提供了简化的封装服务。
如何使用虚拟资源
假设你已经按照基础教程完成了Arm-2D的部署。
准备阶段
在工程管理器中展开Acceleration,找到LCD驱动模板arm_2d_disp_adapter_0.h。通过Configuration Wizard打开图形配置界面,在"Maximum number of Virtual Resources used per API"下拉列表中勾选除"NO Virtual Resource"以外的选项(比如1 per API),然后保存。
此时编译会出现错误,提示需要实现两个基本接口函数:
__disp_adapter0_vres_read_memory() - 从外部存储器读取数据
__disp_adapter0_vres_get_asset_address() - 返回虚拟资源起始地址
SPI Flash读取函数的实现示例:
extern void spi_flash_read(void *pBuffer, uint32_t nAddressInFlash, size_t nSize);
void __disp_adapter0_vres_read_memory(intptr_t pObj, void *pBuffer,
uintptr_t pAddress, size_t nSizeInByte)
{
ARM_2D_UNUSED(pObj);
spi_flash_read(pBuffer, (void * const)pAddress, nSizeInByte);
}
获取资源地址函数的简单实现:
uintptr_t __disp_adapter0_vres_get_asset_address(uintptr_t pObj,
arm_2d_vres_t *ptVRES)
{
ARM_2D_UNUSED(ptVRES);
ARM_2D_UNUSED(pObj);
return 0x00000000;
}
完成上述实现后,再次编译应该没有问题。
创建虚拟资源
在源代码中包含头文件:
#include "arm_2d_disp_adapter_0.h"
定义arm_2d_vres_t类型的静态变量:
static arm_2d_vres_t s_tMyVirtualRes =
disp_adapter0_impl_vres(
ARM_2D_COLOUR_RGB565,
320,
256
);
其中,disp_adapter0_impl_vres()宏的原型是:
disp_adapter0_impl_vres(__COLOUR_FORMAT, __WIDTH, __HEIGHT, ...)
创建好的虚拟资源可以像普通贴图那样在Arm-2D的API中作为素材和蒙版使用:
arm_2d_tile_copy_only(
&s_tMyVirtualRes.tTile,
ptTile,
NULL
);
也可以在虚拟资源的基础上创建子贴图:
static const arm_2d_tile_t c_tChildImage =
impl_child_tile(
s_tMyVirtualRes.tTile,
160,
128,
160,
128
);
多图片资源处理
当存在多个图片资源时,关键是通过__disp_adapter0_vres_get_asset_address()函数返回对应图片在外部存储器中的地址。
支持多图片有两种方式:
面向对象方式
利用pTarget成员传递对象信息,在接口函数中根据对象信息返回对应地址。
所见即所得方式
在建立虚拟资源时,直接将目标图片在外部存储器中的地址赋值给pTarget:
static arm_2d_vres_t s_tVRes0 =
disp_adapter0_impl_vres(
ARM_2D_COLOUR_RGB565,
32,
32,
.pTarget = 0x00001000
);
static arm_2d_vres_t s_tVRes1 =
disp_adapter0_impl_vres(
ARM_2D_COLOUR_RGB565,
32,
32,
.pTarget = 0x00002000
);
然后在获取地址函数中直接返回pObj:
uintptr_t __disp_adapter0_vres_get_asset_address(uintptr_t pObj,
arm_2d_vres_t *ptVRES)
{
ARM_2D_UNUSED(ptVRES);
return pObj;
}
SRAM占用优化建议
虚拟资源通过将外部存储器中的图片资源载入到芯片内部缓冲区来完成贴图操作。SRAM占用取决于API中使用的虚拟素材数量:
- 1个虚拟素材:需要1块缓冲
- 2个虚拟素材:需要2块缓冲(如图片像素数组和对应蒙版)
- 3个虚拟素材:需要3块缓冲
在Display Adapter配置中正确设置"Maximum number of Virtual Resource used per API"很重要。如果设置了"1 per API",系统会预留1个PFB空间;"2 per API"预留2个PFB空间,以此类推。
如果芯片RAM相对较大,可以配置使用堆分配来分配缓冲区,在配置文件中勾选"Use heap to allocate buffer in the virtual resource helper service"。
对于小资源芯片,可以使用背景载入模式,该模式下仅能使用arm_2d_tile_copy_only()显示虚拟资源,无需申请额外存储空间。
Arm-2D提供了动画背景场景模板,可以通过以下步骤添加:
- 在工程管理器中选择目标Group,右键选择Add New Item to Group
- 在User Code Template中展开Acceleration,选择Arm-2D Helper:PFB的Scene Template: Animate Background
- 在工程配置中添加头文件搜索路径
- 在main.c中包含头文件并初始化场景
参考Demo
Arm-2D在Demo中提供了虚拟资源的使用示例,包含文件arm_2d_scene_virtual_resource.c和arm_2d_scene_virtual_resource.h。该Demo展示了虚拟资源的基本用法和访问字库的方式。
演示代码使用memcpy替代了实际的SPI Flash访问函数,在实际应用中需要替换为真正的SPI Flash读取函数。
从使用极小缓冲区实现整块屏幕刷新的部分缓冲技术,到帮助用户直接使用外部存储器中图片资源的虚拟素材,Arm-2D为小资源环境下的GUI开发提供了完整的解决方案。
项目地址:https://github.com/ARM-software/Arm-2D