嵌入式系统与单片机|技术阅读
登录|注册

您现在的位置是:嵌入式系统与单片机 > 技术阅读 > 玩转Arm-2D(二)Tile与子Tile,制作一个简单的进度条

玩转Arm-2D(二)Tile与子Tile,制作一个简单的进度条

上一篇我们简单介绍了一下arm公司的开源项目Arm-2D,这一篇我们就用Arm-2D制作一个背景可以有动画效果的进度条,效果如下图:

什么,背景还有动画效果,这的消耗多大的flash资源啊,小资源单片机能开销的起吗?

哈哈哈,别忘了Arm-2D是专门为小资源单片机量身定做的显示驱动,实现上图的效果其实只用了一张17*17像素的小图片素材,如下图所示

什么,这么小的素材是怎么实现动画效果的呢?

背景会动只是我们的视觉错觉,其实只是实现一个周期函数,

所以,我们只要做一个周期的图片(17*17像素图片素材),然后,不停的填充(重复周期)就可以了。如下图所示:


所谓的动画效果,其实只是改了周期函数的相位(贴图起始位置)而已。

 如下图所示:

  • 坐标从(-17,0)移动到(0,0)是一个周期,如此循环黑框中就出现了动画效果。


是不是觉得很简单,那我们就从Arm-2D的基本数据结构Tile讲起,一步一步实现这个动画效果。



首先讲一下什么是Tile(贴图)


Tile是用来描述资源的,他可以是一张图片、一个显示区域、一个字库数组等,他是Arm-2D操作的最基本的数据结构。

其原型如下:

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 : 5; uint8_t : 8; uint8_t : 8; arm_2d_color_info_t tColourInfo; //!< enforced colour }, tInfo); ||尺寸信息 implement_ex(arm_2d_region_t, tRegion); ||数据指针 union { /*! when bIsRoot is true, phwBuffer is available, *! otherwise ptParent is available */ arm_2d_tile_t *ptParent; uint16_t *phwBuffer; uint32_t *pwBuffer; uint8_t *pchBuffer; intptr_t nAddress; };};
  • arm_2d_tile_t类型共分3部分。

  • 第1个结构体成员tInfo是描述属性信息的(包括是不是根节点、颜色信息等)

  • 第2个结构体成员tRegion是描述区域尺寸信息的(包括区域坐标和大小)

  • 第3个结构体成员是一个数据指针的联合体(可以指向不同数据类型的Buffer)

  • 从17、18行的注释我们可以知道bIsRoot为true,代表根Tile,可以用phwBuffer指针,指向缓冲区;

    bIsRoot为false,代表是子Tile,可以用ptParent指针,指向父Tile。


从上面的Tile结构体中我们发现了root Tile(根Tile)、子Tile和指向父Tile的ptParent指针,那到底他们是什么关系呢,一个根Tile可以有几个子Tile呢,子Tile还可以有子Tile吗?

答案是一个根Tile可以有多个子Tile,子Tile还可以有子Tile,子子孙孙可以无穷尽也,如下图所示:

  • 只有Root Tile才会指向缓冲区,(也就是所有Tile共享了同一片缓冲区)。

  • (iX,iY)坐标是相对于Parent Tile的。


那子Tile的区域可以比 Parent Tile区域大吗?

答案是可以的,

不过超出部分Arm-2D会给我们裁剪掉,如下图所示:

  • 阴影部分是被裁剪掉的,不会在屏幕中显示出来。

  • 我们可以移动(iX,iY)坐标来 显示child  Tile的不同的区域。


知道了Tile的结构,那Tile怎么用呢?


  • 首先Tile是一个图片资源,如下:

const arm_2d_tile_t c_tileBlueSlashes = { .tRegion = {    ||图片大小  .tSize = { .iWidth = 17, .iHeight = 17 }, }, ||设置bIsRoot为true,可以用phwBuffer指针 .tInfo.bIsRoot = true, ||指向图片数组 .phwBuffer = (uint16_t *)c_bmpBlueSlashes,};
||图片数组__attribute__((aligned(2)))const uint8_t c_bmpBlueSlashes[] = { /*Pixel format: Blue: 5 bit, Green: 6 bit, Red: 5 bit*/ 0x3d, 0xa6, 0x3d, 0xa6, 0x3d, 0xa6, 0x3d, 0xa6, 0x3d, 0xa6, 0x3d, 0xa6, 0x3d, 0xa6, 0x3d, 0xa6,     ...};
  • 注意:我们的像素颜色格式为565,一个像素颜色信息需要两个字节保存,所以使用了__attribute__((aligned(2))),两字节对齐。


  • Tile是一个显示区域,如下所示:

