当前位置:首页 > 公众号精选 > 裸机思维
[导读]在前面的文章《【例说Arm-2D界面设计】“手撸GUI”的利器——场景播放器》中,我们详细介绍了智能设备时代一种“基于面板(Panel)的嵌入式界面设计范式”,并以Arm-2D的场景播放器(Scene Player)为例,介绍了小资源环境下具体“手搓GUI”的方式。

【说在前面的话】


在前面的文章《【例说Arm-2D界面设计】“手撸GUI”的利器——场景播放器》中,我们详细介绍了智能设备时代一种“基于面板(Panel)的嵌入式界面设计范式”,并以Arm-2D的场景播放器(Scene Player)为例,介绍了小资源环境下具体“手搓GUI”的方式。
很多小伙伴看了以后大为震撼,并纷纷发出了叩问灵魂的拷问:

我芯片内部Flash已经很紧张了,随便一个背景图你让我存哪里?


这是个好问题。一般来说,我们所说的小资源环境是指:内部FLASH空间小于64K,当然也包括那些虽然芯片Flash较大(比如有128K)但原本的应用代码已经非常庞大——“留给GUI的空间已经不多”的情况。总之,留给Arm-2D的代码空间已经捉襟见肘,图片资源又该保存到哪里呢?

一般来说,很多芯片会提供一个叫做 XIP 的外设(也许你的芯片中对应的外设并不叫这个名字,但你可以根据下面的功能描述来对号入座),以便:
  • 通过QSPI接口连接外部的SPI Flash芯片;
  • 将外部Flash芯片中的内容映射到4G地址空间中

换句话说,我们可以像访问内部Flash那样使用芯片外部的SPI Flash,这其中不仅包括存储数据(比如图片),甚至还可以执行代码。


如果你芯片拥有XIP,那么你的芯片已经不属于我们所讨论的“资源受限”的环境了——因为大容量的片外Flash不仅长见而且廉价。相信很多人对此都非常熟悉,我就不再赘述了。


如果你的芯片没有XIP,而你也只能通过外设 SPI 和自己编写的驱动来访问外部 Flash,此时,我们如何使用 Arm-2D 来简化“手搓GUI” 的开发过程呢?不要急,本文就将为你揭晓谜底。


【什么是虚拟资源(Virtual Resource)】


