傻孩子(GorgonMeducer) · 2022年01月17日

【玩转Arm-2D】如何用小资源制作一个酷炫进度条

以下文章来源于嵌入式小书虫 ,作者FledgingSu 支离苏
image.png
上一篇我们简单介绍了一下arm公司的开源项目Arm-2D,这一篇我们就用Arm-2D制作一个背景可以有动画效果的进度条,效果如下图:
640.gif
什么,背景还有动画效果,这的消耗多大的flash资源啊,小资源单片机能开销的起吗?

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

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

所以,我们只要做一个周期的图片(17 * 17像素图片素材),然后,不停的填充(重复周期)就可以了。如下图所示:
image.png
所谓的动画效果,其实只是改了周期函数的相位(贴图起始位置)而已。

如下图所示:
image.png

  • 坐标从(-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,子子孙孙可以无穷尽也,如下图所示:
image.png

  • 只有Root Tile才会指向缓冲区,(也就是所有Tile共享了同一片缓冲区)。
  • (iX,iY)坐标是相对于Parent Tile的。

那子Tile的区域可以比 Parent Tile区域大吗?
答案是可以的,
不过超出部分Arm-2D会给我们裁剪掉,如下图所示:
image.png

  • 阴影部分是被裁剪掉的,不会在屏幕中显示出来。
  • 我们可以移动(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 paving
arm_2d_tile_generate_child(
      ptTarget, ||父Tile
      &tChildTileRegion,||子Tile的 Region
      &ChildTile,||生成的子Tile 
      false); ||默认为false   
  • 注意:第4个参数一般默认为false,这个参数是官方预留的接口,我们目前用不到。

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

下面我们就动手制作一个简单的进度条,在父Tile中动态生成进度条子Tile,原理如下图所示:
image.png
好了,下面就开始实现他,程序如下:

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/[progress-bar-02][bare-metal][pfb]/controls/progress_bar_drill.c

首发:裸机思维
作者:FledgingSu 支离苏

专栏推荐文章

如果你喜欢我的思维,欢迎订阅裸机思维
推荐阅读
关注数
1466
内容数
108
探讨嵌入式系统开发的相关思维、方法、技巧。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息