arm_2d_tile_t child_tile ={ .tRegion = { ||区域坐标 .tLocation = { .iX = 30, .iY = 30, }, ||区域大小 .tSize = { .iWidth = 60, .iHeight = 20, }, }, ||设置bIsRoot为false,可以用ptParent 指针 .tInfo.bIsRoot = false,  ||指向Parent Tile .ptParent = (uint16_t *)ptTile,} ;


  • 还可以使用API函数arm_2d_tile_generate_child动态生成Child Tile,如下所示:

arm_2d_region_t tChildTileRegion = {  .tSize = {.iWidth = 10,.iHeight = 10,}, .tLocation = {.iX = 0,},};arm_2d_tile_t ChildTile;     //! generate a child tile for texture pavingarm_2d_tile_generate_child(      ptTarget, ||父Tile &tChildTileRegion,||子Tile的 Region &ChildTile,||生成的子Tile       false); ||默认为false          
  • 注意:第4个参数一般默认为false,这个参数是官方预留的接口,我们目前用不到。


有了上面的知识,我们就可以用Arm-2D制作一个进度条了


下面我们就动手制作一个简单的进度条,在父Tile中动态生成进度条子Tile,原理如下图所示:

好了,下面就开始实现他,程序如下:

void progress_bar_drill_show(const arm_2d_tile_t *ptTarget, int_fast16_t iProgress){ int_fast16_t iWidth = ptTarget->tRegion.tSize.iWidth * 6 >> 3; //!< 3/8 Width
ASSERT(NULL != ptTarget); ASSERT(iProgress <= 1000); ||定义进度条的区域 arm_2d_region_t tBarRegion = { .tLocation = { .iX = (ptTarget->tRegion.tSize.iWidth - (int16_t)iWidth) / 2, .iY = (ptTarget->tRegion.tSize.iHeight - c_tileBlueSlashes.tRegion.tSize.iHeight) / (int16_t)2, }, .tSize = { .iWidth = (int16_t)iWidth, .iHeight = c_tileBlueSlashes.tRegion.tSize.iHeight, },  };      
do { static uint8_t s_chOffset = 0; ||定义图片填充区域 arm_2d_region_t tInnerRegion = { .tSize = { .iWidth = tBarRegion.tSize.iWidth + c_tileBlueSlashes.tRegion.tSize.iWidth, .iHeight = tBarRegion.tSize.iHeight, }, .tLocation = {         ||iX从负数开始         .iX = -c_tileBlueSlashes.tRegion.tSize.iWidth + s_chOffset, }, };    arm_2d_tile_t tileInnerSlot; ||生成一个子Tile //! generate a child tile for texture paving arm_2d_tile_generate_child(ptTarget, &tBarRegion, &tileInnerSlot, false);
    ||往子TIle中填充图片      arm_2d_rgb16_tile_copy( &c_tileBlueSlashes,||填充的图片资源 &tileInnerSlot, ||子Tile &tInnerRegion, ||填充区域 ARM_2D_CP_MODE_FILL);    ||偏移量增加 s_chOffset++; if (s_chOffset >= c_tileBlueSlashes.tRegion.tSize.iWidth) {       s_chOffset = 0;    }  } while(0);     if (iProgress > 0) { //! calculate the width of the inner stripe       tBarRegion.tSize.iWidth = tBarRegion.tSize.iWidth * (int16_t)iProgress / 1000;            ||绘制进度矩形 arm_2d_rgb16_fill_colour(ptTarget, &tBarRegion, GLCD_COLOR_OLIVE); ||显示进度 lcd_text_location( 6, 12); lcd_printf( "%d%%",iProgress/10);  }    }

  • arm_2d_rgb16_tile_copy这个函数复制图片资源填充到目标Tile中的指定区域。

  • 这个背景会动的进度条只使用了一个17*17像素大小的图片,是不是感觉Arm-2D很厉害。

    如果你不想使用图片填充,也可以使用3个矩形来实现类似windosXP开机时的进度条风格哦。

程序视频演示如下:


备注:这个进度条程序是参考官方的例子修改而来,图片资源官方都有提供,大家可以参考实现自己风格的进度条。官方程序地址如下,里面使用了clock函数来控制动画速度,感兴趣的可以去看看。

https://github.com/ARM-software/EndpointAI/blob/main-arm-2d-more-examples/Kernels/Research/Arm-2D/examples/%5Bprogress-bar-02%5D%5Bbare-metal%5D%5Bpfb%5D/controls/progress_bar_drill.c


下一篇精彩预告:

参考官方lcd_printf函数实现自己的汉字显示程序

这里我们会讲子Tile的另一种用法,资源也可以用子tile来建立(即取一个大图的局部作为新的资源)

(* ̄︶ ̄)


原创不易,如果你喜欢我的公众号、觉得我 文章对你有所启发,

请务必“点赞、收藏、转发”,这对我很重要,谢谢!

欢迎订阅    嵌入式小书虫