什么是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.kirto@qq.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.txt
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_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.kirto@qq.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 - 1
if (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删减移植的来的。
arm_2d_disp_adapter_0.c代码如下:
/**
* @file arm_2d_disp_adapter_0.c
* @author cangyu (sky.kirto@qq.com)
* @brief
* @version 0.1
* @date 2024-06-07
*
* @copyright Copyright (c) 2024, CorAL. All rights reserved.
*
*/
/* ==================== [Includes] ========================================== */
#include "arm_2d_disp_adapter_0.h"
#include "arm_2d_helper.h"
#include "arm_extra_lcd_printf.h"
#include "arm_extra_controls.h"
#include "esp_timer.h"
#include "esp_system.h"
/* ==================== [Defines] =========================================== */
/* ==================== [Typedefs] ========================================== */
/* ==================== [Static Prototypes] ================================= */
static IMPL_PFB_ON_DRAW(__pfb_draw_handler);
static void __on_frame_start(arm_2d_scene_t *ptScene);
static void __on_frame_complete(arm_2d_scene_t *ptScene);
static void __user_scene_player_init(void);
IMPL_PFB_ON_LOW_LV_RENDERING(__disp_adapter0_pfb_render_handler);
static bool __on_each_frame_complete(void *ptTarget);
static void disp_adapter0_navigator_init(void);
#if !__DISP0_CFG_DISABLE_NAVIGATION_LAYER__
IMPL_PFB_ON_DRAW(__disp_adapter0_user_draw_navigation);
IMPL_PFB_ON_DRAW(__disp_adapter0_draw_navigation);
#endif
/* ==================== [Static Variables] ================================== */
arm_2d_scene_player_t DISP0_ADAPTER;
Disp0_DrawBitmap_t _Disp0_DrawBitmap;
#if __DISP0_CFG_USE_CONSOLE__
static
struct {
console_box_t tConsole;
int64_t lTimestamp;
bool bShowConsole;
uint8_t chOpacity;
arm_2d_region_list_item_t tBackground;
} DISP0_CONSOLE;
#endif
/* ==================== [Macros] ============================================ */
/* ==================== [Global Functions] ================================== */
void disp_adapter0_init(Disp0_DrawBitmap_t Disp0_DrawBitmap)
{
_Disp0_DrawBitmap = Disp0_DrawBitmap;
__user_scene_player_init();
arm_extra_controls_init();
disp_adapter0_navigator_init();
DISP0_ADAPTER.Benchmark.lTimestamp = arm_2d_helper_get_system_timestamp();
if (!__DISP0_CFG_DISABLE_DEFAULT_SCENE__) {
static arm_2d_scene_t s_tScenes[] = {
[0] = {
/* the canvas colour */
.tCanvas = {GLCD_COLOR_WHITE},
.fnScene = &__pfb_draw_handler,
//.ptDirtyRegion = (arm_2d_region_list_item_t *)s_tDirtyRegions,
.fnOnFrameStart = &__on_frame_start,
.fnOnFrameCPL = &__on_frame_complete,
.fnDepose = NULL,
},
};
arm_2d_scene_player_append_scenes(
&DISP0_ADAPTER,
(arm_2d_scene_t *)s_tScenes,
dimof(s_tScenes));
}
}
arm_fsm_rt_t __disp_adapter0_task(void)
{
return arm_2d_scene_player_task(&DISP0_ADAPTER);
}
/* ==================== [Static Functions] ================================== */
static IMPL_PFB_ON_DRAW(__pfb_draw_handler)
{
ARM_2D_PARAM(pTarget);
ARM_2D_PARAM(ptTile);
arm_2d_canvas(ptTile, __top_container) {
arm_2d_align_centre(__top_container, 100, 100) {
draw_round_corner_box( ptTile,
&__centre_region,
GLCD_COLOR_BLACK,
64,
bIsNewFrame);
}
busy_wheel2_show(ptTile, bIsNewFrame);
}
arm_2d_op_wait_async(NULL);
return arm_fsm_rt_cpl;
}
static void __on_frame_start(arm_2d_scene_t *ptScene)
{
ARM_2D_UNUSED(ptScene);
}
static void __on_frame_complete(arm_2d_scene_t *ptScene)
{
ARM_2D_UNUSED(ptScene);
}
static void __user_scene_player_init(void)
{
memset(&DISP0_ADAPTER, 0, sizeof(DISP0_ADAPTER));
#if __DISP0_CFG_OPTIMIZE_DIRTY_REGIONS__
ARM_NOINIT
static arm_2d_region_list_item_t s_tDirtyRegionList[__DISP0_CFG_DIRTY_REGION_POOL_SIZE__];
#endif
//! initialise FPB helper
if (ARM_2D_HELPER_PFB_INIT(
&DISP0_ADAPTER.use_as__arm_2d_helper_pfb_t, //!< FPB Helper object
__DISP0_CFG_SCEEN_WIDTH__, //!< screen width
__DISP0_CFG_SCEEN_HEIGHT__, //!< screen height
COLOUR_INT, //!< colour date type
__DISP0_COLOUR_FORMAT__, //!< colour format
__DISP0_CFG_PFB_BLOCK_WIDTH__, //!< PFB block width
__DISP0_CFG_PFB_BLOCK_HEIGHT__, //!< PFB block height
__DISP0_CFG_PFB_HEAP_SIZE__ //!< number of PFB in the PFB pool
#if __DISP0_CFG_VIRTUAL_RESOURCE_HELPER__ \
&& !__DISP0_CFG_USE_HEAP_FOR_VIRTUAL_RESOURCE_HELPER__
+ __DISP0_CFG_VIRTUAL_RESOURCE_HELPER__
#else
+ (__DISP0_CFG_ROTATE_SCREEN__ > 0)
#endif
,{
.evtOnLowLevelRendering = {
//! callback for low level rendering
.fnHandler = &__disp_adapter0_pfb_render_handler,
},
.evtOnEachFrameCPL = {
.fnHandler = &__on_each_frame_complete,
},
#if __DISP0_CFG_ROTATE_SCREEN__
.evtBeforeFlushing = {
.fnHandler = &__before_flushing,
},
#endif
},
#if __DISP0_CFG_SWAP_RGB16_HIGH_AND_LOW_BYTES__
.FrameBuffer.bSwapRGB16 = true,
#endif
#if __DISP0_CFG_DEBUG_DIRTY_REGIONS__
.FrameBuffer.bDebugDirtyRegions = true,
#endif
.FrameBuffer.u3PixelWidthAlign = __DISP0_CFG_PFB_PIXEL_ALIGN_WIDTH__,
.FrameBuffer.u3PixelHeightAlign = __DISP0_CFG_PFB_PIXEL_ALIGN_HEIGHT__,
#if __DISP0_CFG_VIRTUAL_RESOURCE_HELPER__ \
&& !__DISP0_CFG_USE_HEAP_FOR_VIRTUAL_RESOURCE_HELPER__
// reserve PFB blocks for the virtual resource service
.FrameBuffer.u4PoolReserve = __DISP0_CFG_VIRTUAL_RESOURCE_HELPER__,
#endif
#if __DISP0_CFG_OPTIMIZE_DIRTY_REGIONS__
.DirtyRegion.ptRegions = s_tDirtyRegionList,
.DirtyRegion.chCount = dimof(s_tDirtyRegionList),
#endif
) < 0) {
//! error detected
assert(false);
}
#if __DISP0_CFG_ENABLE_3FB_HELPER_SERVICE__
do {
extern uintptr_t __DISP_ADAPTER0_3FB_FB0_ADDRESS__;
extern uintptr_t __DISP_ADAPTER0_3FB_FB1_ADDRESS__;
extern uintptr_t __DISP_ADAPTER0_3FB_FB2_ADDRESS__;
extern arm_2d_helper_2d_copy_handler_t __disp_adapter0_request_2d_copy;
extern arm_2d_helper_dma_copy_handler_t __disp_adapter0_request_dma_copy;
arm_2d_helper_3fb_cfg_t tCFG = {
.tScreenSize = {
__DISP0_CFG_SCEEN_WIDTH__,
__DISP0_CFG_SCEEN_HEIGHT__,
},
.chPixelBits = __DISP0_CFG_COLOUR_DEPTH__,
.pnAddress = {
[0] = ((uintptr_t)__DISP_ADAPTER0_3FB_FB0_ADDRESS__),
[1] = ((uintptr_t)__DISP_ADAPTER0_3FB_FB1_ADDRESS__),
[2] = ((uintptr_t)__DISP_ADAPTER0_3FB_FB2_ADDRESS__),
},
#if __DISP0_CFG_ENABLE_ASYNC_FLUSHING__
.evtOn2DCopy = {
.fnHandler = __disp_adapter0_request_2d_copy,
},
.evtOnDMACopy = {
.fnHandler = __disp_adapter0_request_dma_copy,
},
#endif
};
arm_2d_helper_3fb_init(&s_tDirectModeHelper, &tCFG);
} while(0);
#endif
arm_lcd_text_init((arm_2d_region_t []) {
{ .tSize = {
.iWidth = __DISP0_CFG_SCEEN_WIDTH__,
.iHeight = __DISP0_CFG_SCEEN_HEIGHT__,
}}});
DISP0_ADAPTER.Benchmark.wMin = UINT32_MAX;
DISP0_ADAPTER.Benchmark.hwIterations = __DISP0_CFG_ITERATION_CNT__;
DISP0_ADAPTER.Benchmark.hwFrameCounter = 0;
}
IMPL_PFB_ON_LOW_LV_RENDERING(__disp_adapter0_pfb_render_handler)
{
const arm_2d_tile_t *ptTile = &(ptPFB->tTile);
ARM_2D_PARAM(pTarget);
ARM_2D_PARAM(bIsNewFrame);
_Disp0_DrawBitmap(ptTile->tRegion.tLocation.iX,
ptTile->tRegion.tLocation.iY,
ptTile->tRegion.tSize.iWidth,
ptTile->tRegion.tSize.iHeight,
(const uint8_t *)ptTile->pchBuffer);
arm_2d_helper_pfb_report_rendering_complete(
&DISP0_ADAPTER.use_as__arm_2d_helper_pfb_t);
}
static bool __on_each_frame_complete(void *ptTarget)
{
ARM_2D_PARAM(ptTarget);
int64_t lTimeStamp = arm_2d_helper_get_system_timestamp();
#if __DISP0_CFG_FPS_CACULATION_MODE__ == ARM_2D_FPS_MODE_REAL
static int64_t s_lLastTimeStamp = 0;
int32_t nElapsed = 0;
if (0 != s_lLastTimeStamp) {
nElapsed = (int32_t)(lTimeStamp - s_lLastTimeStamp);
}
s_lLastTimeStamp = lTimeStamp;
#else /* __DISP0_CFG_FPS_CACULATION_MODE__ == ARM_2D_FPS_MODE_RENDER_ONLY */
int32_t nElapsed = DISP0_ADAPTER.use_as__arm_2d_helper_pfb_t.Statistics.nTotalCycle;
#endif
int32_t nTotalLCDCycCount = DISP0_ADAPTER.use_as__arm_2d_helper_pfb_t.Statistics.nRenderingCycle;
DISP0_ADAPTER.Benchmark.wLCDLatency = nTotalLCDCycCount;
/* calculate real-time FPS */
if (__DISP0_CFG_ITERATION_CNT__) {
if (DISP0_ADAPTER.Benchmark.hwIterations) {
int32_t nRenderCycle = DISP0_ADAPTER.use_as__arm_2d_helper_pfb_t.Statistics.nTotalCycle;
DISP0_ADAPTER.Benchmark.wMin = MIN((uint32_t)nElapsed, DISP0_ADAPTER.Benchmark.wMin);
DISP0_ADAPTER.Benchmark.wMax = MAX(nElapsed, (int32_t)DISP0_ADAPTER.Benchmark.wMax);
DISP0_ADAPTER.Benchmark.dwTotal += nElapsed;
DISP0_ADAPTER.Benchmark.dwRenderTotal += nRenderCycle;
DISP0_ADAPTER.Benchmark.hwIterations--;
DISP0_ADAPTER.Benchmark.hwFrameCounter += (nRenderCycle != 0) ? 1 : 0;
if (0 == DISP0_ADAPTER.Benchmark.hwIterations) {
if (0 == DISP0_ADAPTER.Benchmark.hwFrameCounter) {
DISP0_ADAPTER.Benchmark.wAverage = 0;
} else {
DISP0_ADAPTER.Benchmark.wAverage =
(uint32_t)(DISP0_ADAPTER.Benchmark.dwTotal / (uint64_t)DISP0_ADAPTER.Benchmark.hwFrameCounter);
DISP0_ADAPTER.Benchmark.wAverage = MAX(1, DISP0_ADAPTER.Benchmark.wAverage);
}
int64_t lElapsed = lTimeStamp - DISP0_ADAPTER.Benchmark.lTimestamp;
if (lElapsed) {
DISP0_ADAPTER.Benchmark.fCPUUsage = (float)((double)DISP0_ADAPTER.Benchmark.dwRenderTotal / (double)lElapsed) * 100.0f;
}
/* log statistics */
if (DISP0_ADAPTER.Benchmark.wAverage) {
ARM_2D_LOG_INFO(
STATISTICS,
0,
"DISP_ADAPTER0",
"FPS:%3d(%dms)\tCPU:%2.2f%%\tLCD-Latency:%2dms",
MIN(arm_2d_helper_get_reference_clock_frequency() / DISP0_ADAPTER.Benchmark.wAverage, 999),
(int32_t)arm_2d_helper_convert_ticks_to_ms(DISP0_ADAPTER.Benchmark.wAverage),
DISP0_ADAPTER.Benchmark.fCPUUsage,
(int32_t)arm_2d_helper_convert_ticks_to_ms(DISP0_ADAPTER.Benchmark.wLCDLatency)
);
} else {
ARM_2D_LOG_INFO(
STATISTICS,
0,
"DISP_ADAPTER0",
"FPS: SKIPPED\tCPU:%2.2f%%\tLCD-Latency:%2dms",
DISP0_ADAPTER.Benchmark.fCPUUsage,
(int32_t)arm_2d_helper_convert_ticks_to_ms(DISP0_ADAPTER.Benchmark.wLCDLatency)
);
}
DISP0_ADAPTER.Benchmark.wMin = UINT32_MAX;
DISP0_ADAPTER.Benchmark.wMax = 0;
DISP0_ADAPTER.Benchmark.dwTotal = 0;
DISP0_ADAPTER.Benchmark.dwRenderTotal = 0;
DISP0_ADAPTER.Benchmark.hwIterations = __DISP0_CFG_ITERATION_CNT__;
DISP0_ADAPTER.Benchmark.hwFrameCounter = 0;
DISP0_ADAPTER.Benchmark.lTimestamp = arm_2d_helper_get_system_timestamp();
}
}
}
return true;
}
#if !__DISP0_CFG_DISABLE_NAVIGATION_LAYER__
static void disp_adapter0_navigator_init(void)
{
/*! define dirty regions for the navigation layer */
IMPL_ARM_2D_REGION_LIST(s_tNavDirtyRegionList, static)
/* a region for the status bar on the bottom of the screen */
ADD_LAST_REGION_TO_LIST(s_tNavDirtyRegionList,
.tLocation = {
.iX = 0,
.iY = ((__DISP0_CFG_SCEEN_HEIGHT__ + 7) / 8 - 2) * 8},
.tSize = {
.iWidth = __DISP0_CFG_SCEEN_WIDTH__,
.iHeight = 8,
},
),
END_IMPL_ARM_2D_REGION_LIST()
#if __DISP0_CFG_USE_CONSOLE__
do {
#if __DISP0_CFG_SCEEN_WIDTH__ < 204
# define __DISP0_CONSOLE_WIDTH__ __DISP0_CFG_SCEEN_WIDTH__
#else
# define __DISP0_CONSOLE_WIDTH__ 204
#endif
#if __DISP0_CFG_SCEEN_HEIGHT__ < 200
# define __DISP0_CONSOLE_HEIGHT__ __DISP0_CFG_SCEEN_HEIGHT__
#else
# define __DISP0_CONSOLE_HEIGHT__ 192
#endif
#if __DISP0_CFG_CONSOLE_INPUT_BUFFER__
static uint8_t s_chInputBuffer[256];
#endif
static uint8_t s_chConsoleBuffer[ (__DISP0_CONSOLE_WIDTH__ / 6)
* (__DISP0_CONSOLE_HEIGHT__ / 8)];
console_box_cfg_t tCFG = {
.tBoxSize = {
.iWidth = __DISP0_CONSOLE_WIDTH__,
.iHeight = __DISP0_CONSOLE_HEIGHT__,
},
.pchConsoleBuffer = s_chConsoleBuffer,
.hwConsoleBufferSize = sizeof(s_chConsoleBuffer),
#if __DISP0_CFG_CONSOLE_INPUT_BUFFER__
.pchInputBuffer = s_chInputBuffer,
.hwInputBufferSize = sizeof(s_chInputBuffer),
#endif
.tColor = GLCD_COLOR_GREEN,
.bUseDirtyRegion = true,
.ppDirtyRegionList = (arm_2d_region_list_item_t **)&s_tNavDirtyRegionList,
};
console_box_init( &DISP0_CONSOLE.tConsole,
NULL,
&tCFG);
} while(0);
arm_2d_dirty_region_item_ignore_set(&DISP0_CONSOLE.tBackground, true);
arm_2d_region_t tScreen = {
.tSize = {
__DISP0_CFG_SCEEN_WIDTH__,
__DISP0_CFG_SCEEN_HEIGHT__
},
};
arm_2d_align_top_left(tScreen, 220, 200) {
DISP0_CONSOLE.tBackground.tRegion = __top_left_region;
}
arm_2d_helper_pfb_append_dirty_regions_to_list(
(arm_2d_region_list_item_t **)&s_tNavDirtyRegionList,
&DISP0_CONSOLE.tBackground,
1);
DISP0_CONSOLE.lTimestamp = 0;
#endif
/* register event handler for evtOnDrawNavigation */
arm_2d_scene_player_register_on_draw_navigation_event_handler(
&DISP0_ADAPTER,
__disp_adapter0_draw_navigation,
NULL,
(arm_2d_region_list_item_t *)s_tNavDirtyRegionList);
}
#else
static void disp_adapter0_navigator_init(void)
{
}
#endif
#if !__DISP0_CFG_DISABLE_NAVIGATION_LAYER__
IMPL_PFB_ON_DRAW(__disp_adapter0_user_draw_navigation)
{
ARM_2D_PARAM(pTarget);
ARM_2D_PARAM(bIsNewFrame);
return arm_fsm_rt_cpl;
}
IMPL_PFB_ON_DRAW(__disp_adapter0_draw_navigation)
{
ARM_2D_PARAM(pTarget);
ARM_2D_PARAM(bIsNewFrame);
__disp_adapter0_user_draw_navigation(pTarget, ptTile, bIsNewFrame);
#if __DISP0_CFG_USE_CONSOLE__
if (bIsNewFrame) {
if (console_box_on_frame_start(&DISP0_CONSOLE.tConsole)) {
DISP0_CONSOLE.lTimestamp = 0;
if (!DISP0_CONSOLE.bShowConsole) {
arm_2d_dirty_region_item_ignore_set(&DISP0_CONSOLE.tBackground, false);
} else {
arm_2d_dirty_region_item_ignore_set(&DISP0_CONSOLE.tBackground, true);
}
DISP0_CONSOLE.bShowConsole = true;
DISP0_CONSOLE.chOpacity = 255;
}
#if __DISP0_CFG_CONSOLE_DISPALY_TIME__ >= 1000 \
&& __DISP0_CFG_CONSOLE_DISPALY_TIME__ != 0xFFFFFFFF
if (DISP0_CONSOLE.bShowConsole) {
if (arm_2d_helper_is_time_out(__DISP0_CFG_CONSOLE_DISPALY_TIME__, &DISP0_CONSOLE.lTimestamp)) {
DISP0_CONSOLE.bShowConsole = false;
} else {
int64_t lTimeElapsedInMs = -arm_2d_helper_time_elapsed(&DISP0_CONSOLE.lTimestamp);
if (lTimeElapsedInMs > 255) {
DISP0_CONSOLE.chOpacity = 255;
} else {
DISP0_CONSOLE.chOpacity = lTimeElapsedInMs;
}
//disp_adapter0_printf("lTimeElapsedInMs: %lld\r\n", lTimeElapsedInMs);
}
}
#endif
}
arm_2d_canvas(ptTile, __navigation_canvas) {
if (DISP0_CONSOLE.bShowConsole) {
arm_2d_align_top_left(__navigation_canvas, 220, 200) {
draw_round_corner_box( ptTile,
&__top_left_region,
GLCD_COLOR_DARK_GREY,
(128 * DISP0_CONSOLE.chOpacity) >> 8,
bIsNewFrame);
console_box_show(&DISP0_CONSOLE.tConsole,
ptTile,
&__top_left_region,
bIsNewFrame,
DISP0_CONSOLE.chOpacity);
}
}
}
#endif
arm_lcd_text_set_target_framebuffer((arm_2d_tile_t *)ptTile);
arm_lcd_text_set_font(&ARM_2D_FONT_6x8.use_as__arm_2d_font_t);
arm_lcd_text_set_draw_region(NULL);
/* draw real-time FPS info */
if (__DISP0_CFG_ITERATION_CNT__) {
arm_2dp_fill_colour_with_opacity(
NULL,
ptTile,
(arm_2d_region_t []){
{
.tLocation = {
.iX = 0,
.iY = ((__DISP0_CFG_SCEEN_HEIGHT__ + 7) / 8 - 2) * 8},
.tSize = {
.iWidth = __DISP0_CFG_SCEEN_WIDTH__,
.iHeight = 8,
},
},
},
(__arm_2d_color_t){__RGB(64,64,64)},
255 - 32);
arm_2d_op_wait_async(NULL);
arm_lcd_text_set_colour(GLCD_COLOR_GREEN, GLCD_COLOR_WHITE);
arm_lcd_text_location((__DISP0_CFG_SCEEN_HEIGHT__ + 7) / 8 - 2,
0);
if (DISP0_ADAPTER.Benchmark.wAverage) {
arm_lcd_printf(
"FPS:%3d:%dms ",
MIN(arm_2d_helper_get_reference_clock_frequency() / DISP0_ADAPTER.Benchmark.wAverage, 999),
(int32_t)arm_2d_helper_convert_ticks_to_ms(DISP0_ADAPTER.Benchmark.wAverage));
}
#if __DISP0_CFG_SCEEN_WIDTH__ >= 240
arm_lcd_printf(
"CPU:%2.2f%% LCD-Latency:%2dms",
DISP0_ADAPTER.Benchmark.fCPUUsage,
(int32_t)arm_2d_helper_convert_ticks_to_ms(DISP0_ADAPTER.Benchmark.wLCDLatency));
#else
arm_lcd_printf(
"LCD:%2dms",
(int32_t)arm_2d_helper_convert_ticks_to_ms(DISP0_ADAPTER.Benchmark.wLCDLatency) );
#endif
}
#if __DISP0_CFG_SCEEN_WIDTH__ >= 320
/* draw verion info on the bottom right corner */
arm_lcd_text_set_colour(GLCD_COLOR_LIGHT_GREY, GLCD_COLOR_WHITE);
arm_lcd_text_location( (__DISP0_CFG_SCEEN_HEIGHT__ + 7) / 8 - 2,
(__DISP0_CFG_SCEEN_WIDTH__ / 6) - 12);
arm_lcd_printf("v"
ARM_TO_STRING(ARM_2D_VERSION_MAJOR)
"."
ARM_TO_STRING(ARM_2D_VERSION_MINOR)
"."
ARM_TO_STRING(ARM_2D_VERSION_PATCH)
"-"
ARM_2D_VERSION_STR
);
#endif
arm_2d_op_wait_async(NULL);
return arm_fsm_rt_cpl;
}
#endif
int64_t arm_2d_helper_get_system_timestamp(void)
{
return esp_timer_get_time();
}
uint32_t arm_2d_helper_get_reference_clock_frequency(void)
{
return 1000000;
}
arm_2d_disp_adapter_0.h代码如下:
/**
* @file arm_2d_disp_adapter_0.h
* @author cangyu (sky.kirto@qq.com)
* @brief
* @version 0.1
* @date 2024-06-07
*
* @copyright Copyright (c) 2024, CorAL. All rights reserved.
*
*/
#ifndef __ARM_2D_DISP_ADAPTER_0_H__
#define __ARM_2D_DISP_ADAPTER_0_H__
/* ==================== [Includes] ========================================== */
#include "arm_2d_helper_scene.h"
#include "arm_2d_helper.h"
#include "arm_2d.h"
#ifdef __cplusplus
extern "C" {
#endif
/* ==================== [Defines] =========================================== */
//-------- <<< Use Configuration Wizard in Context Menu >>> -----------------
//
// <o> Select the screen colour depth
// <8=> 8 Bits
// <16=> 16Bits
// <32=> 32Bits
// <i> The colour depth of your screen
#ifndef __DISP0_CFG_COLOUR_DEPTH__
# define __DISP0_CFG_COLOUR_DEPTH__ __GLCD_CFG_COLOUR_DEPTH__
#endif
// <o>Width of the screen <8-32767>
// <i> The width of your screen
// <i> Default: 320
#ifndef __DISP0_CFG_SCEEN_WIDTH__
# define __DISP0_CFG_SCEEN_WIDTH__ __GLCD_CFG_SCEEN_WIDTH__
#endif
// <o>Height of the screen <8-32767>
// <i> The height of your screen
// <i> Default: 240
#ifndef __DISP0_CFG_SCEEN_HEIGHT__
# define __DISP0_CFG_SCEEN_HEIGHT__ __GLCD_CFG_SCEEN_HEIGHT__
#endif
/*
ARM_SCREEN_NO_ROTATION 0
ARM_SCREEN_ROTATE_90 1
ARM_SCREEN_ROTATE_180 2
ARM_SCREEN_ROTATE_270 3
*/
// <o>Rotate the Screen
// <0=> NO Rotation
// <1=> 90 Degree
// <2=> 180 Degree
// <3=> 270 Degree
// <i> Rotate the Screen for specified degrees.
// <i> NOTE: This is extremely slow. Please avoid using it whenever it is possible.
#ifndef __DISP0_CFG_ROTATE_SCREEN__
# define __DISP0_CFG_ROTATE_SCREEN__ 0
#endif
// <o>Width of the PFB block
// <i> The width of your PFB block size used in disp0
#ifndef __DISP0_CFG_PFB_BLOCK_WIDTH__
# define __DISP0_CFG_PFB_BLOCK_WIDTH__ 240/10 //__DISP0_CFG_SCEEN_WIDTH__
#endif
// <o>Height of the PFB block
// <i> The height of your PFB block size used in disp0
#ifndef __DISP0_CFG_PFB_BLOCK_HEIGHT__
# define __DISP0_CFG_PFB_BLOCK_HEIGHT__ 240 //__DISP0_CFG_SCEEN_HEIGHT__
#endif
// <o>Width Alignment of generated PFBs
// <0=> 1 pixel
// <1=> 2 pixel
// <2=> 4 pixel
// <3=> 8 pixel
// <4=> 16 pixel
// <5=> 32 pixel
// <6=> 64 pixel
// <7=> 128 pixel
// <i> Make sure the x and width of the PFB is always aligned to 2^n pixels
#ifndef __DISP0_CFG_PFB_PIXEL_ALIGN_WIDTH__
# define __DISP0_CFG_PFB_PIXEL_ALIGN_WIDTH__ 0
#endif
// <o>Height Alignment of generated PFBs
// <0=> 1 pixel
// <1=> 2 pixel
// <2=> 4 pixel
// <3=> 8 pixel
// <4=> 16 pixel
// <5=> 32 pixel
// <6=> 64 pixel
// <7=> 128 pixel
// <i> Make sure the y and height of the PFB is always aligned to 2^n pixels
#ifndef __DISP0_CFG_PFB_PIXEL_ALIGN_HEIGHT__
# define __DISP0_CFG_PFB_PIXEL_ALIGN_HEIGHT__ 2
#endif
// <o>PFB Block Count <1-65535>
// <i> The number of blocks in the PFB pool.
#ifndef __DISP0_CFG_PFB_HEAP_SIZE__
# define __DISP0_CFG_PFB_HEAP_SIZE__ 1
#endif
// <o>Number of iterations <0-2000>
// <i> run number of iterations before calculate the FPS.
#ifndef __DISP0_CFG_ITERATION_CNT__
# define __DISP0_CFG_ITERATION_CNT__ 60
#endif
// <o>FPS Calculation Mode
// <0=> Render-Only FPS
// <1=> Real FPS
// <i> Decide the meaning of the real time FPS display
#ifndef __DISP0_CFG_FPS_CACULATION_MODE__
# define __DISP0_CFG_FPS_CACULATION_MODE__ 1
#endif
// <q> Enable Dirty Region Debug Mode
// <i> Draw dirty regions on the screen for debug.
#ifndef __DISP0_CFG_DEBUG_DIRTY_REGIONS__
# define __DISP0_CFG_DEBUG_DIRTY_REGIONS__ 0
#endif
// <q> Enable Dirty Region Optimization Service
// <i> Optimize dirty regions to avoid fresh overlapped areas
#ifndef __DISP0_CFG_OPTIMIZE_DIRTY_REGIONS__
# define __DISP0_CFG_OPTIMIZE_DIRTY_REGIONS__ 1
#endif
// <o> Dirty Region Pool Size <4-255>
// <i> The number of dirty region items available for the dirty region optimization service
#ifndef __DISP0_CFG_DIRTY_REGION_POOL_SIZE__
# define __DISP0_CFG_DIRTY_REGION_POOL_SIZE__ 8
#endif
// <q> Swap the high and low bytes
// <i> Swap the high and low bytes of the 16bit-pixels
#ifndef __DISP0_CFG_SWAP_RGB16_HIGH_AND_LOW_BYTES__
# define __DISP0_CFG_SWAP_RGB16_HIGH_AND_LOW_BYTES__ 0
#endif
// <q>Enable the helper service for Asynchronous Flushing
// <i> Please select this option when using asynchronous flushing, e.g. DMA + ISR
#ifndef __DISP0_CFG_ENABLE_ASYNC_FLUSHING__
# define __DISP0_CFG_ENABLE_ASYNC_FLUSHING__ 0
#endif
// <q>Enable the helper service for 3FB (LCD Direct Mode)
// <i> You can select this option when your LCD controller supports direct mode
#ifndef __DISP0_CFG_ENABLE_3FB_HELPER_SERVICE__
# define __DISP0_CFG_ENABLE_3FB_HELPER_SERVICE__ 0
#endif
// <q>Disable 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
// <q>Disable the navigation layer
// <i> Remove the navigation layer for this display adapter. NOTE: Disable the navigation layer will also remove the real-time FPS display.
#ifndef __DISP0_CFG_DISABLE_NAVIGATION_LAYER__
# define __DISP0_CFG_DISABLE_NAVIGATION_LAYER__ 0
#endif
// <o>Maximum number of Virtual Resources used per API
// <0=> NO Virtual Resource
// <1=> 1 Per API
// <2=> 2 Per API
// <3=> 3 Per API
// <i> Introduce a helper service for loading virtual resources.
// <i> This feature is disabled by default.
#ifndef __DISP0_CFG_VIRTUAL_RESOURCE_HELPER__
# define __DISP0_CFG_VIRTUAL_RESOURCE_HELPER__ 0
#endif
// <q>Use heap to allocate buffer in the virtual resource helper service
// <i> Use malloc and free in the virtual resource helper service. When disabled, a static buffer in the size of current display adapter PFB will be used.
// <i> This feature is disabled by default.
#ifndef __DISP0_CFG_USE_HEAP_FOR_VIRTUAL_RESOURCE_HELPER__
# define __DISP0_CFG_USE_HEAP_FOR_VIRTUAL_RESOURCE_HELPER__ 0
#endif
// <q> Enable Console
// <i> Add a simple console to the display adapter in a floating window.
// <i> This feature is disabled by default.
#ifndef __DISP0_CFG_USE_CONSOLE__
# define __DISP0_CFG_USE_CONSOLE__ 1
#endif
// <o> Console Input Buffer Size
// <i> The size of console input buffer, 0 means no input buffer
#ifndef __DISP0_CFG_CONSOLE_INPUT_BUFFER__
# define __DISP0_CFG_CONSOLE_INPUT_BUFFER__ 255
#endif
// <o> Console Display Time in ms <1000-0xFFFFFFFF>
// <i> The time before the console disappear for each content update.
#ifndef __DISP0_CFG_CONSOLE_DISPALY_TIME__
# define __DISP0_CFG_CONSOLE_DISPALY_TIME__ 3000
#endif
// <<< end of configuration section >>>
#ifndef __DISP0_COLOUR_FORMAT__
# if __DISP0_CFG_COLOUR_DEPTH__ == 8
# define __DISP0_COLOUR_FORMAT__ ARM_2D_COLOUR_GRAY8
# elif __DISP0_CFG_COLOUR_DEPTH__ == 16
# define __DISP0_COLOUR_FORMAT__ ARM_2D_COLOUR_RGB565
# elif __DISP0_CFG_COLOUR_DEPTH__ == 32
# define __DISP0_COLOUR_FORMAT__ ARM_2D_COLOUR_CCCN888
# endif
#endif
/* ==================== [Typedefs] ========================================== */
typedef void (*Disp0_DrawBitmap_t)(uint32_t x, uint32_t y, uint32_t width, uint32_t height, const uint8_t *bitmap);
/* ==================== [Global Prototypes] ================================= */
void disp_adapter0_init(Disp0_DrawBitmap_t Disp0_DrawBitmap);
arm_fsm_rt_t __disp_adapter0_task(void);
/* ==================== [Macros] ============================================ */
#define disp_adapter0_task(...) \
({ \
static bool ARM_2D_SAFE_NAME(s_bRefreshLCD) = false; \
arm_fsm_rt_t ARM_2D_SAFE_NAME(ret) = arm_fsm_rt_on_going; \
if (!__ARM_VA_NUM_ARGS(__VA_ARGS__)) { \
ARM_2D_SAFE_NAME(ret) = __disp_adapter0_task(); \
} else { \
if (!ARM_2D_SAFE_NAME(s_bRefreshLCD)) { \
/* lock framerate */ \
if (arm_2d_helper_is_time_out(1000 / (1000,##__VA_ARGS__))) { \
ARM_2D_SAFE_NAME(s_bRefreshLCD) = true; \
} \
} else { \
ARM_2D_SAFE_NAME(ret) = __disp_adapter0_task(); \
if (arm_fsm_rt_cpl == ARM_2D_SAFE_NAME(ret)) { \
ARM_2D_SAFE_NAME(s_bRefreshLCD) = false; \
} \
} \
}; \
ARM_2D_SAFE_NAME(ret);})
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif // __ARM_2D_DISP_ADAPTER_0_H__
接了这两个文件的代码后由于引入了.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/asset"
INCLUDE_DIRS "." "Arm-2D/Library/Include" "Arm-2D/Helper/Include" "Arm-2D/examples/common/controls" "Arm-2D/examples/common/asset"
REQUIRES esp_timer esp_system)
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-implicit-fallthrough -Wno-unused-variable)
target_compile_options(${COMPONENT_LIB} PRIVATE -fms-extensions)
这部分的代码真的很难理解,作者十分擅长用宏。在这样的基础之下,写下的代码如同自带一层混淆,让人难以读懂和移植。
主函数调用arm2d
到了主函数了,首先我们要引入头文件
#include "arm_2d.h"
#include "arm_2d_disp_adapter_0.h"
然后我们要对接Disp0_DrawBitmap函数
static 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_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. Can't 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-le
Sections:
Idx Name Size VMA LMA File off Algn
0 .rtc.text 00000010 600fe000 600fe000 00054000 2**0
ALLOC
1 .rtc.force_fast 00000000 600fe010 600fe010 0005374f 2**0
CONTENTS
2 .rtc_noinit 00000000 50000000 50000000 0005374f 2**0
CONTENTS
3 .rtc.force_slow 00000000 50000000 50000000 0005374f 2**0
CONTENTS
4 .rtc_reserved 00000018 600fffe8 600fffe8 00053fe8 2**3
ALLOC
5 .iram0.vectors 00000403 40374000 40374000 0001d000 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
6 .iram0.text 0000edbb 40374404 40374404 0001d404 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
7 .dram0.dummy 0000b200 3fc88000 3fc88000 0000f000 2**0
ALLOC
8 .dram0.data 0000255c 3fc93200 3fc93200 0001a200 2**4
CONTENTS, ALLOC, LOAD, DATA
9 .noinit 00000000 3fc9575c 3fc9575c 0005374f 2**0
CONTENTS
10 .dram0.bss 00004040 3fc95760 3fc95760 0001c75c 2**3
ALLOC
11 .flash.text 0002672f 42000020 42000020 0002d020 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .flash_rodata_dummy 00030000 3c000020 3c000020 00001020 2**0
ALLOC
13 .flash.appdesc 00000100 3c030020 3c030020 00001020 2**4
CONTENTS, ALLOC, LOAD, READONLY, DATA
14 arm2d.tile.c_tileWhiteDotMask 00000010 3c030120 3c030120 00001120 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
15 arm2d.tile.c_tileWhiteDotRGB565 00000010 3c030130 3c030130 00001130 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
16 arm2d.asset.c_bmpWhiteDotRGB565 00000188 3c030140 3c030140 00001140 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
17 arm2d.asset.c_bmpWhiteDotAlpha 000000c4 3c0302c8 3c0302c8 000012c8 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
18 .flash.rodata 0000d01c 3c030390 3c030390 00001390 2**4
CONTENTS, ALLOC, LOAD, DATA
19 .flash.rodata_noload 00000000 3c03d3ac 3c03d3ac 0005374f 2**0
CONTENTS
20 .ext_ram.dummy 0003ffe0 3c000020 3c000020 00001020 2**0
ALLOC
21 .ext_ram.bss 00000000 3c040000 3c040000 0005374f 2**0
CONTENTS
22 .iram0.text_end 00000041 403831bf 403831bf 0002c1bf 2**0
ALLOC
23 .iram0.data 00000000 40383200 40383200 0005374f 2**0
CONTENTS
24 .iram0.bss 00000000 40383200 40383200 0005374f 2**0
CONTENTS
25 .dram0.heap_start 00000000 3fc997a0 3fc997a0 0005374f 2**0
CONTENTS
26 .xt.prop 0002c2f8 00000000 00000000 0005374f 2**0
CONTENTS, READONLY
27 .xt.lit 000013a0 00000000 00000000 0007fa47 2**0
CONTENTS, READONLY
28 .xtensa.info 00000038 00000000 00000000 00080de7 2**0
CONTENTS, READONLY
29 .comment 0000004b 00000000 00000000 00080e1f 2**0
CONTENTS, READONLY
30 .debug_frame 00013cd8 00000000 00000000 00080e6c 2**2
CONTENTS, READONLY, DEBUGGING, OCTETS
31 .debug_info 001d8e2e 00000000 00000000 00094b44 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
32 .debug_abbrev 00028366 00000000 00000000 0026d972 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
33 .debug_loc 000d92c3 00000000 00000000 00295cd8 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
34 .debug_aranges 00007910 00000000 00000000 0036efa0 2**3
CONTENTS, READONLY, DEBUGGING, OCTETS
35 .debug_ranges 0000f150 00000000 00000000 003768b0 2**3
CONTENTS, READONLY, DEBUGGING, OCTETS
36 .debug_line 00176594 00000000 00000000 00385a00 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
37 .debug_str 000474ad 00000000 00000000 004fbf94 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
38 .debug_loclists 0000f07c 00000000 00000000 00543441 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
39 .debug_rnglists 00000418 00000000 00000000 005524bd 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
40 .debug_line_str 00001955 00000000 00000000 005528d5 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
定位了问题出在arm2d里面,好像是arm2d的某个操作导致了内存段覆盖。搜索关键词arm2d.tile,发现很多地方使用了ARM_SECTION(“arm2d.tile.c_tileUTF8UserFontA1Mask”)。知道这里很简单,找到根源然后将它注释,这个宏就不会起作用了。
大约是在components/arm2d/Arm-2D/Library/Include/arm_2d_utils.h文件的620行我找到了这个宏,并将它设置为空
/*!
* \note do NOT use this macro directly
*/
// #define __ARM_SECTION(__X) __attribute__((section(__X)))
#define __ARM_SECTION(__X)
/*!
* \brief an attribute to specify the section
* \note it works for both functions and static/global variables
*/
#ifndef ARM_SECTION
# define ARM_SECTION(__X) __ARM_SECTION(__X)
#endif
至此,我们编译终于成功。虽然这时候还有一些warning没有消除(arm2d被调用的时候产生的warning)。但是我也无力追求完美了。直接编译,烧录。
总结
其实我的结果并不是很好。按照傻孩子大佬的话说,还是有很大优化空间。下一步优化应该就是在spi异步传输的方向上。乐鑫的spi分为queue传输和poll传输,其中bus库采用的是poll传输。然而这种方式相当于同步操作,应该用queue去异步等待,这样能在传输的同时计算像素。能够消除LCD-Latency的时间。我这次记录摸索过程相当于是抛砖引玉,希望大家能够优化出更好的版本
后记(碎碎念)
arm2d的理念是以mask为中心,所有的东西全都是贴图加上arm2d自带的蒙版(mask)完成的效果。这个理念实际上不是gui的理念,例如lvgl是以控件为中心。arm2d则是更加底层, 从使用者的角度实际上会比较麻烦,但是这种效果能够在性能有限的设备上发挥很大的效果。实际上arm2d的源码一度让我崩溃,以宏构建的内容, 很多情况下无从知晓如何使用。代码的抽象程度已经完全是另一种语言。这次的移植意义也不大,因为esp32性能足够,有更加有好的lvgl,没必要折腾arm2d。主要是想学习学习,也想挑战一下自己。arm2d的代码在我看来我不能评价他是不好的,毕竟恐怖的效率,惊人的效果还是深深折服。但是我个人还是不会学习他的做法,我希望能写出更加清晰易懂的代码。