當(dāng)前位置:首頁 > 公眾號精選 > 裸機(jī)思維
[導(dǎo)讀]在前面的文章《【例說Arm-2D界面設(shè)計(jì)】“手?jǐn)]GUI”的利器——場景播放器》中,我們詳細(xì)介紹了智能設(shè)備時(shí)代一種“基于面板(Panel)的嵌入式界面設(shè)計(jì)范式”,并以Arm-2D的場景播放器(Scene Player)為例,介紹了小資源環(huán)境下具體“手搓GUI”的方式。

【說在前面的話】


在前面的文章《【例說Arm-2D界面設(shè)計(jì)】“手?jǐn)]GUI”的利器——場景播放器》中,我們詳細(xì)介紹了智能設(shè)備時(shí)代一種“基于面板(Panel)的嵌入式界面設(shè)計(jì)范式”,并以Arm-2D的場景播放器(Scene Player)為例,介紹了小資源環(huán)境下具體“手搓GUI”的方式。
很多小伙伴看了以后大為震撼,并紛紛發(fā)出了叩問靈魂的拷問:

我芯片內(nèi)部Flash已經(jīng)很緊張了,隨便一個(gè)背景圖你讓我存哪里?


這是個(gè)好問題。一般來說,我們所說的小資源環(huán)境是指:內(nèi)部FLASH空間小于64K,當(dāng)然也包括那些雖然芯片F(xiàn)lash較大(比如有128K)但原本的應(yīng)用代碼已經(jīng)非常龐大——“留給GUI的空間已經(jīng)不多”的情況。總之,留給Arm-2D的代碼空間已經(jīng)捉襟見肘,圖片資源又該保存到哪里呢?

一般來說,很多芯片會提供一個(gè)叫做 XIP 的外設(shè)(也許你的芯片中對應(yīng)的外設(shè)并不叫這個(gè)名字,但你可以根據(jù)下面的功能描述來對號入座),以便:
  • 通過QSPI接口連接外部的SPI Flash芯片;
  • 將外部Flash芯片中的內(nèi)容映射到4G地址空間中

換句話說,我們可以像訪問內(nèi)部Flash那樣使用芯片外部的SPI Flash,這其中不僅包括存儲數(shù)據(jù)(比如圖片),甚至還可以執(zhí)行代碼。


如果你芯片擁有XIP,那么你的芯片已經(jīng)不屬于我們所討論的“資源受限”的環(huán)境了——因?yàn)榇笕萘康钠釬lash不僅長見而且廉價(jià)。相信很多人對此都非常熟悉,我就不再贅述了。


如果你的芯片沒有XIP,而你也只能通過外設(shè) SPI 和自己編寫的驅(qū)動(dòng)來訪問外部 Flash,此時(shí),我們?nèi)绾问褂?Arm-2D 來簡化“手搓GUI” 的開發(fā)過程呢?不要急,本文就將為你揭曉謎底。


【什么是虛擬資源(Virtual Resource)】


