网站代理怎么设置,哪个软件发视频可以赚钱,企业网站制作模板免费,个人网站收款接口什么是ARM2D
Arm在Github上发布了一个专门针对“全体” Cortex-M处理器的2D图形加速库——Arm-2D 我们可以简单的把这个2D图形加速库理解为是一个专门针对Cortex-M处理器的标准“显卡驱动”。虽然这里的“显卡驱动”只是一个夸张的说法——似乎没有哪个Cortex-M处理器“配得上…什么是ARM2D
Arm在Github上发布了一个专门针对“全体” Cortex-M处理器的2D图形加速库——Arm-2D 我们可以简单的把这个2D图形加速库理解为是一个专门针对Cortex-M处理器的标准“显卡驱动”。虽然这里的“显卡驱动”只是一个夸张的说法——似乎没有哪个Cortex-M处理器“配得上”所谓的显卡但其实也并没有差多远——因为根据最新的趋势随着单片机资源的逐步丰富较高级的工艺节点正在逐步降价处理器不仅跑得越来越快、存储器越来越大而且大量的厂商已经或者正在考虑给Cortex-M处理器配备专属的2D图形加速引擎
以上摘自公众号裸机思维的文章
首先arm2d是一个2d引擎库他是纯软件的东西。很多人可能会被它的arm2d名字给误导。分不清arm2d和DMA2D。实际上DMA2D是硬件arm2d则是一个软件。arm2d的优秀性能让我瞠目结舌。在裸机思维的文章中你不难看到诸如M0内核、25M主频的主控的平台上跑出各种逆天的效果 这也是它吸引我的原因。虽然arm开发的初衷是服务于自家的硬件但是不意味着它不能够移植到别的平台。
以下是我摸索并熟悉arm2d的移植过程
移植前的准备
首先我们是基于esp-idf 5.0的sdk做的移植。那么第一需要的肯定是安装环境。这里参考官方手册 就不多赘述。
接下来就应该准备一份驱屏的基础代码了。我们准备了一块esp32s3的开发板其中屏幕使用了st7789的240X240的spi屏幕。
屏幕驱动
我喜欢以esp-iot-solution中的bus和screen为基础写屏幕驱动。bus中提供了诸如:spi i2c 8080 rgb的通讯层封装而screen则基于bus的封装提供了st7789 ili9341等lcd芯片封装。 这使得驱屏变得异常简单
cp -r esp-idf/example/get-started/sample_project ./
cd sample_project 新建components文件夹再复制刚才提到的bus和screen组件放到components文件夹下。接下来开始着手写屏幕驱动代码
/*** file arm_math.h* author cangyu (sky.kirtoqq.com)* brief * version 0.1* date 2024-06-06* * copyright Copyright (c) 2024, CorAL. All rights reserved.* */#include stdio.h
#include stdlib.h#include freertos/FreeRTOS.h
#include freertos/task.h
#include freertos/queue.h
#include esp_log.h#include screen_driver.h
#include esp_log.h
/* [Defines] */#define BOARD_IO_SPI2_MISO -1
#define BOARD_IO_SPI2_MOSI 11
#define BOARD_IO_SPI2_SCK 12
#define BOARD_LCD_SPI_CS_PIN 10
#define BOARD_LCD_SPI_DC_PIN 9
#define BOARD_LCD_SPI_RESET_PIN -1
#define BOARD_LCD_SPI_BL_PIN 46
#define BOARD_LCD_SPI_CLOCK_FREQ 40000000/* [Typedefs] *//* [Static Prototypes] */static void screen_clear(scr_driver_t *lcd, int color);/* [Static Variables] */static const char *TAG screen example;
static scr_driver_t g_lcd;/* [Macros] *//* [Global Functions] */void app_main(void)
{spi_config_t bus_conf {.miso_io_num BOARD_IO_SPI2_MISO,.mosi_io_num BOARD_IO_SPI2_MOSI,.sclk_io_num BOARD_IO_SPI2_SCK,.max_transfer_sz 1024*10};spi_bus_handle_t spi2_bus_handle spi_bus_create(SPI2_HOST, bus_conf);scr_interface_spi_config_t spi_lcd_cfg {.spi_bus spi2_bus_handle,.pin_num_cs BOARD_LCD_SPI_CS_PIN,.pin_num_dc BOARD_LCD_SPI_DC_PIN,.clk_freq BOARD_LCD_SPI_CLOCK_FREQ,.swap_data true,};scr_interface_driver_t *iface_drv;scr_interface_create(SCREEN_IFACE_SPI, spi_lcd_cfg, iface_drv);scr_find_driver(SCREEN_CONTROLLER_ST7789, g_lcd);scr_controller_config_t lcd_cfg {.interface_drv iface_drv,.pin_num_rst BOARD_LCD_SPI_RESET_PIN,.pin_num_bckl BOARD_LCD_SPI_BL_PIN,.rst_active_level 0,.bckl_active_level 1,.offset_hor 0,.offset_ver 0,.width 240,.height 240,.rotate SCR_DIR_LRTB,};g_lcd.init(lcd_cfg);scr_info_t lcd_info;g_lcd.get_info(lcd_info);ESP_LOGI(TAG, Screen name:%s | width:%d | height:%d, lcd_info.name,lcd_info.width, lcd_info.height);screen_clear(g_lcd, COLOR_GREEN);}/* [Static Functions] */static void screen_clear(scr_driver_t *lcd, int color)
{scr_info_t lcd_info;lcd-get_info(lcd_info);uint16_t *buffer malloc(lcd_info.width * sizeof(uint16_t));for (size_t i 0; i lcd_info.width; i) {buffer[i] color;}for (int y 0; y lcd_info.height; y) {lcd-draw_bitmap(0, y, lcd_info.width, 1, buffer);}free(buffer);
}
接下来就是编译烧录的事情了
idf.py set-target esp32s3 # 切换芯片
idf.py build # 编译代码
idf.py flash # 烧录
idf.py monitor # 显示串口log这是运行的效果 如此我们便得到了一个干净的驱屏的工程。
ARM2D的组件加入
我们在components文件夹下面创建一个arm2d的组件文件夹。再在arm2d文件夹里clone arm2d的仓库
cd components # 进入组件文件夹
mkdir arm2d # 创建arm2d组件件夹
cd arm2d # 进入arm2d组件文件夹
git clone https://github.com/ARM-software/Arm-2D.git # clone arm2d仓库arm2d的仓库里面很多文件夹很多文件。我们首要的就是要弄清楚哪些是我们需要的。我们需要的文件主要分布在Library中和Helper中。其中Library是核心部分而Helper则是后续添加的有帮助的部分。我们在arm2d里面创建一个CMakeLists.txt用于添加编译
touch CMakeLists.txtCMakeLists.txt内容如下
idf_component_register(SRC_DIRS Arm-2D/Library/Source Arm-2D/Helper/Source INCLUDE_DIRS Arm-2D/Library/Include Arm-2D/Helper/Include)接下来就是退出到工程根目录开始启动上述的编译。 不出所料发生了报错 找不到arm_2d_cfg.h。那我们在arm2d的文件夹下面添加它 通过搜索这个文件名我们发现在components/arm2d/Arm-2D/Library/Include/template路径下是有一个同名的config文件。我们把内容复制粘贴过来。大致看一遍配置值得注意的是GLCD_CFG_SCEEN_WIDTH 和 GLCD_CFG_SCEEN_HEIGHT 是屏幕的宽和高别忘记改成我们的屏幕大小240*240 修改后别忘记CMakeLists.txt也要修改
idf_component_register(SRC_DIRS Arm-2D/Library/Source Arm-2D/Helper/Source INCLUDE_DIRS . Arm-2D/Library/Include Arm-2D/Helper/Include)再次进行编译果然没那么简单。这里告诉我们缺少arm_2d_user_arch_port.h文件。哎之前的tamplate文件夹里好像有。直接复制过来。再次进行编译。
然后发现缺少arm_math.h文件。这个文件比较棘手是arm的dsp库。arm2d为了加速图形计算使用了很多arm的dsp库来加速。我们的esp32s3不是arm架构的根本没法使用。这下只能自己写一个arm_math.h文件将arm2d内部依赖arm-dsp库的内容提取出来 并简单的替代。这个过程比较费时费力需要从报错中找到源头然后从arm2d中理解。使用math.h进行替换。这里我直接放出我最终的arm_math.h
/*** file arm_math.h* author cangyu (sky.kirtoqq.com)* brief * version 0.1* date 2024-06-06* * copyright Copyright (c) 2024, CorAL. All rights reserved.* */#ifndef __ARM_MATH_H__
#define __ARM_MATH_H__/* [Includes] */
#include math.h#ifdef __cplusplus
extern C {
#endif/* [Defines] *//* [Typedefs] */typedef int16_t q15_t;
typedef int32_t q31_t;
typedef int64_t q63_t;/* [Global Prototypes] */__STATIC_FORCEINLINE q31_t clip_q63_to_q31(q63_t x)
{return ((q31_t) (x 32) ! ((q31_t) x 31)) ?((0x7FFFFFFF ^ ((q31_t) (x 63)))) : (q31_t) x;
}__STATIC_FORCEINLINE float arm_sin_f32(float x)
{return sin(x);
}__STATIC_FORCEINLINE float arm_cos_f32(float x)
{return cos(x);
}__STATIC_FORCEINLINE q31_t arm_sin_q31(q31_t x)
{return (q31_t)sin((float)x);
}__STATIC_FORCEINLINE q31_t arm_cos_q31(q31_t x)
{return (q31_t)cosl((float)x);
}__STATIC_FORCEINLINE uint32_t usat(int32_t val, uint8_t sat) {uint32_t max (1U sat) - 1; // 最大值为 2^sat - 1if (val 0) {return 0;} else if (val max) {return max;} else {return (uint32_t)val;}
}__STATIC_FORCEINLINE int32_t saturate_to_int32(int64_t value) {if (value INT32_MAX) {return INT32_MAX;} else if (value INT32_MIN) {return INT32_MIN;} else {return (int32_t)value;}
}__STATIC_FORCEINLINE int32_t qadd_impl(int32_t x, int32_t y) {int64_t result (int64_t)x y; // 将x和y相加return saturate_to_int32(result); // 对结果进行饱和处理
}/* [Macros] */// 计算一个32位整数从最高有效位
#define __CLZ(x) __builtin_clz(x)// 确保一个数值在给定的位宽内
#define __USAT(val, sat) usat(val, sat)// 它将两个32位有符号整数相加并在结果超出32位有符号整数范围时进行饱和处理
#define __QADD(x, y) qadd_impl(x, y)#ifdef __cplusplus
} /* extern C */
#endif#endif // __ARM_MATH_H__
再度编译发现虽然编译过了但是很多地方有warning看着十分难受。这里去请教了arm2d的作者傻孩子大佬。大致了解了原因后按照他的说法在CMakeLists.txt中加入了两行编译器命令
idf_component_register(SRC_DIRS Arm-2D/Library/Source Arm-2D/Helper/Source INCLUDE_DIRS . Arm-2D/Library/Include Arm-2D/Helper/Include)target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-implicit-fallthrough -Wno-unused-variable)
target_compile_options(${COMPONENT_LIB} PRIVATE -fms-extensions)
这样的话编译就没有警告了nice
对接arm2d
arm2d的对接十分曲折由于arm2d的源代码拥有若干个宏堆砌而成。很难读懂我也是参考了components/arm2d/Arm-2D/examples/[template][pc][vscode]/platform路径下的arm_2d_disp_adapter_0.h和arm_2d_disp_adapter_0.c拉过来放到arm2d文件夹下。 接了这两个文件的代码后由于引入了.c和一些esp32的代码其中arm_2d_disp_adapter_0.c的代码还借用了components/arm2d/Arm-2D/examples/common里面的代码。那么CMakeLists.txt自然也要修改如下
idf_component_register(SRC_DIRS . Arm-2D/Library/Source Arm-2D/Helper/Source Arm-2D/examples/common/controls Arm-2D/examples/common/assetINCLUDE_DIRS . Arm-2D/Library/Include Arm-2D/Helper/Include Arm-2D/examples/common/controls Arm-2D/examples/common/asset)target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-implicit-fallthrough -Wno-unused-variable)
target_compile_options(${COMPONENT_LIB} PRIVATE -fms-extensions)然而编译后发现arm_2d_disp_adapter_0.c 和 arm_2d_disp_adapter_0.h里面的代码都是灰色原来是缺少RTE_Acceleration_Arm_2D_Helper_Disp_Adapter0宏搜索arm2d文件发现在 components/arm2d/Arm-2D/examples/[template][pc][vscode]/platform/RTE_Components.h 中有定义那么拉取到arm2d的文件夹内后还需要 修改CMakeLists.txt添加一个编译宏 _RTE_ 就可以了
idf_component_register(SRC_DIRS . Library/Source Helper/Source common/controls common/asset INCLUDE_DIRS . Library/Include Helper/Include common/controls common/asset )target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-implicit-fallthrough -Wno-unused-variable)
target_compile_options(${COMPONENT_LIB} PRIVATE -fms-extensions)
target_compile_definitions(${COMPONENT_LIB} PRIVATE _RTE_ )这部分的代码真的很难理解作者十分擅长用宏。在这样的基础之下写下的代码如同自带一层混淆让人难以读懂和移植。 然后我们编译后发现缺少
void Disp0_DrawBitmap(uint32_t x, uint32_t y, uint32_t width, uint32_t height, const uint8_t *bitmap)
int64_t arm_2d_helper_get_system_timestamp(void)
uint32_t arm_2d_helper_get_reference_clock_frequency(void)
这部分是对接esp32底层我们留在main里面做
主函数调用arm2d
到了主函数了首先我们要引入头文件
// arm2d的内容
#include arm_2d.h
#include arm_2d_disp_adapter_0.h// 对接需要的内容
#include esp_timer.h然后我们要对接Disp0_DrawBitmap函数这部分是给arm2d底层刷新屏幕使用
void Disp0_DrawBitmap(uint32_t x, uint32_t y, uint32_t width, uint32_t height, const uint8_t *bitmap)
{g_lcd.draw_bitmap(x, y, width, height, (uint16_t*)bitmap);
}对接arm_2d_helper_get_system_timestamp函数这部分给arm2d提供时间戳
int64_t arm_2d_helper_get_system_timestamp(void)
{return esp_timer_get_time();
}对接arm_2d_helper_get_reference_clock_frequency函数这部分是时间戳频率
uint32_t arm_2d_helper_get_reference_clock_frequency(void)
{return 1000000;
}然后主函数下面加入代码 arm_irq_safe {arm_2d_init();}disp_adapter0_init(Disp0_DrawBitmap);while (1){disp_adapter0_task();vTaskDelay(1);}开始编译结果最后的链接阶段报错
A fatal error occurred: Segment loaded at 0x3c030390 lands in same 64KB flash mapping as segment loaded at 0x3c030020. Cant generate binary. Suggest changing linker script or ELF to merge sections.
ninja: build stopped: subcommand failed.通过翻译软件我们知道这里的段错误好像是冲突了。 我们通过指令xtensa-esp32-elf-objdump -h build/lcd_tjpgd.elf查找了所有的段
$ xtensa-esp32-elf-objdump -h build/lcd_tjpgd.elf build/lcd_tjpgd.elf: file format elf32-xtensa-leSections:
Idx Name Size VMA LMA File off Algn0 .rtc.text 00000010 600fe000 600fe000 00054000 2**0ALLOC1 .rtc.force_fast 00000000 600fe010 600fe010 0005374f 2**0CONTENTS2 .rtc_noinit 00000000 50000000 50000000 0005374f 2**0CONTENTS3 .rtc.force_slow 00000000 50000000 50000000 0005374f 2**0CONTENTS4 .rtc_reserved 00000018 600fffe8 600fffe8 00053fe8 2**3ALLOC5 .iram0.vectors 00000403 40374000 40374000 0001d000 2**2CONTENTS, ALLOC, LOAD, READONLY, CODE6 .iram0.text 0000edbb 40374404 40374404 0001d404 2**2CONTENTS, ALLOC, LOAD, READONLY, CODE7 .dram0.dummy 0000b200 3fc88000 3fc88000 0000f000 2**0ALLOC8 .dram0.data 0000255c 3fc93200 3fc93200 0001a200 2**4CONTENTS, ALLOC, LOAD, DATA9 .noinit 00000000 3fc9575c 3fc9575c 0005374f 2**0CONTENTS10 .dram0.bss 00004040 3fc95760 3fc95760 0001c75c 2**3ALLOC11 .flash.text 0002672f 42000020 42000020 0002d020 2**2CONTENTS, ALLOC, LOAD, READONLY, CODE12 .flash_rodata_dummy 00030000 3c000020 3c000020 00001020 2**0ALLOC13 .flash.appdesc 00000100 3c030020 3c030020 00001020 2**4CONTENTS, ALLOC, LOAD, READONLY, DATA14 arm2d.tile.c_tileWhiteDotMask 00000010 3c030120 3c030120 00001120 2**2CONTENTS, ALLOC, LOAD, READONLY, DATA15 arm2d.tile.c_tileWhiteDotRGB565 00000010 3c030130 3c030130 00001130 2**2CONTENTS, ALLOC, LOAD, READONLY, DATA16 arm2d.asset.c_bmpWhiteDotRGB565 00000188 3c030140 3c030140 00001140 2**2CONTENTS, ALLOC, LOAD, READONLY, DATA17 arm2d.asset.c_bmpWhiteDotAlpha 000000c4 3c0302c8 3c0302c8 000012c8 2**2CONTENTS, ALLOC, LOAD, READONLY, DATA18 .flash.rodata 0000d01c 3c030390 3c030390 00001390 2**4CONTENTS, ALLOC, LOAD, DATA19 .flash.rodata_noload 00000000 3c03d3ac 3c03d3ac 0005374f 2**0CONTENTS20 .ext_ram.dummy 0003ffe0 3c000020 3c000020 00001020 2**0ALLOC21 .ext_ram.bss 00000000 3c040000 3c040000 0005374f 2**0CONTENTS22 .iram0.text_end 00000041 403831bf 403831bf 0002c1bf 2**0ALLOC23 .iram0.data 00000000 40383200 40383200 0005374f 2**0CONTENTS24 .iram0.bss 00000000 40383200 40383200 0005374f 2**0CONTENTS25 .dram0.heap_start 00000000 3fc997a0 3fc997a0 0005374f 2**0CONTENTS26 .xt.prop 0002c2f8 00000000 00000000 0005374f 2**0CONTENTS, READONLY27 .xt.lit 000013a0 00000000 00000000 0007fa47 2**0CONTENTS, READONLY28 .xtensa.info 00000038 00000000 00000000 00080de7 2**0CONTENTS, READONLY29 .comment 0000004b 00000000 00000000 00080e1f 2**0CONTENTS, READONLY30 .debug_frame 00013cd8 00000000 00000000 00080e6c 2**2CONTENTS, READONLY, DEBUGGING, OCTETS31 .debug_info 001d8e2e 00000000 00000000 00094b44 2**0CONTENTS, READONLY, DEBUGGING, OCTETS32 .debug_abbrev 00028366 00000000 00000000 0026d972 2**0CONTENTS, READONLY, DEBUGGING, OCTETS33 .debug_loc 000d92c3 00000000 00000000 00295cd8 2**0CONTENTS, READONLY, DEBUGGING, OCTETS34 .debug_aranges 00007910 00000000 00000000 0036efa0 2**3CONTENTS, READONLY, DEBUGGING, OCTETS35 .debug_ranges 0000f150 00000000 00000000 003768b0 2**3CONTENTS, READONLY, DEBUGGING, OCTETS36 .debug_line 00176594 00000000 00000000 00385a00 2**0CONTENTS, READONLY, DEBUGGING, OCTETS37 .debug_str 000474ad 00000000 00000000 004fbf94 2**0CONTENTS, READONLY, DEBUGGING, OCTETS38 .debug_loclists 0000f07c 00000000 00000000 00543441 2**0CONTENTS, READONLY, DEBUGGING, OCTETS39 .debug_rnglists 00000418 00000000 00000000 005524bd 2**0CONTENTS, READONLY, DEBUGGING, OCTETS40 .debug_line_str 00001955 00000000 00000000 005528d5 2**0CONTENTS, READONLY, DEBUGGING, OCTETS定位了问题出在arm2d里面好像是arm2d的某个操作导致了内存段覆盖。搜索关键词arm2d.tile发现很多地方使用了ARM_SECTION(“arm2d.tile.c_tileUTF8UserFontA1Mask”)。知道这里很简单找到根源然后将它注释这个宏就不会起作用了。 大约是在components/arm2d/Arm-2D/Library/Include/arm_2d_utils.h文件的620行我找到了这个宏并在 arm_2d_cfg.h中加入宏定义来替换掉内部的宏
// 屏蔽内部的段操作
#define ARM_SECTION(__X)至此我们编译终于成功。虽然这时候还有一些warning没有消除arm2d被调用的时候产生的warning。但是我也无力追求完美了。直接编译烧录。 结果没有出现想要的动画效果这里我们通过在arm_2d_cfg.h中打开log分析发现是因为没有启动arm_2d_disp_adapter_0.h中的默认界面。我们通过arm_2d_disp_adapter_0.h的下面宏
// qDisable the default scene
// i Remove the default scene for this display adapter. We highly recommend you to disable the default scene when creating real applications.
#ifndef __DISP0_CFG_DISABLE_DEFAULT_SCENE__
# define __DISP0_CFG_DISABLE_DEFAULT_SCENE__ 0
#endif这里默认__DISP0_CFG_DISABLE_DEFAULT_SCENE__ 是1我们设置成0打开它。再进行编译烧录别忘记arm_2d_cfg.h中关闭log。结果如下 总结
其实我的结果并不是很好。按照傻孩子大佬的话说还是有很大优化空间。下一步优化应该就是在spi异步传输的方向上。乐鑫的spi分为queue传输和poll传输其中bus库采用的是poll传输。然而这种方式相当于同步操作应该用queue去异步等待这样能在传输的同时计算像素。能够消除LCD-Latency的时间。我这次记录摸索过程相当于是抛砖引玉希望大家能够优化出更好的版本
关于移植
移植的一个最大的准则就是”不要动别人的源码“。按照傻孩子大佬的说法就是【用扩展替代修改】。我遇到问题虽然会深入源码但是会根据源码的情况在配置文件或者是自己写的文件里面进行补充。如果别人的代码让你无法这样操作那就是提issue的时候。
后记碎碎念
arm2d的理念是以mask为中心所有的东西全都是贴图加上arm2d自带的蒙版(mask)完成的效果。这个理念实际上不是gui的理念例如lvgl是以控件为中心。arm2d则是更加底层 从使用者的角度实际上会比较麻烦但是这种效果能够在性能有限的设备上发挥很大的效果。实际上arm2d的源码一度让我崩溃以宏构建的内容 很多情况下无从知晓如何使用。代码的抽象程度已经完全是另一种语言。这次的移植意义也不大因为esp32性能足够有更加有好的lvgl没必要折腾arm2d。主要是想学习学习也想挑战一下自己。arm2d的代码在我看来我不能评价他是不好的毕竟恐怖的效率惊人的效果还是深深折服。但是我个人还是不会学习他的做法我希望能写出更加清晰易懂的代码。