Arm-2D几乎所有API的基本操作单位都是“贴图(Tile)”,它的数据结构定义如下:
/*! * \brief a type for tile *  */typedef struct arm_2d_tile_t arm_2d_tile_t;struct arm_2d_tile_t { implement_ex(struct { uint8_t bIsRoot              : 1; //!< is this tile a root tile uint8_t bHasEnforcedColour   : 1; //!< does this tile contains enforced colour info uint8_t bDerivedResource     : 1; //!< indicate whether this is a derived resources (when bIsRoot == 0) uint8_t bVirtualResource     : 1; //!< indicate whether the resource should be loaded on-demand uint8_t : 4; uint8_t : 8; uint8_t : 8; arm_2d_color_info_t tColourInfo; //!< enforced colour }, tInfo);  implement_ex(arm_2d_region_t, tRegion); //!< the region of the tile  union { /* when bIsRoot is true, phwBuffer is available, * otherwise ptParent is available */ arm_2d_tile_t *ptParent; //!< a pointer points to the parent tile uint8_t *pchBuffer; //!< a pointer points to a buffer in a 8bit colour type uint16_t *phwBuffer; //!< a pointer points to a buffer in a 16bit colour type uint32_t *pwBuffer; //!< a pointer points to a buffer in a 32bit colour type  intptr_t nAddress; //!< a pointer in integer };};
如果看着比较晕,不要紧,其实它就只有三大部分而已:
  • 贴图的各类属性描述信息:tInfo

  • 贴图的尺寸和位置信息: tRegion

  • 贴图的指针或引用


贴图从种类上来说分类两种:根贴图(Root Tile)子贴图(Child Tile)。其中,根贴图是指那些直接“拥有”具体图片资源(或者是显示缓冲区)的贴图(Tile),这表现在:
  • 根贴图的属性 tInfo.bIsRoot 一定为 true
  • 根贴图的指针直接指向具体的资源数组或者现实缓冲区

从前面的数据结构中,我们可以看到union中有很多指针,比如 pchBuffer、phwBuffer和pwBuffer。由于他们都是共用体,因此这些指针保存的地址值都是相同的,而具体使用哪个类型的指针则取决于目标资源的颜色格式,这一信息可以是省略的,但一般通过 img2c.py 脚本转换出来的tile都会在tInfo.tColourInfo中包含具体的颜色信息。

值得强调的是 ptParent 仅在子贴图中有意义,用于指向自己的父贴图(Parent Tile),而 nAddress 仅仅是方便对地址值进行四则运算的一个整形变量(uintptr_t)。


推论1:我们所有的图片资源都是用根贴图来描述的。
也许你已经从根贴图的指针看出了端倪:普通的根贴图要求其指向的图片资源必须存在于4G地址空间中——换句话说就是普通指针可以访问的地方——保存在外部Flash中的图片资源(在未经XIP帮助的情况下)则无法满足上述要求,因此无法直接用 arm_2d_tile_t 进行描述

为了解决这一问题,arm-2d 在基类 arm_2d_tile_t 的基础上派生出了一个新的类:虚拟资源(Virtual Resource),arm_2d_vres_t——专门用于描述这类无法直接访问的图片资源。其数据结构如下:

/*! * \brief a type for virtual resource * * \note the flag tTile.tInfo.bVirtualResource must be true (1) */typedef struct arm_2d_vres_t arm_2d_vres_t;struct arm_2d_vres_t {  /*! base class: tTile */ implement_ex( arm_2d_tile_t, tTile);  /*!  a reference of an user object  */ uintptr_t pTarget;  /*! *  \brief a method to load a specific part of an image *  \param[in] pTarget a reference of an user object  *  \param[in] ptVRES a reference of this virtual resource *  \param[in] ptRegion the target region of the image *  \return intptr_t the address of a resource buffer which holds the content */ intptr_t (*Load)   ( uintptr_t pTarget,  arm_2d_vres_t *ptVRES,  arm_2d_region_t *ptRegion);  /*! *  \brief a method to despose the buffer *  \param[in] pTarget a reference of an user object  *  \param[in] ptVRES a reference of this virtual resource *  \param[in] pBuffer the target buffer */ void (*Depose) ( uintptr_t pTarget,  arm_2d_vres_t *ptVRES,  intptr_t pBuffer );};



对上述结构提描述感到一头雾水的小伙伴不要慌张——实际使用中,我们并不需要与 arm_2d_vres_t 的内部结构打交道——arm-2d为我们提供了傻瓜式的封装服务,使用起来依然非常简单


【如何使用虚拟资源?】


这里,我们假设你已经按照文章《【喂到嘴边了的模块】准备徒手撸GUI?用Arm-2D三分钟就够了》的步骤完成了Arm-2D的部署。
准备阶段:
在工程管理器中展开 Acceleration,并找到你的LCD驱动模板 arm_2d_disp_adapter_0.h(这里假设你只有一个屏幕):


通过Configuraion Wizard打开图形配置界面:


假设你已经配置好了其它部分,勾选这里的“Enable the virtual resoure helper service” 选项后保存——至此,我们就为 Display Adapter 0 开启了其专属的虚拟资源辅助服务(Helper Service)。需要强调的是,每个Display Adapter 都有自己独立的虚拟资源辅助服务,需要独立的打开


此时,如果直接编译,会看到如下的错误:
Error: L6218E: Undefined symbol __disp_adapter0_vres_get_asset_address (referred from arm_2d_disp_adapter_0.o).Error: L6218E: Undefined symbol __disp_adapter0_vres_read_memory (referred from arm_2d_disp_adapter_0.o).

不要慌,这是我们有意为之——它提醒我们作为用户需要提供(实现)两个最基本额接口函数:

  • __disp_adapter0_vres_read_memory()

一个专门用于从外部存储器的指定地址读取指定长度字节的函数,其原型如下:
void __disp_adapter0_vres_read_memory( intptr_t pObj,  void *pBuffer, uintptr_t pAddress, size_t nSizeInByte);

这里:

    • pObj 我们可以暂时忽略

    • pBuffer 指向一块缓冲区,用于保存我们从外部存储器中读取到的内容;

    • pAddress 保存的是目标内容在外部存储器中的地址;

    • nSizeInByte 保存的是要读取的字节数


一般来说,如果我们已经事先调试好了一个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); /* it is just a demo, in real application, you can place a function to  * read SPI Flash  */ spi_flash_read(pBuffer, (void * const)pAddress, nSizeInByte);}



  • __disp_adapter0_vres_get_asset_address()

一个专门用于返回当前虚拟资源起始地址的函数。需要注意的是,它的返回类型是 uintptr_t,在Cortex-M环境下是一个32位的无符号整形(uint32_t),我们用它来返回目标图片在SPI Flash中的起始地址绰绰有余。其函数原型是:
uintptr_t __disp_adapter0_vres_get_asset_address( uintptr_t pObj, arm_2d_vres_t *ptVRES);

这里:

    • pObj 我们可以暂时忽略

    • ptVRES 指向的是我们的目标虚拟资源


在最简单的情况下,假设你的系统只有一背景图保存在外部SPI Flash中,且地址为 0x00000000,那么这个函数就极其简单了:

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

也许你要问,如果我要处理多个图片该怎么办呢?别着急,后面会有专门的章节详细介绍。现阶段我们先专注于完成一个最简单的例子。


完成了上述准备工作,再次编译就应该毫无问题了。




创建自己的虚拟资源:


在要创建虚拟资源的源代码中加入对 Display Adapter 0 的头文件引用:

#include "arm_2d_disp_adapter_0.h"


定义一个 arm_2d_vres_t 类型的静态变量(或者全局变量),并使用专门的宏 disp_adapter0_impl_vres 来描述资源的颜色和尺寸信息,比如:

static arm_2d_vres_t s_tMyVirtualRes =  disp_adapter0_impl_vres(  ARM_2D_COLOUR_RGB565, // 图片的颜色格式 320, // 图片的宽度 256, // 图片的高度 );

其中,宏disp_adapter0_impl_vres() Display Adapter 0 专用的,以此类推,如果你的虚拟资源要在 Display Adapter 1上使用,则对应的描述宏为 disp_adapter1_impl_vres()。不管如何,它们的原型是一样的:

disp_adapter0_impl_vres(__COLOUR_FORMAT, __WIDTH, __HEIGHT,...)

这里:

    • __COLOUR_FORMAT 是目标素材的颜色格式,具体可用的颜色在 arm_2d_type.h 中定义,都以 ARM_2D_COLOUR_ 作为前缀。

    • __WIDTH 是目标素材的像素宽度

    • __HEIGHT是目标素材的像素高度

    • ... 是一系列可选的参数,主要用于初始化 arm_2d_vres_t 中的一些特殊成员(比如 pTarget),这个在随后的章节中会用到。


在上述例子中,我们创建了一个虚拟资源 s_tMyVirtualRes,由于它是 arm_2d_tile_t 的派生类,因此可以像普通的贴图那样在arm-2d的API中作为素材(source tile)蒙版(mask)来直接使用,比如:

/* 把 虚拟素材 显示在屏幕上 */arm_2d_tile_copy(   &s_tMyVirtualRes.tTile, /* 素材 */ ptTile, /* 目标缓冲区 */ NULL,  ARM_2D_CP_MODE_COPY); 


效果如下:




正如我们前面说过的,虚拟素材(virtual resource)也是贴图(Tile)的一种,因此,也可以在它的基础上创建子贴图(Child Tile),比如:

staticconst arm_2d_tile_t c_tChildImage = { .tRegion = { .tLocation = { .iX = 160, .iY = 128, }, .tSize = { .iWidth = 160, .iHeight = 128, }, }, .tInfo = { .bIsRoot = false, .bDerivedResource = true, }, .ptParent = (arm_2d_tile_t *)&s_tMyVirtualRes.tTile,};

这里:

  • bIsRootfalse,清晰的标明了 c_tChildImage 的子贴图身份;

  • 创建子贴图作为素材时,bDerivedResource一定要设置为 true切记切记!

  • 这个例子中,观察 tLocationtSize容易发现:我们实际上是取了原图右下角的1/4作为新的素材


修改代码,将新的素材也拷贝到屏幕上:

/* 把 虚拟素材 显示在屏幕上 */arm_2d_tile_copy(   &s_tMyVirtualRes.tTile, /* 素材 */ ptTile, /* 目标缓冲区 */ NULL,  ARM_2D_CP_MODE_COPY);/* 把 子贴图 显示在屏幕上 */arm_2d_tile_copy(   &c_tChildImage, /* 素材 */ ptTile, /* 目标缓冲区 */ NULL,  ARM_2D_CP_MODE_COPY);

由于我们在拷贝子贴图时没有指定要复制的位置(给了NULL),因此被默认放置到了屏幕的左上角,形成了如下的效果:





【我们有多个图片该怎么办?】


前面的例子中,为了让小伙伴们快速的体验虚拟资源的爽快,因此我们对内容作了简化——只演示了一个图片的情况——实际应用中,显然这是无法满足要求的。

聪明的小伙伴也许已经注意到了,当存在多个图片资源的时候,决定我们实际读取那一张图片的关键就是函数 __disp_adapter0_vres_get_asset_address() 的返回值——它返回谁的地址,读取的就是谁的图片。
观察它的函数原型,容易发现两个形参都很有潜质。
uintptr_t __disp_adapter0_vres_get_asset_address( uintptr_t pObj, arm_2d_vres_t *ptVRES)

换句话说,支持多图片的关键就在于如何使用传递进来的参数返回对应图片在外部存储器中的地址


进一步观察 arm_2d_vres_t 的结构,我们可以注意到一个有趣的成员 pTarget
 typedef struct arm_2d_vres_t arm_2d_vres_t;struct arm_2d_vres_t { ... /*!  a reference of an user object  */ uintptr_t pTarget; ...};


无论我们给它赋任何内容,它的值都会作为第一个实参传递给接口函数

__disp_adapter0_vres_read_memory(intptr_t pObj, …… )

__disp_adapter0_vres_get_asset_address(uintptr_t pObj, ……)

也就是这里的 pObj。


至此,对多图片的支持实际上就形成了两种方式:

  • 面向对象的方式(OOPC)

  • 所见即所得的方式


对于熟悉使用C语言进行面向对象开发(OOPC)的小伙伴来说,恐怕看了上面的描述就已经心领神会了吧。这里就不再赘述。


剩下的篇幅,我们将着重介绍“所见即所得”的方法:


步骤一:在建立(描述)虚拟资源时,将目标图片在外部存储器中的地址直接赋值给 pTarget。比如:
static arm_2d_vres_t s_tVRes0 =  disp_adapter0_impl_vres(  ARM_2D_COLOUR_RGB565, 32, 32, .pTarget = <这个资源在外部存储器中的地址> ); static arm_2d_vres_t s_tVRes1 =  disp_adapter0_impl_vres(  ARM_2D_COLOUR_RGB565, 32, 32, .pTarget = <这个资源在外部存储器中的地址> );...


步骤二:在函数 __disp_adapter0_vres_get_asset_address 直接将 pObj 的值(也就是 ptVRES->pTarget)的值返回:

uintptr_t __disp_adapter0_vres_get_asset_address(uintptr_t pObj, arm_2d_vres_t *ptVRES){ ARM_2D_UNUSED(ptVRES); return pObj;}


本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

近日,特斯拉发布了Optimus最新进展视频,展现了其分拣电池、行走、执行工厂任务的能力,并配文“最近正在努力变得有用!”。

关键字: 特斯拉 机器人 Optimus

2024年5月9日晚,中国大陆晶圆代工龙头厂中芯国际发布2024年第一季度财报,销售收入为17.5亿美元,环比增长4.3%,同比增长19.7%;毛利率为13.7%,均好于指引。值得一提的是,这也是中芯国际的季度营收首次超...

关键字: 中芯国际

据韩联社报道,近日 SK 海力士子公司 SK 海力士系统集成电路拟以3.493亿美元的价格向无锡产业发展集团有限公司转让所持有的 SK 海力士系统集成电路(无锡)有限公司(下文简称无锡晶圆厂) 49.9% 股权。

关键字: SK 海力士 晶圆厂

近日,美国空军在加州爱德华兹空军基地进行了首次正式的AI控制战斗机试飞,美联社等少数媒体代表受邀观摩,美国空军方面明确表示,AI战机将是未来美国空中力量的重要组成部分。

关键字: 美国 AI

从近期媒体的一份爆料来看,苹果近年来其实已经下了不少力气深耕AI领域——在过去六年间从谷歌挖走了数十名人工智能专家,并在苏黎世创建了一个神秘的欧洲实验室。

关键字: 苹果 谷歌 实验室 AI

据外媒最新报道,微软近日披露了一个名为“ Dirty Stream ”的严重安全漏洞,该漏洞可能影响到数十亿下载量的 Android 应用。这种攻击可能使得攻击者完全控制应用,未经授权访问敏感用户数据,或拦截私密登录信息...

关键字: 安卓 漏洞 小米

近日,能源巨头壳牌宣布退出中国市场的电力业务,引发市场广泛关注。4月29日,壳牌(中国)新闻发言人对外确认了这一消息。此前,江苏和广东电力交易中心已分别发布公告,称已受理壳牌能源(中国)有限公司自愿退出市场的申请。

关键字: 壳牌 电力

业内消息,今年一季度中国智能手机出货量为6330万台,同比增长1%。这结束了连续11个季度的年度下滑。在厂商方面,四大厂商——OPPO/一加、荣耀、华为和vivo的市场份额不相上下;OPPO/一加以17.1%的市场份额领...

关键字: iPhone Others 苹果 库克

业内媒体报道,近日美国进一步收紧了对华为的出口限制,吊销了英特尔、高通等公司向华为出口芯片的许可证。华为迅速反击,海思半导体董事长何庭波,终端 BG 董事长余承东对内发布《致战友们的一封信》,提出针对 PC 端芯片的备胎...

关键字: 华为 高通 英特尔

电平逆变器的应用推荐低压MOS系列,产品稳定,性能可靠,满足恶劣环境工况下使用

关键字: 功率器件 多电平逆变器 逆变器 低压MOS
关闭