Arm-2D幾乎所有API的基本操作單位都是“貼圖(Tile)”,它的數(shù)據(jù)結(jié)構(gòu)定義如下:
/*! * \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 };};
如果看著比較暈,不要緊,其實(shí)它就只有三大部分而已:
  • 貼圖的各類屬性描述信息:tInfo

  • 貼圖的尺寸和位置信息: tRegion

  • 貼圖的指針或引用


貼圖從種類上來說分類兩種:根貼圖(Root Tile)子貼圖(Child Tile)。其中,根貼圖是指那些直接“擁有”具體圖片資源(或者是顯示緩沖區(qū))的貼圖(Tile),這表現(xiàn)在:
  • 根貼圖的屬性 tInfo.bIsRoot 一定為 true;
  • 根貼圖的指針直接指向具體的資源數(shù)組或者現(xiàn)實(shí)緩沖區(qū)

從前面的數(shù)據(jù)結(jié)構(gòu)中,我們可以看到union中有很多指針,比如 pchBuffer、phwBuffer和pwBuffer。由于他們都是共用體,因此這些指針保存的地址值都是相同的,而具體使用哪個(gè)類型的指針則取決于目標(biāo)資源的顏色格式,這一信息可以是省略的,但一般通過 img2c.py 腳本轉(zhuǎn)換出來的tile都會在tInfo.tColourInfo中包含具體的顏色信息。

值得強(qiáng)調(diào)的是 ptParent 僅在子貼圖中有意義,用于指向自己的父貼圖(Parent Tile),而 nAddress 僅僅是方便對地址值進(jìn)行四則運(yùn)算的一個(gè)整形變量(uintptr_t)。


推論1:我們所有的圖片資源都是用根貼圖來描述的。
也許你已經(jīng)從根貼圖的指針看出了端倪:普通的根貼圖要求其指向的圖片資源必須存在于4G地址空間中——換句話說就是普通指針可以訪問的地方——保存在外部Flash中的圖片資源(在未經(jīng)XIP幫助的情況下)則無法滿足上述要求,因此無法直接用 arm_2d_tile_t 進(jìn)行描述

為了解決這一問題,arm-2d 在基類 arm_2d_tile_t 的基礎(chǔ)上派生出了一個(gè)新的類:虛擬資源(Virtual Resource),arm_2d_vres_t——專門用于描述這類無法直接訪問的圖片資源。其數(shù)據(jù)結(jié)構(gòu)如下:

/*! * \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 );};



對上述結(jié)構(gòu)提描述感到一頭霧水的小伙伴不要慌張——實(shí)際使用中,我們并不需要與 arm_2d_vres_t 的內(nèi)部結(jié)構(gòu)打交道——arm-2d為我們提供了傻瓜式的封裝服務(wù),使用起來依然非常簡單。


【如何使用虛擬資源?】


這里,我們假設(shè)你已經(jīng)按照文章《【喂到嘴邊了的模塊】準(zhǔn)備徒手?jǐn)]GUI?用Arm-2D三分鐘就夠了》的步驟完成了Arm-2D的部署。
準(zhǔn)備階段:
在工程管理器中展開 Acceleration,并找到你的LCD驅(qū)動(dòng)模板 arm_2d_disp_adapter_0.h(這里假設(shè)你只有一個(gè)屏幕):


通過Configuraion Wizard打開圖形配置界面:


假設(shè)你已經(jīng)配置好了其它部分,勾選這里的“Enable the virtual resoure helper service” 選項(xiàng)后保存——至此,我們就為 Display Adapter 0 開啟了其專屬的虛擬資源輔助服務(wù)(Helper Service)。需要強(qiáng)調(diào)的是,每個(gè)Display Adapter 都有自己獨(dú)立的虛擬資源輔助服務(wù),需要獨(dú)立的打開。


此時(shí),如果直接編譯,會看到如下的錯(cuò)誤:
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).

不要慌,這是我們有意為之——它提醒我們作為用戶需要提供(實(shí)現(xiàn))兩個(gè)最基本額接口函數(shù):

  • __disp_adapter0_vres_read_memory()

一個(gè)專門用于從外部存儲器的指定地址讀取指定長度字節(jié)的函數(shù),其原型如下:
void __disp_adapter0_vres_read_memory( intptr_t pObj,  void *pBuffer, uintptr_t pAddress, size_t nSizeInByte);

這里:

    • pObj 我們可以暫時(shí)忽略

    • pBuffer 指向一塊緩沖區(qū),用于保存我們從外部存儲器中讀取到的內(nèi)容;

    • pAddress 保存的是目標(biāo)內(nèi)容在外部存儲器中的地址;

    • nSizeInByte 保存的是要讀取的字節(jié)數(shù)


一般來說,如果我們已經(jīng)事先調(diào)試好了一個(gè)SPI Flash讀取函數(shù),就可以輕松的實(shí)現(xiàn)這一函數(shù),比如:

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()

一個(gè)專門用于返回當(dāng)前虛擬資源起始地址的函數(shù)。需要注意的是,它的返回類型是 uintptr_t,在Cortex-M環(huán)境下是一個(gè)32位的無符號整形(uint32_t),我們用它來返回目標(biāo)圖片在SPI Flash中的起始地址綽綽有余。其函數(shù)原型是:
uintptr_t __disp_adapter0_vres_get_asset_address( uintptr_t pObj, arm_2d_vres_t *ptVRES);

這里:

    • pObj 我們可以暫時(shí)忽略

    • ptVRES 指向的是我們的目標(biāo)虛擬資源


在最簡單的情況下,假設(shè)你的系統(tǒng)只有一背景圖保存在外部SPI Flash中,且地址為 0x00000000,那么這個(gè)函數(shù)就極其簡單了:

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

也許你要問,如果我要處理多個(gè)圖片該怎么辦呢?別著急,后面會有專門的章節(jié)詳細(xì)介紹。現(xiàn)階段我們先專注于完成一個(gè)最簡單的例子。


完成了上述準(zhǔn)備工作,再次編譯就應(yīng)該毫無問題了。




創(chuàng)建自己的虛擬資源:


在要?jiǎng)?chuàng)建虛擬資源的源代碼中加入對 Display Adapter 0 的頭文件引用:

#include "arm_2d_disp_adapter_0.h"


定義一個(gè) arm_2d_vres_t 類型的靜態(tài)變量(或者全局變量),并使用專門的宏 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上使用,則對應(yīng)的描述宏為 disp_adapter1_impl_vres()。不管如何,它們的原型是一樣的:

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

這里:

    • __COLOUR_FORMAT 是目標(biāo)素材的顏色格式,具體可用的顏色在 arm_2d_type.h 中定義,都以 ARM_2D_COLOUR_ 作為前綴。

    • __WIDTH 是目標(biāo)素材的像素寬度

    • __HEIGHT是目標(biāo)素材的像素高度

    • ... 是一系列可選的參數(shù),主要用于初始化 arm_2d_vres_t 中的一些特殊成員(比如 pTarget),這個(gè)在隨后的章節(jié)中會用到。


在上述例子中,我們創(chuàng)建了一個(gè)虛擬資源 s_tMyVirtualRes,由于它是 arm_2d_tile_t 的派生類,因此可以像普通的貼圖那樣在arm-2d的API中作為素材(source tile)蒙版(mask)來直接使用,比如:

/* 把 虛擬素材 顯示在屏幕上 */arm_2d_tile_copy(   &s_tMyVirtualRes.tTile, /* 素材 */ ptTile, /* 目標(biāo)緩沖區(qū) */ NULL,  ARM_2D_CP_MODE_COPY); 


