Arm_2D移植与入门
在当今竞争激烈的市场中,嵌入式设备不仅需要高性能,还需要具有吸引力的用户界面。图形用户界面(GUI)在提升用户体验方面起着至关重要的作用。为了满足这一需求,Arm 开发了 Arm_2D,并且在Github上发布了一个专为嵌入式系统设计的高效 2D 图形库:
https://github.com/ARM-software/Arm-2D
1. 什么是Arm_2D?
Arm_2D 是 Arm Limited 提供的一个用于嵌入式系统的 2D 图形库。它专门设计用于在基于 Arm 架构的微控制器上提供高效的图形渲染能力。Arm_2D 库优化了性能和内存使用,使其非常适合资源受限的嵌入式环境;它不仅能够提供基本的2D图形渲染功能,还能够支持复杂的图形操作,如平移、缩放、旋转等。它通过简化GUI开发流程,让开发者能够快速地为Arm架构的MCU开发出吸引人的界面。
2. Arm_2D的主要特点与优势
-
多级缓存系统
Arm_2D 引入了多级缓存机制,允许开发者实现部分屏幕刷新和多缓冲技术。这不仅减少了不必要的渲染,还显著降低了系统的功耗。
-
硬件加速
Arm_2D 能够利用 Arm 微控制器中的图形加速硬件,如 GPU 或专用 2D 图形引擎,从而提高渲染性能。
-
丰富的图形操作
Arm_2D 提供了广泛的图形操作,包括但不限于位图复制、颜色填充、透明混合、旋转和缩放。这些操作为开发者提供了强大的工具,以实现复杂的图形效果。
-
可配置性
Arm_2D 的设计考虑了不同硬件平台和应用场景的需求,因此它具有很高的可配置性,允许开发者根据具体的项目需求进行定制。
-
易用性
Arm_2D 的 API 设计简洁直观,使得即使是初学者也能快速上手。它简化了复杂图形效果的实现过程,让开发者能够专注于创新而非底层细节。
-
跨平台支持
虽然 Arm_2D 最初是为 Arm 架构设计的,但它的跨平台特性意味着它可以被移植到其他架构上,增加了它的适用性。
-
开源许可
Arm_2D 在 Apache 2.0 许可下开源,这意味着开发者可以免费使用、修改和分发该库,同时享有知识产权的保护。
3. Cube MX 配置
在进行Arm_2D移植前我们先要进行一些初始化;
-
配置时钟源
-
配置SPI
-
配置时钟树
4. Arm_2D移植
我们如何才能在程序上部署Arm_2D呢?,有两种方式,一种是直接利用Github里提供的源码移植,获取链接已经放在上面,另一种是利用CMSIS-PACK包移植,由于CMSIS-PACK包移植只需我们点点鼠标就可以配置完成编译通过,所以接下来我就已CMSIS-PACK包来进行移植,另一种方法大家自己进行尝试。
4.1 准备工作
首先我们准备一个工程,并且确保该工程已经实现了基本的LCD初始化;(我这里用的是STM32F103ZET6进行移植)
提供一个向LCD指定区域传送位图的函数,其在Arm_2D中的原型如下:
void Disp0_DrawBitmap (uint32_t x,
uint32_t y,
uint32_t width,
uint32_t height,
const uint8_t *bitmap)
-
uint32_t x
:位图绘制起始点的 x 坐标(水平位置)。 -
uint32_t y
:位图绘制起始点的 y 坐标(垂直位置)。 -
uint32_t width
:要绘制的位图图像的宽度。 -
uint32_t height
:要绘制的位图图像的高度。 -
const uint8_t *bitmap
:指向位图数据的指针。数据格式通常是一个字节数组,其中包含了位图图像的像素信息。
4.2 获取pack包
在 MDK 中部署 Arm-2D的第一步是获取对应的 cmsis-pack。你可以直接通过Pack Installer中找到Arm-2D:
也可以通过KEIL官网获取:
https://www.keil.arm.com/packs/arm-2d-arm/versions/
但是官网这里提供的是1.1.5版本的,而我用的是1.1.6,相对上一版本而言1.16拥有众多新特性等,推荐使用最新版,要想获得最新的版本,推荐向 【裸机思维】 公众号发送关键字 “Arm-2D” 获取网盘链接。
还可以通过 https://github.com/ARM-software/Arm-2D 获取:
获得 Arm-2D 的cmsis-pack后,可以直接双击进行安装之后进行一系列无脑操作,(这里强烈推荐将MDK安装最新版本,以避免一系列不必要的麻烦,节省时间)。
4.3 部署cmsis-pack
4.3.1 加入组件
点击 Manage Run-Time Environment快捷按钮来打开RTE配置窗口:
点击并展开Acceleration并根据下图勾选以下组件:
- Core:Arm-2D的核心(必选)
- Alpha-Blending:大部分与透明度相关的操作,比如基于蒙版的拷贝、透明图层合成、透明色块填充等等
- Transform:旋转、缩放等操作(支持蒙版、抠图和透明度)
- Filter:渲染滤镜(目前支持抗锯齿和模糊效果)
为了简化我们与LCD显示驱动进行对接,需要勾选以下服务:
- FPB:Partial Frame-Buffer模块,支持部分刷新的核心组件
- Display Adapter:一个使用 PFB 来适配 LCD 底层驱动的代码模板,帮我我们快速在上层绘图和底层LCD刷新之间建立桥梁。一般来说 Display Adapter 与 屏幕是一一对应关系:如果你有一块屏幕,这里就选“1”,如果你有两块屏幕,这里就选“2”,以此类推。
由于 Display Adapter 依赖了一些额外(Extra)的模块,因此,如果你只勾选了上述的部分,你会在窗口中看到橙色的警告:
这里警告的含义是说:Display Adapter依赖了模块 Controls 和 LCD ASCII Printf,但你没有勾选它们。简单的单击左下角的 Resolve 按钮,RTE会自动帮你勾选上所依赖的模块。
点击OK按钮完成组件添加,后续还可过此来添加demo组件。
为了方便后续的开发,强烈推荐下载并安装 perf_counter 模块,大家可以通过【裸机思维】公众号发送关键字“perf_counter”来获取对应的 cmsis-pack;
现在完成后,同样是打开RTE,找到 Utilities,并勾选 perf_counter 中的 Core,推荐以 Source ** 形式进行部署, 如出现橙色警告,还是点击Resolve**按钮来解决
如果不使用perf_counter来为Arm-2D提供时间基准服务,则需要手动实现下列两个函数:
extern
int64_t arm_2d_helper_get_system_timestamp(void);
extern
uint32_t arm_2d_helper_get_reference_clock_frequency(void)
其中:
- arm_2d_helper_get_system_timestamp() 要返回一个int64_t类型的时间戳,它的精度至少要是10KHz级别,并且不允许(在你我的有生之年内)溢出;
- arm_2d_helper_get_reference_clock_frequency() 要返回这个时间戳的实际频率,同样,根据要求,它至少要大于等于10KHz。
推荐使用 perf_counter ,不需要那么麻烦去实现两个函数,并且在最新的版本中 perf_counter 已经解绑了SysTick——允许用户将perf_couner移植到任意架构下、使用任意精度的定时器了。
4.3.2 配置编译环境
使用Arm Compiler 6(armclang)进行编译,因为 Arm Compiler 5(armcc) 已经不在支持Arm_2D了,需要打开对C11和GNU扩展的支持,即直接在"Language C"中选择“gnu11”:
此外,在使用Arm Compiler 6时,除调试专用的-O0以外,请务必开启Link-Time-Optimization。Arm-2D推荐的优化选项是:
-
如果只在乎性能不在乎尺寸,请选择-Ofast + Link Time Optimization
-
如果只在乎尺寸完全不在乎性能,请选择-Oz + Link Time Optimization
-
如果在乎尺寸和性能的平衡,请选择-Os + Link Time Optimization
-
不开Link Time Optimization的Arm Compiler 6 无法拉开和 gcc 的差距。开了Link Time Optimization的 Arm Compiler 6 可以瞬间超越(或者至少战平)IAR。
由于Arm-2D依赖CMSIS ,可以通过配置MDK的RTE的方式来获得最新版本CMSIS的支持。
- 点击工具栏最右边的按钮打开Pack Installer,找到ARM::CMSIS,并确保它显示“Up to date”,如果没有就单击对应的按钮进行更新。Arm-2D所依赖的CMSIS版本不得低于5.7.0(如果你要用Cortex-M55,则版本不得低于5.8.0),如下图所示:
-
确保显示“Up to date”后再打卡RTE,找到CMSIS,并勾选CORE和DSP,并将DSP下的Source选项勾选,这将允许我们直接使用源代码的形式来编译CMSIS-DSP的库。
-
如果不确定CMSIS是都为最新版本,可以点击底部的Select Packs按钮,如果看到顶部 “Use latest Software Packs for Target” 被勾选,基本上就不用担心它啦,依次点击🆗关闭就可以了,点击编译,可以看到成功完成编译,这样我们就将CMSIS编译进程序里了。
4.3.3 模块配置
在工程管理器中展开 Acceleration,并找到新加入的显示驱动适配器文件(arm_2d_disp_adapter_0.h)
双击打开后,在编辑器的左下角选择 Configuration Wizard
进入图形配置界面:
进入后根据自己的屏幕填写正确信息:
- 颜色位数(Screen Colour Depth)
- 横向分辨率(Width of the screen)
- 纵向分辨率(Height of the Screen)
- 部分刷新缓冲块的宽度(Width of the PFB Block),一般优先考虑为整行或者1/2行像素的宽度
- 部分刷新缓冲块的高度(Height of the PFB Block),一般推荐为1/10屏幕像素高度。在RAM较为紧缺时,考虑8或者1。
- 进行帧率计算时,平均多少帧做一次数据更新(Number of iterations),默认是30,选0将关闭帧率实时计算功能。
- 实时帧率统计模式(FPS Calculation Mode),默认为 Render-Only FPS(只计算Arm-2D渲染部分的帧率)而Real FPS则计算实际的帧率。
- 如果你的LCD控制芯片无法以硬件的方式交换RGB565像素的高低字节,则你可能需要勾选 Swap the high and low bytes 通过软件来实现这一功能。
填写完信息后保存,之后在Acceleration中找到 arm_2d_cfg.h并打开:
之后同样打开它的 Configuration Wizard 图形配置界面:
由于我们用到了 Extra中的一些模块,比如 Contols 和 LCD ASCII Printf,因此需要提供对应的信息:比如屏幕的颜色位数、分辨率和 printf 打印行缓冲的大小(默认值是64个ASCII字符)。
对于 Arm-2D General Configuration 中的一些选项,这里有必要做一下说明:
- Enable Asynchronous Programmers’ model support:目前推荐关闭
- Enable anti-alias support for all transform operations:使能旋转、缩放操作的抗锯齿功能
- Enable Support for accessing individual Colour channels:当你的目标屏幕是 RGB888,而你又需要支持 PNG 图片时推荐打开。
Patches for improving performance 中都是一些通过Hack掉某些可能用不到的特性或者功能来换取性能的选项。推荐做法是:
-
保持默认选项
-
在完成应用后依次尝试:如果对应用没有明显的影响,就勾选以换取一定的性能提升。
Benchmark 中是一些跑Arm-2D基准测试相关的选项。这里,我们需要设置屏幕的分辨率。如果你的Flash尺寸小于512K,则务必勾选Use Tiny mode to run benchmark——它将开启TINY模式,确保哪怕最低 64K Flash的芯片也可以运行最小的2D基准测试。
4.4 添加代码
添加完代码后,我们的移植工作就算是完成了;不出意外的话,我们就可以看到屏幕上会有显示;
首先再mian.c函数里添加源代码文件中包含的头文件:
#include "arm_2d_helper.h"
并在 **main()**函数中完成对 arm-2d 的初始化:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SPI1_Init();
//MX_TIM4_Init();
/* USER CODE BEGIN 2 */
HAL_GPIO_WritePin(GPIOA, PWM_Pin, GPIO_PIN_SET);//点亮背光
lcd_init_320x480(0);//屏幕初始化
arm_irq_safe {
arm_2d_init(); // 初始化 arm-2d
}
// 初始化 Display Adapter 0
disp_adapter0_init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
// 执行 Display Adapter 的刷新任务
disp_adapter0_task();
}
/* USER CODE END 3 */
}
编译、下载,如果一切顺利,你应该可以在屏幕上看到类似如下的画面:
如果你看到画面中的载入圈圈并未旋转,则可以按照以下思路来检查:
-
有没有部署 perf_counter
-
如果没有部署perf_counter,有没有按照要求实现函数 arm_2d_helper_get_system_timestamp() 和 arm_2d_helper_get_reference_clock_frequency();
-
如果部署了 perf_counter,检查是否正确初始化了perf_counter。在Cortex-M平台下,则应该检查运行 disp_adapter0_task() 的时候,SysTick是否处于运行状态。
-
如果还没有得到解决,看一下CubeMX时钟处是否为SisTick;
5. 移植常见问题
- 编译时报告某些系统的intrinsics重复定义,比如__set_PRIMASK 等等
这是由于工程中自带的cmsis版本太低,且与RTE所部署的cmsis冲突导致的。
解决办法:打开工程配置页面,再 Include Paths 处删除老版本CMSIS的头文件路径即可,以本工程为例,…/Drivers/CMSIS/Include就是存放老版本的地方。
- 提示找不到__aeabi_assert
这是由于我们在工程中选择了 microLib,而 microLib 没有为 assert.h 提供底层实现导致的。添加如下代码即可:
#include "arm_2d.h"
#include "cmsis_compiler.h"
#if defined(__MICROLIB)
void __aeabi_assert(const char *chCond, const char *chLine, int wErrCode)
{
ARM_2D_UNUSED(chCond);
ARM_2D_UNUSED(chLine);
ARM_2D_UNUSED(wErrCode);
while(1) {
__NOP();
}
}
#endif
还可以通过CMSIS-Pack包配置:
打开RTE,找到Compiler选项下I/O选项下的STDERR勾选为ITM,保存重新编译即可;
- 提示找不到 Disp0_DrawBitmap
你选择 Display Adapter 服务时,需要用户提供一个向 LCD 刷新数据的函数。当你有多个屏幕时,需要在 RTE 里为 Display Adapter 选择对应的数量:
然后给每个Display Adapter 添加一个属于自己的底层刷新函数;
- 出现Hardfault
检查栈(Stack)的大小,推荐在 0xC00(3K)以上为易,HEAP在1K以上。
- 屏幕颜色不对,要注意需要进行字节交换