| 本帖最后由 云天 于 2025-8-14 00:25 编辑 
 【项目背景}
 在当今的智能硬件开发领域,ESP32-P4 开发板凭借其强大的性能和丰富的功能接口,成为了众多创客和开发者的热门选择。FireBeetle 2 ESP32-P4结合 MIPI-DSI 屏幕,我们可以实现各种精彩的交互式项目。本文将详细介绍如何利用FireBeetle 2 ESP32-P4开发板和 MIPI-DSI 屏幕,实现一个支持触屏交互和定时切换显示内容的创意项目。
 【项目准备】
 1.硬件准备
 2. 软件准备ESP32-P4 开发板:确保你有一个 FireBeetle 2 ESP32-P4开发板。MIPI-DSI 屏幕: 7寸树莓派TFT触摸屏,800*480分辨率,支持MIPI-DSI 接口,自带电容式触摸面板,支持5点触控,并且兼容树莓派 4B 的 DSI 线序。连接线:将屏幕连接到FireBeetle 2 ESP32-P4的 MIPI-DSI 接口。
 ESP-IDF:安装最新版本的 ESP-IDF,这是 Espressif 官方的开发框架。ESP32_Display_Panel:克隆 ESP32_Display_Panel 仓库,并将其作为组件添加到你的 ESP-IDF 项目中。(ESP32_Display_Panel\examples\esp_idf\lvgl_v8_port,VSC打开文件夹)
 3.配置项目
 打开 ESP-IDF 的配置菜单,选择目标开发板为“FIREBEETLE-ESP32-P4-LCD-5”,完成项目的初始配置。
 Board——Select a target board——FIREBEETLE-ESP32-P4-LCD-5
 【编写程序】以下是项目的主程序代码,它实现了屏幕的初始化、LVGL 图形库的初始化,并展示了如何使用 LVGL 创建简单的用户界面。
 
 复制代码/*
 * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: CC0-1.0
 */
 #include "esp_check.h"
 #include "esp_display_panel.hpp"
 #include "esp_lib_utils.h"
 #include "lvgl.h"
 #include "lvgl_v8_port.h"
 #include "lv_demos.h"
 
 #define EXAMPLE_LCD_USE_EXTERNAL_INIT_CMD   (0)
 using namespace esp_panel::drivers;
 using namespace esp_panel::board;
 
 static const char *TAG = "example";
 #if EXAMPLE_LCD_USE_EXTERNAL_INIT_CMD
 static const esp_panel_lcd_vendor_init_cmd_t external_init_cmd[] = {
 //  {cmd, { data }, data_size, delay_ms}
     // {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
     // {0xEF, (uint8_t []){0x08}, 1, 0},
     // {0xFF, (uint8_t []){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
     // ...
 };
 #endif // EXAMPLE_LCD_USE_EXTERNAL_INIT_CMD
 
 extern "C" void app_main()
 {
     Board *board = new Board();
     assert(board);
 
     ESP_LOGI(TAG, "Initializing board");
     ESP_UTILS_CHECK_FALSE_EXIT(board->init(), "Board init failed");
 
 #if LVGL_PORT_AVOID_TEARING_MODE
     {
         auto lcd = board->getLCD();
         // When avoid tearing function is enabled, the frame buffer number should be set in the board driver
         lcd->configFrameBufferNumber(LVGL_PORT_DISP_BUFFER_NUM);
 #   if ESP_PANEL_DRIVERS_BUS_ENABLE_RGB && CONFIG_IDF_TARGET_ESP32S3
         auto lcd_bus = lcd->getBus();
         /**
          * As the anti-tearing feature typically consumes more PSRAM bandwidth, for the ESP32-S3, we need to utilize the
          * "bounce buffer" functionality to enhance the RGB data bandwidth.
          * This feature will consume `bounce_buffer_size * bytes_per_pixel * 2` of SRAM memory.
          */
         if (lcd_bus->getBasicAttributes().type == ESP_PANEL_BUS_TYPE_RGB) {
             static_cast<BusRGB *>(lcd_bus)->configRGB_BounceBufferSize(lcd->getFrameWidth() * 10);
         }
 #   endif
     }
 #endif // LVGL_PORT_AVOID_TEARING_MODE
 
 #if EXAMPLE_LCD_USE_EXTERNAL_INIT_CMD
     ESP_LOGI(TAG, "Using external LCD init command");
     {
         auto lcd = board->getLCD();
         ESP_UTILS_CHECK_FALSE_EXIT(
             lcd->configVendorCommands(external_init_cmd, sizeof(external_init_cmd) / sizeof(external_init_cmd[0])),
             "LCD init failed"
         );
     }
     /**
      * In addition, you can also get handles to any other devices (like touch) and use `config*()` functions to
      * configure or replace default parameters, but this must be completed before `board->begin()`
      */
 #endif // EXAMPLE_LCD_USE_EXTERNAL_INIT_CMD
 
     ESP_UTILS_CHECK_FALSE_EXIT(board->begin(), "Board begin failed");
 
     ESP_LOGI(TAG, "Initializing LVGL");
     ESP_UTILS_CHECK_FALSE_EXIT(lvgl_port_init(board->getLCD(), board->getTouch()), "LVGL init failed");
 
     ESP_LOGI(TAG, "Creating UI");
     /* Lock the mutex due to the LVGL APIs are not thread-safe */
     lvgl_port_lock(-1);
 
     // lv_demo_widgets();
     // lv_demo_benchmark();
lv_demo_music();
     //lv_demo_stress();
/* Release the mutex */
     lvgl_port_unlock();
 }
 
 5.显示文字
 
 在项目中,我们可以通过 LVGL 图形库轻松实现文字和图片的显示。以下是具体的代码示例:
 
 
 复制代码// Create a label to display text
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Hello, FireBeetle2 ESP32-P4!");
// Set the font size
static lv_style_t style_text;
lv_style_init(&style_text);
lv_style_set_text_font(&style_text, &lv_font_montserrat_34); // 设置字体大小为 34
lv_obj_add_style(label, &style_text, LV_PART_MAIN | LV_STATE_DEFAULT); 
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); // Center the label on the screen
 6.显示图片
 首先,使用工具将图片处理为适合屏幕分辨率的格式,并通过 LVGL 提供的工具将其转换为 C 代码格式。然后在代码中加载图片。
 (1)处理图片
 使用Macromedia Fireworks 8将图片处理成分辨率800*400的jpg格式。
 
 (2)转换格式
 lv_img_conv 是 LVGL 提供的一个工具,用于将图片文件(如 PNG、JPG、BMP 等)转换为 LVGL 支持的格式(如 C 数组或二进制文件),以便在嵌入式系统中使用。以下是 在线版lv_img_conv 的使用方法:
 访问在线工具:打开 LVGL Online Image Converter。
 
 选择图片文件:点击“Image file(s)”按钮,选择要转换的图片文件。设置输出文件名:在“File name(s)”中输入输出文件的名称(例如 background_img)。选择颜色格式:根据目标硬件选择合适的颜色格式(如 RGB565 或 True Color)。选择输出格式:选择输出格式,通常是 C array(生成 C 代码)或 Binary(生成二进制文件)。点击转换:点击“Convert”按钮,下载转换后的文件。
 将生成文件background_img.c,放入项目文件夹中。
 
 复制代码// 背景图片已经转换为 C 代码格式