效果如下:




正如我們前面說過的,虛擬素材(virtual resource)也是貼圖(Tile)的一種,因此,也可以在它的基礎(chǔ)上創(chuàng)建子貼圖(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,清晰的標(biāo)明了 c_tChildImage 的子貼圖身份;

  • 創(chuàng)建子貼圖作為素材時(shí),bDerivedResource一定要設(shè)置為 true切記切記!

  • 這個(gè)例子中,觀察 tLocationtSize容易發(fā)現(xiàn):我們實(shí)際上是取了原圖右下角的1/4作為新的素材


修改代碼,將新的素材也拷貝到屏幕上:

/* 把 虛擬素材 顯示在屏幕上 */arm_2d_tile_copy(   &s_tMyVirtualRes.tTile, /* 素材 */ ptTile, /* 目標(biāo)緩沖區(qū) */ NULL,  ARM_2D_CP_MODE_COPY);/* 把 子貼圖 顯示在屏幕上 */arm_2d_tile_copy(   &c_tChildImage, /* 素材 */ ptTile, /* 目標(biāo)緩沖區(qū) */ NULL,  ARM_2D_CP_MODE_COPY);

由于我們在拷貝子貼圖時(shí)沒有指定要復(fù)制的位置(給了NULL),因此被默認(rèn)放置到了屏幕的左上角,形成了如下的效果:





【我們有多個(gè)圖片該怎么辦?】


前面的例子中,為了讓小伙伴們快速的體驗(yàn)虛擬資源的爽快,因此我們對內(nèi)容作了簡化——只演示了一個(gè)圖片的情況——實(shí)際應(yīng)用中,顯然這是無法滿足要求的。

聰明的小伙伴也許已經(jīng)注意到了,當(dāng)存在多個(gè)圖片資源的時(shí)候,決定我們實(shí)際讀取那一張圖片的關(guān)鍵就是函數(shù) __disp_adapter0_vres_get_asset_address() 的返回值——它返回誰的地址,讀取的就是誰的圖片。
觀察它的函數(shù)原型,容易發(fā)現(xiàn)兩個(gè)形參都很有潛質(zhì)。
uintptr_t __disp_adapter0_vres_get_asset_address( uintptr_t pObj, arm_2d_vres_t *ptVRES)

換句話說,支持多圖片的關(guān)鍵就在于如何使用傳遞進(jìn)來的參數(shù)返回對應(yīng)圖片在外部存儲器中的地址


進(jìn)一步觀察 arm_2d_vres_t 的結(jié)構(gòu),我們可以注意到一個(gè)有趣的成員 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; ...};


無論我們給它賦任何內(nèi)容,它的值都會作為第一個(gè)實(shí)參傳遞給接口函數(shù)

__disp_adapter0_vres_read_memory(intptr_t pObj, …… )

__disp_adapter0_vres_get_asset_address(uintptr_t pObj, ……)

也就是這里的 pObj。


至此,對多圖片的支持實(shí)際上就形成了兩種方式:

  • 面向?qū)ο蟮姆绞剑∣OPC)

  • 所見即所得的方式


對于熟悉使用C語言進(jìn)行面向?qū)ο箝_發(fā)(OOPC)的小伙伴來說,恐怕看了上面的描述就已經(jīng)心領(lǐng)神會了吧。這里就不再贅述。


剩下的篇幅,我們將著重介紹“所見即所得”的方法:


步驟一:在建立(描述)虛擬資源時(shí),將目標(biāo)圖片在外部存儲器中的地址直接賦值給 pTarget。比如:
static arm_2d_vres_t s_tVRes0 =  disp_adapter0_impl_vres(  ARM_2D_COLOUR_RGB565, 32, 32, .pTarget = <這個(gè)資源在外部存儲器中的地址> ); static arm_2d_vres_t s_tVRes1 =  disp_adapter0_impl_vres(  ARM_2D_COLOUR_RGB565, 32, 32, .pTarget = <這個(gè)資源在外部存儲器中的地址> );...


步驟二:在函數(shù) __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;}


本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動(dòng)現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動(dòng)力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