Part1 无缝移植 EEZ Studio 生成的 UI 工程
在成功驱动屏幕并移植好 LVGL 之后,下一个关键步骤就是将我们在 EEZ Studio 中精心设计的 UI 界面,真正地运行在 ESP32 硬件上。本文将提供一份清晰、完整的指南,帮助你完成从 EEZ Studio 到 ESP-IDF 工程的移植工作。
一、移植前的准备工作
重要提示: 请确保你的 ESP32 工程已经成功驱动了显示屏幕,并完成了 LVGL 库的移植。本教程的所有操作都基于这两个前提,否则无法进行。
二、开始移植
我们将通过创建一个自定义组件的方式,将 EEZ 生成的 UI 文件优雅地集成到 ESP-IDF 工程结构中。
1. 创建 UI 组件
在 ESP-IDF 工程的 components 目录下,使用以下命令创建一个名为 ui 的新组件:
# 在工程根目录下打开终端,或使用 ESP-IDF 插件提供的命令创建
idf.py create-component ui或者,如果你使用的是 VS Code 的 ESP-IDF 插件,可以通过命令面板执行 ESP-IDF: Create New ESPIDF-Component。
2. 清理组件目录
创建完成后,进入 components/ui 文件夹,删除该目录下自动生成的 ui.c 和 include 文件夹,只保留 CMakeLists.txt 文件。
3. 修改 CMakeLists.txt
打开 components/ui/CMakeLists.txt,将其内容替换为以下配置。这告诉编译系统如何构建我们的 UI 组件,并声明它依赖于 lvgl 和 esp_lvgl_port。
idf_component_register(
SRC_DIRS "." # 源文件就在当前目录
INCLUDE_DIRS "." # 头文件也在当前目录
REQUIRES lvgl esp_lvgl_port # 声明依赖
)4. 复制 EEZ Studio 生成的文件
现在,将你在 EEZ Studio 中生成的所有 UI 文件(通常包括 ui.c, ui.h, screens.c,以及可能存在的资源文件等),全部复制到 components/ui/ 目录下。
小贴士: 如果你的开发环境是 Windows(运行 EEZ Studio)和 Linux(编写 ESP-IDF)分离的,手动复制是最直接的方式。你也可以考虑在 EEZ Studio 中设置输出路径到共享文件夹,但手动操作更简单可控。
移植完成后的文件结构应该类似于:
your_project/
├── components/
│ └── ui/
│ ├── CMakeLists.txt
│ ├── ui.c
│ ├── ui.h
│ ├── screens.c
│ ├── images/ (如果生成了图片文件夹)
│ │ └── ...
│ └── ...
├── main/
│ ├── CMakeLists.txt
│ ├── main.c
│ └── ...
└── ...5. 在 main 组件中引用 UI 组件
为了让 main 组件能够找到并使用我们新建的 ui 组件,需要修改两处地方:
5.1 修改 main 组件的 CMakeLists.txt
打开 main/CMakeLists.txt,在 REQUIRES 列表中添加 ui,确保 main 组件在链接时能找到 ui 组件的符号。
idf_component_register(
SRCS "main.c"
INCLUDE_DIRS "."
REQUIRES ui # 添加 ui 依赖
)5.2 在 main.c 中包含头文件
在 main/main.c 文件中,添加必要的头文件引用:
#include "ui.h" // EEZ Studio 生成的 UI 主头文件
#include "esp_lvgl_port.h" // ESP32 的 LVGL 端口,用于线程安全操作6. 初始化 UI
UI 的初始化必须在 LCD 初始化完成并且 LVGL 就绪之后进行。为了线程安全,所有 LVGL 相关的操作(包括创建 UI 对象)都应放在 lvgl_port_lock 和 lvgl_port_unlock 之间执行。
在 main.c 中,找到初始化 LCD 和 LVGL 之后的代码位置,添加以下内容:
// ... 前面是 LCD 和 LVGL 初始化代码 ...
// 初始化 UI 组件(在 LVGL 锁内进行对象创建)
lvgl_port_lock(0); // 0 表示无限等待锁
ui_init(); // 调用 EEZ 生成的初始化函数
lvgl_port_unlock();
// ... 后续代码 ...7. 创建 UI 刷新任务
EEZ Studio 生成的 UI 需要一个 ui_tick() 函数来定期刷新。我们需要创建一个独立的 FreeRTOS 任务来专门做这件事。
在 main.c 中添加任务函数和任务创建代码:
// 静态函数声明
static void ui_task(void *arg);
void app_main(void)
{
// ... 初始化 LCD, LVGL ...
// 初始化 UI
lvgl_port_lock(0);
ui_init();
lvgl_port_unlock();
// 创建 UI 刷新任务
BaseType_t ui_task_ok = xTaskCreate(ui_task, "ui_task", 4096, NULL, 5, NULL);
ESP_ERROR_CHECK(ui_task_ok == pdPASS ? ESP_OK : ESP_FAIL); // 检查任务是否创建成功
// ... 其他任务 ...
}
// UI 刷新任务函数
static void ui_task(void *arg)
{
(void)arg; // 消除未使用参数的警告
for (;;)
{
// 在锁内调用 ui_tick,保证 LVGL 操作安全
lvgl_port_lock(0);
ui_tick(); // 通知 EEZ UI 进行周期性处理
lvgl_port_unlock();
// 刷新周期设为 20ms (约 50Hz),在流畅度和 CPU 占用之间取得良好平衡
vTaskDelay(pdMS_TO_TICKS(20));
}
}8. 见证成果
完成以上所有步骤后,编译并烧录程序到你的 ESP32 开发板。如果一切顺利,你将在屏幕上看到与 EEZ Studio 设计完全一致的 UI 界面。
EEZ Studio 设计画面:
ESP32 实际显示效果:
恭喜!你已经成功地将一个通过 GUI 设计工具开发的 UI 工程,完整地移植到了嵌入式设备上。这种方式不仅极大地提升了 UI 开发的效率,也让设计和代码实现之间有了清晰的界限,便于后续的迭代和维护。
评论
暂无评论