extern const lv_img_dsc_t background_img;
// 创建背景图片
lv_obj_t *background = lv_img_create(lv_scr_act());
lv_img_set_src(background, &background_img); // 设置图片源
lv_obj_align(background, LV_ALIGN_CENTER, 0, 0); // 将图片居中显示 
 7.触屏功能
 
 
 通过 LVGL 的事件回调机制,我们可以实现点击屏幕按钮切换图片的功能。以下是代码示例:
 
 复制代码void switch_image(lv_event_t * e) {
    lv_obj_t *img = (lv_obj_t *)e->user_data;
    static bool switch_state = false;
    if (!switch_state) {
        lv_img_set_src(img, &background_img);
    } else {
        lv_img_set_src(img, &background_img1);
    }
    switch_state = !switch_state;
}
lv_obj_t *btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, 100, 50);
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 100); // 将按钮放置在屏幕下方
lv_obj_add_event_cb(btn, switch_image, LV_EVENT_CLICKED, background);
  
 8.定时切换
 
 
 我们还可以通过 LVGL 的定时器功能实现图片的定时切换,代码如下:
 
 
 【演示视频】复制代码void switch_image(lv_timer_t *timer) {
    lv_obj_t *img = (lv_obj_t *)timer->user_data;
    static bool switch_state = false;
    static lv_anim_t a;
    lv_anim_init(&a);
    lv_anim_set_var(&a, img);
    if (!switch_state) {
        lv_anim_set_exec_cb(&a, [](void *img, int32_t v) {
            lv_img_set_src((lv_obj_t *)img, &background_img);
        });
    } else {
        lv_anim_set_exec_cb(&a, [](void *img, int32_t v) {
            lv_img_set_src((lv_obj_t *)img, &background_img1);
        });
    }
    lv_anim_set_time(&a, 3000); // 动画持续时间 3000 毫秒
    lv_anim_set_playback_time(&a, 1000); // 回放时间 1000 毫秒
    lv_anim_set_repeat_count(&a, 1); // 重复一次
    lv_anim_set_path_cb(&a, lv_anim_path_ease_in_out); // 使用缓动函数
    lv_anim_start(&a);
    switch_state = !switch_state;
}
lv_timer_t *timer = lv_timer_create(switch_image, 5000, background);
lv_timer_set_repeat_count(timer, LV_ANIM_REPEAT_INFINITE);
 
 
 
 |