ESP32-S3 点亮 AXS15260 屏并适配 LVGL 9 完整指南

发布时间:2026/8/31 10:45:04
ESP32-S3 点亮 AXS15260 屏并适配 LVGL 9 完整指南 在嵌入式 GUI 开发中ESP32-S3 搭配小尺寸 RGB/LCD 显示屏是常见组合真正麻烦的往往不是画界面而是先把一块不熟悉的屏幕点亮再把 LVGL 9 的显示驱动、输入驱动、字库和动画正确对接起来。本文围绕esp32s31_3.97寸axs15260_lvgl9这个项目完整梳理从环境搭建、AXS15260 初始化、LVGL 9 移植到性能调优和问题排查的一条链路。适合正在做 ESP32-S3 人机界面、想从 Arduino 或裸机显示逻辑切换到 LVGL或者已经在用 LVGL 8 想了解 9.x 适配差异的开发者。## 1. 先理解 AXS15260 屏幕与 LVGL 9 之间的适配关系 ### 1.1 AXS15260 是什么为什么移植前要先确认接口类型 AXS15260 是一颗常见于中小尺寸屏幕的 LCD 驱动控制 IC3.97 英寸这个规格下常见分辨率是 480x800 竖屏。也就是说它并不是一块「SPI 显示屏」而是一颗需要由主控通过特定总线发送初始化命令、再持续刷新像素数据的驱动芯片。 在 ESP32-S3 项目里AXS15260 的接口形式可能有几种 - 8/9/16/18 bit MCU 并口 - RGB 接口 - SPI 控制脚组合 - MIPI DSI少见一般需要 DSI 控制器或转换桥 所以第一步不是写代码而是看原理图、看屏厂规格书、确认这屏是哪种接口、供电电压、背光引脚、复位引脚、TE 引脚是否引出。尤其是 ESP32-S3 本身没有 RGB 并口原生控制器很多情况下是通过 LCD_CAM 外设或者 GPIO 拉时序来完成。不同接口对应的 ESP-IDF 配置完全不同一旦判断错误后面所有 LVGL 配置都会建立在错误基础上。 ### 1.2 LVGL 9 与 LVGL 8 在驱动层的关键差异 LVGL 9 在显示驱动和输入驱动的 API 上做了一次较大的整理。LVGL 8 里常用的 lv_disp_drv_t、lv_disp_draw_buf_t到了 LVGL 9 中变成了 lv_display_t 和 lv_display_set_flush_cb初始化入口也从 lv_init 演变为 lv_init 后必须显式创建 display 对象。 LVGL 9 对开发者影响最直接的点包括 - 显示驱动初始化代码写法变了不能用旧版的 lv_disp_drv_register 直接编译。 - 颜色格式统一走 lv_color_format_t例如 LV_COLOR_FORMAT_ARGB8888、LV_COLOR_FORMAT_RGB565。 - 缓冲区管理对象改为 lv_draw_buf_t分配和释放需要走 lv_draw_buf_create / lv_draw_buf_destroy。 - 输入设备驱动改为 lv_indev_create lv_indev_set_read_cb。 这些 API 变化意味着网络上的 LVGL 8 教程不能在 LVGL 9 中直接照抄。尤其是从 ESP32-S3 驱动 AXS15260 这种 RGB/并口屏时如果直接复制旧版 flush_cb 回调编译不过或者刷新异常是必然的。 ### 1.3 从屏幕到 UI 的完整数据链路 把 AXS15260 跑起来再运行 LVGL 9 界面核心链路是这样的ESP32-S3 主控 - 初始化 AXS15260 寄存器复位、扫描方向、显示开关 - 准备显存缓冲LVGL 9 draw buffer - 注册 display flush 回调 - LVGL 9 构建控件树并绘制到 buffer - flush_cb 把 buffer 数据通过总线写进 AXS15260 - 触摸/按键输入通过 lv_indev 回调回报给 LVGL 9理解这条链路后每个环节的调试方向就清楚了黑屏查初始化、花屏查像素格式和时序、刷新慢查缓冲策略和 DMA、触摸漂移查坐标转换。下文按这条链路逐步展开。 ## 2. 搭建 ESP32-S3 开发环境与工程目录 ### 2.1 使用 ESP-IDF 还是 Arduino先按项目复杂度选择 ESP32-S3 上跑 LVGL 9 有两条主流路线一条是基于 ESP-IDF 的组件化工程一条是基于 Arduino 框架的 .ino 工程。如果只是快速验证一个界面Arduino 上手快如果要长期迭代、做产品、接入 AWS/蓝牙/多任务ESP-IDF 更合适。 | 对比项 | ESP-IDF | Arduino-ESP32 | | --- | --- | --- | | 版本管理 | 组件 版本依赖清晰 | 库管理简单 | | LVGL 9 适配 | 通过 idf_component.yml 或手动源码加入 | 通过库管理器或源码目录 | | 多任务 | 原生 FreeRTOS | 兼容 FreeRTOS但封装程度高 | | 外设驱动控制 | 寄存器级灵活 | 可能没有现成 AXS15260 驱动 | | 排错难度 | 日志更底层信息更完整 | 简单但封装带来黑盒 | 考虑到 AXS15260 不是那种几行 Arduino 库就能驱动的通用屏幕工程上更推荐 ESP-IDF。即使驱动已经有人封装好ESP-IDF 也能更方便地看日志、改时序和调 DMA。 ### 2.2 推荐工程目录结构 一个可维护的 LVGL 9 ESP32-S3 工程不要把所有 .c 文件堆在 main 里。建议按功能拆开esp32s31_axs15260_lvgl9/ ├── CMakeLists.txt ├── main/ │ ├── CMakeLists.txt │ ├── main.c │ ├── lvgl_app/ │ │ ├── ui_screen.c │ │ ├── ui_screen.h │ │ └── ui_common.h │ ├── dev/ │ │ ├── axs15260.c │ │ ├── axs15260.h │ │ ├── axs15260_regs.h │ │ ├── touch.c │ │ └── touch.h │ └── config/ │ └── board_config.h ├── components/ │ └── lvgl/ # 或者用 idf_component.yml 引入 └── sdkconfigboard_config.h 里放引脚和屏幕参数axs15260.c 只做屏幕初始化与数据写入lvgl_app 里只放 UI 代码。这样后续换屏幕或改引脚时不会把驱动和界面混在一起改。 ### 2.3 引入 LVGL 9 的方式 以 ESP-IDF 为例推荐用 idf_component.yml 声明依赖避免手动合并源码。 yaml dependencies: lvgl/lvgl: version: ^9.2如果网络环境不方便拉取组件管理器也可以直接从 LVGL 官方仓库下载 9.x 源码放到components/lvgl但要注意保留lvgl目录下的lvgl.h和配置文件。LVGL 9 的裁剪配置通常放在lv_conf.h需要从lvgl仓库复制模板并启用#define LV_COLOR_DEPTH 16 #define LV_MEM_SIZE (32 * 1024) #define LV_DRAW_SW_SHADOW 1 #define LV_USE_LOG 1 #define LV_LOG_LEVEL LV_LOG_LEVEL_WARN注意LV_COLOR_DEPTH要和实际 AXS15260 屏的像素格式一致。如果屏幕是 RGB565而 LVGL 使用 ARGB8888会在 flush 回调里多一次颜色转换降低刷新效率。3. 点亮 AXS15260初始化序列与显示驱动接入3.1 引脚定义与背光控制在写初始化代码前先把引脚定义整理清楚。不同板子的接线差异很大下面示例只说明通常需要哪些引脚实际要以原理图为准。// board_config.h #define PIN_LCD_RST 4 #define PIN_LCD_DC 5 #define PIN_LCD_CS 6 #define PIN_LCD_CLK 7 #define PIN_LCD_DATA 8 #define PIN_LCD_BL 9 #define PIN_TOUCH_SDA 10 #define PIN_TOUCH_SCL 11 #define PIN_TOUCH_INT 12如果 AXS15260 是 RGB 接口还会用到PCLK、HSYNC、VSYNC、DE、D0-D15这一组信号数量比 SPI 屏多很多。这时候引脚定义表就不只是几个 GPIO 的事必须做成数组并且要确认 ESP32-S3 的 GPIO 矩阵是否支持对应的外设信号映射。RGB 屏的 PCLK 频率比较高建议优先使用 ESP32-S3 的专用 LCD 外设引脚避免全部依赖 GPIO 矩阵绕线后出现信号时序问题。背光不是「给电就亮」这么简单。很多 AXS15260 模块的背光由 PWM 控制亮度可调ledc_timer_config_t timer { .speed_mode LEDC_LOW_SPEED_MODE, .duty_resolution LEDC_TIMER_8_BIT, .timer_num LEDC_TIMER_0, .freq_hz 1000, .clk_cfg LEDC_AUTO_CLK, }; ledc_timer_config(timer); ledc_channel_config_t channel { .gpio_num PIN_LCD_BL, .speed_mode LEDC_LOW_SPEED_MODE, .channel LEDC_CHANNEL_0, .timer_sel LEDC_TIMER_0, .duty 128, .hpoint 0, }; ledc_channel_config(channel);先不急着调亮度先把背光设置为一个固定值确认屏能亮再接入 PWM。3.2 AXS15260 初始化时序AXS15260 的初始化通常包含三部分复位、延时、写入寄存器序列。不同刷新率、扫描方向、RGB 接口时序对应的寄存器值不同建议直接以屏厂提供的初始化代码为基准。通用初始化流程如下void axs15260_init(void) { gpio_set_level(PIN_LCD_RST, 0); vTaskDelay(pdMS_TO_TICKS(20)); gpio_set_level(PIN_LCD_RST, 1); vTaskDelay(pdMS_TO_TICKS(120)); axs15260_send_cmd(0xB0); // page select axs15260_send_data(0x00); axs15260_send_cmd(0x11); // sleep out vTaskDelay(pdMS_TO_TICKS(120)); // 根据屏规格书填充寄存器序列 axs15260_send_cmd(0x36); axs15260_send_data(0x00); // 扫描方向 axs15260_send_cmd(0x35); // TE enable axs15260_send_data(0x00); axs15260_send_cmd(0x29); // display on }这个示例只是说明流程0xB0、0x36这些寄存器含义必须对照 AXS15260 数据手册确认。最容易踩坑的是「寄存器页」机制AXS15260 这类 IC 可能通过 0xFF 或 0xB0 切换寄存器页不同页下相同命令地址含义完全不同。如果初始化后花屏但背光亮优先怀疑寄存器序列不完整或扫描方向、像素格式不匹配。3.3 对接 LVGL 9 的 flush 回调LVGL 9 里显示驱动的注册方式和旧版不同核心是创建lv_display_t并设置 flush 回调。下面给出一个 RGB565 场景的示例static lv_display_t *display; void lvgl_display_init(void) { lv_init(); display lv_display_create(LCD_WIDTH, LCD_HEIGHT); lv_display_set_flush_cb(display, lcd_flush_cb); lv_display_set_color_format(display, LV_COLOR_FORMAT_RGB565); static lv_draw_buf_t draw_buf; lv_draw_buf_init(draw_buf, LCD_WIDTH, LCD_HEIGHT, LV_COLOR_FORMAT_RGB565, 0); lv_draw_buf_t *buf1 lv_draw_buf_create(LCD_WIDTH, LCD_HEIGHT, LV_COLOR_FORMAT_RGB565, 0); lv_draw_buf_t *buf2 lv_draw_buf_create(LCD_WIDTH, LCD_HEIGHT, LV_COLOR_FORMAT_RGB565, 0); lv_display_set_draw_buffers(display, buf1, buf2); }注意如果 AXS15260 是 800x480 分辨率和 RGB565单缓冲就是 800 * 480 * 2 768000 字节双缓冲会接近 1.5MB。ESP32-S3 通常外挂 PSRAM可以放得下但内部 SRAM 放不下。所以分配 draw buffer 时要用大内存或 PSRAM 分配方式例如 ESP-IDF 中的heap_caps_malloc(size, MALLOC_CAP_SPIRAM)。lcd_flush_cb的职责很简单把 LVGL 输出的像素数据发送给 AXS15260 显示。发送完成后必须调用lv_display_flush_ready否则 LVGL 会认为上一次刷新没有完成后续区域不再重绘表现为界面卡住或只绘制一个局部区域。static void lcd_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) { axs15260_set_window(area-x1, area-y1, area-x2, area-y2); axs15260_push_pixels(px_map, (area-x2 - area-x1 1) * (area-y2 - area-y1 1) * 2); lv_display_flush_ready(disp); }这里有个常见误区和 LVGL 9 的细节px_map的格式必须和lv_display_set_color_format设置的一致。如果 LVGL 以为自己是 ARGB8888AXS15260 实际接收 RGB565就会出现颜色错乱。如果确实需要 16 位颜色应该统一配置为 RGB565而不是在 flush 回调里临时转换。3.4 常用排错点AXS15260 点不亮时按顺序排查电源和地是否稳定电流是否足够。3.97 寸屏背光全亮时电流不小USB 供电经常掉压。复位时序是否正确。有些模块复位脚低电平时间要求大于 10ms上电后要等待 120ms。初始化是否完成了 sleep out 和 display on。像素时钟、HSYNC、VSYNC 时序参数是否匹配。LVGL 9 draw buffer 是否分配成功。flush 回调里是否漏掉lv_display_flush_ready。4. 在 LVGL 9 中构建 UI控件、字体与动画4.1 LVGL 9 基础初始化的完整流程在lvgl_display_init里执行lv_init之后还需要一个时间基准源来驱动 LVGL 的计时和动画。ESP-IDF 下常见实现是用 FreeRTOS 的xTaskGetTickCountstatic uint32_t lvgl_tick_get(void) { return (uint32_t)xTaskGetTickCount(); } void lvgl_tick_init(void) { lv_tick_set_cb(lvgl_tick_get); }主循环里调用lv_timer_handler()处理 LVGL 事件。不要把lv_task_handler这种 LVGL 8 的旧函数写进来LVGL 9 已经改名。while (1) { lv_timer_handler(); vTaskDelay(pdMS_TO_TICKS(5)); }4.2 创建第一屏容器、标签和仪表盘LVGL 9 的控件 API 和 LVGL 8 基本一致但对象创建后不再默认居中需要自己设置布局或坐标。下面示例创建背景容器、一个标题标签和一个仪表盘void ui_screen_create(void) { lv_obj_t *scr lv_screen_active(); lv_obj_set_style_bg_color(scr, lv_color_hex(0x1a1a2e), 0); lv_obj_t *cont lv_obj_create(scr); lv_obj_set_size(cont, 440, 760); lv_obj_center(cont); lv_obj_set_layout(cont, LV_LAYOUT_FLEX); lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN); lv_obj_set_style_pad_row(cont, 16, 0); lv_obj_t *label lv_label_create(cont); lv_label_set_text(label, AXS15260 LVGL 9); lv_obj_set_style_text_font(label, lv_font_montserrat_20, 0); lv_obj_t *gauge lv_meter_create(cont); lv_obj_set_size(gauge, 240, 240); // 添加刻度、指针等省略 }LVGL 9 中lv_screen_active()替代了旧版的lv_scr_act()。如果是从 LVGL 8 项目迁移这里也是必改点。4.3 中文字体处理LVGL 默认字体通常只有 ASCII中文需要转换为.c字库文件。常用的做法有两种使用 LVGL 官方提供的在线字体转换工具选择需要的字符集并生成 C 数组。使用脚本从 TTF 字体提取指定汉字范围并生成字库。转换后字体文件导入工程extern const lv_font_t font_cn_20; lv_obj_t *label lv_label_create(scr); lv_label_set_text(label, 温度 25.6°C); lv_obj_set_style_text_font(label, font_cn_20, 0);在中文字体上最常遇到的问题不是渲染不出来而是.c文件非常大。一个 20 像素字号的完整中文字库可能超过 1MB如果内部 flash 紧张要按实际用到的字符做裁剪或者使用字体子集化工具把字符范围限制在 100 个以内。4.4 动画与仪表盘刷新LVGL 9 的动画 API 在lv_anim基础上重命名为lv_anim_t的初始化函数使用lv_anim_start启动。适合配合仪表盘做数值平滑变化static void set_gauge_value(void *obj, int32_t v) { lv_meter_set_indicator_value(gauge, needle, v); } void gauge_anim_create(void) { lv_anim_t a; lv_anim_init(a); lv_anim_set_var(a, gauge); lv_anim_set_exec_cb(a, set_gauge_value); lv_anim_set_values(a, 0, 100); lv_anim_set_time(a, 1200); lv_anim_set_path_cb(a, lv_anim_path_ease_out); lv_anim_start(a); }动画卡顿通常不源于动画本身而在于单帧绘制时间过长。如果刷新一帧需要 50ms动画再顺滑看起来也只有 20fps。优化方向是减小刷新区域、缩小 draw buffer 为局部刷新模式、减少阴影和模糊等重绘制效果。5. 接入触摸与按键事件5.1 触摸芯片与 LVGL 输入设备AXS15260 是显示驱动 IC触摸部分通常由独立的触摸控制器完成。模块上可能是电容触摸芯片也可能不带触摸。触摸芯片通过 I2C 读取触点坐标再把坐标送入 LVGL。LVGL 9 输入设备的注册方式static lv_indev_t *touch_indev; void touch_lvgl_init(void) { touch_indev lv_indev_create(); lv_indev_set_type(touch_indev, LV_INDEV_TYPE_POINTER); lv_indev_set_read_cb(touch_indev, touch_read_cb); }touch_read_cb每次被 LVGL 调用时向lv_indev_data_t写入坐标和按下状态。坐标必须换算为 LVGL 坐标系static void touch_read_cb(lv_indev_t *indev, lv_indev_data_t *data) { uint16_t x, y; uint8_t pressed touch_read_point(x, y); if (pressed) { >static void switch_event_handler(lv_event_t *e) { lv_obj_t *sw lv_event_get_target(e); bool checked lv_obj_has_state(sw, LV_STATE_CHECKED); const char *txt checked ? ON : OFF; lv_label_set_text(state_label, txt); } lv_obj_t *sw lv_switch_create(cont); lv_obj_add_event_cb(sw, switch_event_handler, LV_EVENT_VALUE_CHANGED, NULL);常见现象是 switch 按下后 UI 状态不更新。原因通常是事件类型用错比如只注册了LV_EVENT_CLICKED而不是LV_EVENT_VALUE_CHANGED。LVGL 的 checkable 控件在用户点击后会改变自身 state同时发送LV_EVENT_VALUE_CHANGED应该在VALUE_CHANGED里读取状态并同步文本。如果在这里读取不到新状态检查控件是否设置了LV_OBJ_FLAG_CHECKABLE未设置该 flag 的控件不会在点击时自动切换。6. 运行验证与性能优化6.1 用日志和帧率确认系统正常跑通之后不要只看界面漂亮还要量化验证。打印lv_timer_handler周期。在flush_cb里记录一帧刷新耗时。查看内存占用esp_get_free_heap_size()和 PSRAM 剩余空间。确认 LVGL 日志级别LVGL 9 的日志能直接显示缓冲区分配失败、字体缺失等问题。ESP_LOGW(MAIN, heap free: %d, psram free: %d, heap_caps_get_free_size(MALLOC_CAP_8BIT), heap_caps_get_free_size(MALLOC_CAP_SPIRAM));6.2 LVGL 9 缓冲区策略与刷新性能AXS15260 是 480x800 还是 800x480决定了刷新缓冲设计。LVGL 9 支持整屏缓冲也支持局部缓冲。对于带 PSRAM 的 ESP32-S3可以配双整屏缓冲LVGL 绘制一屏的同时 DMA 发送上一屏帧率更稳定。缺点是内存占用大。缓冲策略内存占用RGB565帧率适用场景单局部缓冲1/10 屏幕约 77KB较低内存紧张单整屏缓冲约 768KB中等对帧率不敏感双整屏缓冲约 1.5MB较高流畅动画和触摸如果不想吃太多 PSRAM可以把缓冲缩小到 1/4 屏LVGL 会分段重绘。但对仪表盘动画这类频繁重绘的场景小缓冲会明显降低帧率。实际项目里建议先跑双整屏缓冲确认帧率后再调小不要一开始就追求省内存。6.3 PSRAM 与 DMA 的配合AXS15260 数据量很大最好使用 DMA 发送避免 CPU 长期忙于逐字节写寄存器。在 ESP-IDF 中如果使用 SPI 接口发送像素可以在 SPI transaction 里加SPI_TRANS_USE_TXDATA或直接传 buffer 指针。RGB 接口屏在 ESP32-S3 上通常用 LCD_CAM 外设做持续扫描。这时 LVGL 的flush_cb只需要把绘制完成的 buffer 写到某个队列或地址由 LCD_CAM 外设持续往屏幕送数据。这种方式 CPU 占用低但初始化代码更复杂需要配置lcd_cam_panel_config_t和各同步信号极性。这个环节最容易出错建议先使用屏厂示例驱动跑通后再套 LVGL 9。7. 常见问题排查清单7.1 黑屏、花屏、刷新慢的问题表问题现象常见原因检查方法处理建议背光不亮背光引脚未配置或 PWM 占空比为 0示波器测 BL 引脚电平先固定高电平确认硬件黑屏但背光亮AXS15260 初始化失败查看初始化日志、确认复位时序检查 sleep out、display on 命令花屏/颜色错乱像素格式不一致检查 LVGL color_format 和屏输入格式统一 RGB565 或 ARGB8888只显示部分画面flush 后未调用 flush_ready代码审查加日志在 flush_cb 末尾补齐触摸位置漂移显示旋转但触摸未旋转按安装方向做坐标映射参考第 5.2 节映射表switch 点击无变化事件类型错误或缺少 CHECKABLE注册日志回调使用 VALUE_CHANGED 事件中文显示为方块字体文件未加载或字库缺失检查字库范围和 lv_label font裁剪必要字符并重新生成字库刷新慢单缓冲且每次局部重绘打印 flush 耗时增大 draw buffer开启双缓冲7.2 启动即重启的问题ESP32-S3 上电后反复重启往往和内存分配失败或看门狗有关。LVGL 9 默认运行lv_timer_handler时要连续调用如果在定时器回调里执行重活比如在 LVGL 事件回调中做长耗时vTaskDelay可能导致任务被看门狗重置。检查方式idf.py monitor观察最后输出到哪一步。如果日志停在Fatal exception且提示LoadStoreError大概率是操作了空指针或 PSRAM 未初始化。如果在初始化 display 之前调用 LVGL 控件 API也会出问题必须严格按lv_init - lv_display_create - lv_indev_create - 创建 UI顺序执行。7.3 尺寸和分辨率不匹配的问题LVGL 9 的坐标系统以lv_display_create时的宽高为准。如果传了800, 480但屏幕实际是480, 800显示内容会整体偏移或只占屏幕一半。排查这类问题时先打印lv_display_get_horizontal_resolution和lv_display_get_vertical_resolution确认 LVGL 认为的分辨率。再检查 AXS15260 的扫描方向寄存器是否设置了BGR、地址递增方向这会影响画面是否有镜像或倒置问题。8. 最佳实践与下一步方向8.1 可复用的发布前检查清单在 AXS15260 LVGL 9 项目收尾前建议过一遍以下清单原理图与代码引脚定义是否一一对应。AXS15260 初始化寄存器序列是否来源于屏厂确认版本。LVGL 9 color format 与屏幕输入格式是否一致。flush_cb 是否在所有分支都调用了lv_display_flush_ready。draw buffer 是否分配在 PSRAM是否使用了双缓冲。触摸坐标是否随屏幕方向做了映射。中文文本使用的字体是否包含全部字符。LVGL 日志在发布版本中是否关闭优化。长时间运行后内存是否稳定是否有内存泄漏。电源是否满足屏幕高亮时的电流需求。8.2 生产环境下的额外保障从能跑通到能上线还要补齐以下环节配置外置化分辨率、引脚、背光亮度、触摸翻转方向都放到配置头文件或配置文件里避免换屏后改多处。日志与监控LVGL 层的告警、内存水位、刷新耗时都应该有日志接口便于现场定位。低功耗设计AXS15260 有 sleep 模式LVGL 9 检测到长时间无操作时可以进入休眠并关闭背光唤醒后再刷新全屏。OTA 与回滚如果固件包含字库和 UI 资源升级失败时要能回到上一版本避免整机变砖。看门狗与异常处理不要在 LVGL 事件回调里等待网络或读 SD 卡耗时操作移到独立任务通过消息队列把结果回传到 UI 层。8.3 下一步可以扩展的方向如果当前项目已经稳定运行下一步可以从三个方向深入把 UI 设计和代码分离使用 LVGL 9 的可视化工具生成界面减少手写控件布局的重复劳动。研究多屏异显或双屏镜像理解 ESP32-S3 的 LCD_CAM 外设在多显示控制器下的调度方式。尝试把 AXS15260 的刷新和 LVGL 的渲染放到不同核心上用 FreeRTOS 任务绑定 CPU进一步降低丢帧率。对新手来说最有价值的练习不是追求炫酷动画而是把显示、触摸、刷新性能这三条链路分别写清楚并加日志验证。能准确回答“一帧花多久、内存剩多少、触摸和图像有没有对正”这个项目才算真正掌握而不是只复制了一个能显示的 demo。