以下文章来源于嵌入式小书虫 ,作者FledgingSu 支离苏
上一篇我们简单介绍了一下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 paving
arm_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/[progress-bar-02][bare-metal][pfb]/controls/progress_bar_drill.c
首发:裸机思维
作者:FledgingSu 支离苏
专栏推荐文章
如果你喜欢我的思维,欢迎订阅裸机思维