基于esp-idf的arm2d移植

什么是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_WIDTHGLCD_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的代码在我看来我不能评价他是不好的,毕竟恐怖的效率,惊人的效果还是深深折服。但是我个人还是不会学习他的做法,我希望能写出更加清晰易懂的代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/725774.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

怎么生成活码类型的二维码?在线制作活码的简单方法

活码是现在很多人会选择使用的一种二维码类型&#xff0c;制作活码二维码可以展现更多类型的内容&#xff0c;而且二维码可以随时在图案不变的情况下修改内容&#xff0c;与静态码相比使用起来更加的灵活。目前&#xff0c;活码可以用来展示图片、视频、音频、文件、网址、表单…

一个小例子助你彻底理解协程

一个小例子助你彻底理解协程 协程&#xff0c;可能是Python中最让初学者困惑的知识点之一&#xff0c;它也是Python中实现并发编程的一种重要方式。Python中可以使用多线程和多进程来实现并发&#xff0c;这两种方式相对来说是大家比较熟悉的。事实上&#xff0c;还有一种实现…

css 文字两端对齐

<body><div class"box"><p>姓名</p><p>性与别</p><p>家庭住址</p><p>how are you</p><p>hello</p><p>1234</p><p>1 2 3 4</p></div> </body> text-a…

Java零基础之多线程篇:线程的多种创建方式

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

【递归、搜索与回溯】综合练习四

综合练习四 1.单词搜索2.黄金矿工3.不同路径 III 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f603; 1.单词搜索 题目链接&#xff1a;79. 单词搜…

前端路线指导(3):前端进阶版学习路线

前端进阶版学习路线&#xff1a; 哈喽大家好&#xff01;我是小粉&#xff0c;双一流本科&#xff0c;自学前端一年&#xff0c;收获腾讯&#xff0c;字节等9家互联网大厂offer&#xff0c;秋招面试通过率100%&#xff0c;其中半数offer为ssp&#xff08;薪资最高档&#xff09…

如何查看公网IP?

什么是公网IP&#xff1f; 公网IP&#xff08;Internet Protocol&#xff09;是指分配给互联网上的计算机设备的唯一标识符。公网IP地址是由互联网服务提供商&#xff08;ISP&#xff09;分配给用户设备&#xff0c;使其可以与全球范围内的其他设备进行通信。公网IP地址通常采…

【超越拟合:深度学习中的过拟合与欠拟合应对策略】

如何处理过拟合 由于过拟合的主要问题是你的模型与训练数据拟合得太好&#xff0c;因此你需要使用技术来“控制它”。防止过拟合的常用技术称为正则化。我喜欢将其视为“使我们的模型更加规则”&#xff0c;例如能够拟合更多类型的数据。 让我们讨论一些防止过拟合的方法。 获…

代理模式(静态代理/动态代理)

代理模式&#xff08;Proxy Pattern&#xff09; 一 定义 为其他对象提供一种代理&#xff0c;以控制对这个对象的访问。 代理对象在客户端和目标对象之间起到了中介作用&#xff0c;起到保护或增强目标对象的作用。 属于结构型设计模式。 代理模式分为静态代理和动态代理。…

视频监控统一管理平台LntonCVS安防视频监控系统视频汇聚方案

LntonCVS平台最初被设计为一个以视频汇聚为核心的平台。那么&#xff0c;什么是视频汇聚平台&#xff0c;以及它是如何处理视频资源的呢&#xff1f;简单来说&#xff0c;视频汇聚平台能够从不同的视频源&#xff08;如直播和点播&#xff09;收集、整合和展示视频内容。以下是…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 身高差值排序(100分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

vue3+element ui +ts 封装周范围选择器

vue3element ui ts 封装周范围选择器 在业务场景中&#xff0c;产品需要在页面中使用周范围选择器&#xff0c;我们在使用ant-design的时候里面是有自带的&#xff0c;但是在emement中只有指定周的范围选择器&#xff1a; 这个是ant-design的周范围选择器 这个是element ui 的…

.net 奇葩问题调试经历之1——在红外相机获取温度时异常

📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:新的征程,我们面对的不仅仅是技术还有人心,人心不可测,海水不可量,唯有技术,才是深沉黑夜中的一座闪烁的灯塔序言 我们在研发中,经常除了造产品…

C#的Switch语句2(case后的值与模式匹配)

文章目录 switch语法结构case具体的值枚举值字符串const关键字 如果没有匹配的值default语句不一定要在最后 模式匹配与C的差异-case穿透&#xff08;Fall-through&#xff09;下一篇文章 switch语法结构 基础的语法结构&#xff0c;在上一篇文章已经写了&#xff0c;具体请看…

Shiro550 反序列化漏洞(CVE-2016-4437)

目录 Shiro介绍 漏洞原理 判断是否存在漏洞 利用ShiroExploit工具执行命令&#xff1a; 利用shiro-exploit工具综合利用工具执行命令&#xff1a; 这一篇是参考别的师傅的好文章对Shiro550反序列化漏洞的学习和练习 Shiro介绍 Apache Shiro是一个强大易用的java安全框架…

ASP.NET MVC企业级程序设计(增删,页面水平排列,字符串拼接,非空,添加框内默认提示)

目录 题目&#xff1a; 实现过程 控制器代码 DAL BLL Index Deile 题目&#xff1a; 实现过程 控制器代码 using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using MvcApplication1.Models;namespac…

Tensorflow入门实战 T05-运动鞋识别

目录 一、完整代码 二、训练过程 &#xff08;1&#xff09;打印2行10列的数据。 &#xff08;2&#xff09;查看数据集中的一张图片 &#xff08;3&#xff09;训练过程&#xff08;训练50个epoch&#xff09; &#xff08;4&#xff09;训练结果的精确度 三、遇到的问…

Docker环境离线安装

Docker环境离线安装 下载下列.deb包 sudo *.deb

【PyQt5】python桌面级应用开发:PyQt5介绍,开发环境搭建快速入门

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

天津媒体邀约,及媒体名单?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 媒体宣传加速季&#xff0c;100万补贴享不停&#xff0c;一手媒体资源&#xff0c;全国100城线下落地执行。详情请联系胡老师。 天津作为中国北方的重要城市&#xff0c;拥有丰富的媒体资…