Files
Smart-flower-system/components/lvgl_st7789_use/lvgl_st7789_use.c
Wang Beihong bec31c01ce 在主应用中更新显示驱动和 MQTT 主题
- 将显示驱动从 lvgl_st7735s_use 改为 lvgl_st7789_use,在 CMakeLists.txt 和 main.c 中。
- 将 MQTT 警报主题从“topic/alert/esp32_iothome_001”更新为“topic/alert/esp32_iothome_002”。
- 重构 UI 任务,去除周期性屏幕切换逻辑,专注于界面刷新。
- 调整错误处理,使新的显示驱动程序用于错误信息。
2026-03-14 15:44:01 +08:00

258 lines
8.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <stdio.h>
#include <stdlib.h>
#include "lvgl_st7789_use.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lvgl_port.h"
static const char *TAG = "lvgl_st7789_use";
static esp_lcd_panel_io_handle_t lcd_io = NULL;
static esp_lcd_panel_handle_t lcd_panel = NULL;
static lv_display_t *lvgl_disp = NULL;
static lv_obj_t *s_center_label = NULL;
#if EXAMPLE_LCD_ENABLE_COLOR_TEST
static esp_err_t app_lcd_color_test(void)
{
const size_t pixels = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES;
uint16_t *frame = calloc(pixels, sizeof(uint16_t));
ESP_RETURN_ON_FALSE(frame != NULL, ESP_ERR_NO_MEM, TAG, "分配测试帧缓冲失败");
for (int y = 0; y < EXAMPLE_LCD_V_RES; y++) {
for (int x = 0; x < EXAMPLE_LCD_H_RES; x++) {
uint16_t color;
if (x < EXAMPLE_LCD_H_RES / 3) {
color = 0xF800; // 红
} else if (x < (EXAMPLE_LCD_H_RES * 2) / 3) {
color = 0x07E0; // 绿
} else {
color = 0x001F; // 蓝
}
frame[y * EXAMPLE_LCD_H_RES + x] = color;
}
}
esp_err_t err = esp_lcd_panel_draw_bitmap(lcd_panel, 0, 0, EXAMPLE_LCD_H_RES, EXAMPLE_LCD_V_RES, frame);
free(frame);
ESP_RETURN_ON_ERROR(err, TAG, "三色测试绘制失败");
ESP_LOGI(TAG, "LCD三色测试图已发送");
return ESP_OK;
}
#endif
/**
* @brief 初始化LCD硬件和SPI接口
*
* 该函数负责初始化LCD所需的GPIO、SPI总线并配置LCD面板
* 包括背光控制、SPI总线配置、面板IO配置和面板驱动安装
*
* @return esp_err_t 初始化结果ESP_OK表示成功
*/
static esp_err_t app_lcd_init(void)
{
esp_err_t ret = ESP_OK;
if (EXAMPLE_LCD_GPIO_BL != GPIO_NUM_NC) {
gpio_config_t bk_gpio_config = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << EXAMPLE_LCD_GPIO_BL
};
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
} else {
ESP_LOGW(TAG, "背光引脚未配置(GPIO_NUM_NC)跳过背光GPIO初始化");
}
ESP_LOGI(TAG, "初始化SPI总线");
const spi_bus_config_t buscfg = {
.sclk_io_num = EXAMPLE_LCD_GPIO_SCLK,
.mosi_io_num = EXAMPLE_LCD_GPIO_MOSI,
.miso_io_num = GPIO_NUM_NC,
.quadwp_io_num = GPIO_NUM_NC,
.quadhd_io_num = GPIO_NUM_NC,
.max_transfer_sz = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_DRAW_BUFF_HEIGHT * sizeof(uint16_t),
};
ESP_RETURN_ON_ERROR(spi_bus_initialize(EXAMPLE_LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO), TAG, "SPI初始化失败");
ESP_LOGI(TAG, "安装面板IO");
const esp_lcd_panel_io_spi_config_t io_config = {
.dc_gpio_num = EXAMPLE_LCD_GPIO_DC,
.cs_gpio_num = EXAMPLE_LCD_GPIO_CS,
.pclk_hz = EXAMPLE_LCD_PIXEL_CLK_HZ,
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
.spi_mode = 0,
.trans_queue_depth = 10,
};
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)EXAMPLE_LCD_SPI_NUM, &io_config, &lcd_io), err, TAG, "创建面板IO失败");
ESP_LOGI(TAG, "安装LCD驱动");
const esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = EXAMPLE_LCD_GPIO_RST,
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6, 0, 0)
.rgb_endian = EXAMPLE_LCD_COLOR_ORDER_BGR ? LCD_RGB_ENDIAN_BGR : LCD_RGB_ENDIAN_RGB,
#else
.rgb_ele_order = EXAMPLE_LCD_COLOR_ORDER_BGR ? LCD_RGB_ELEMENT_ORDER_BGR : LCD_RGB_ELEMENT_ORDER_RGB,
#endif
.bits_per_pixel = EXAMPLE_LCD_BITS_PER_PIXEL,
};
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_st7789(lcd_io, &panel_config, &lcd_panel), err, TAG, "创建面板失败");
ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(lcd_panel), err, TAG, "面板复位失败");
ESP_GOTO_ON_ERROR(esp_lcd_panel_init(lcd_panel), err, TAG, "面板初始化失败");
ESP_GOTO_ON_ERROR(esp_lcd_panel_swap_xy(lcd_panel, false), err, TAG, "设置面板swap_xy失败");
ESP_GOTO_ON_ERROR(esp_lcd_panel_mirror(lcd_panel, false, false), err, TAG, "设置面板镜像失败");
ESP_GOTO_ON_ERROR(esp_lcd_panel_set_gap(lcd_panel, EXAMPLE_LCD_GAP_X, EXAMPLE_LCD_GAP_Y), err, TAG, "设置显示偏移失败");
ESP_LOGI(TAG, "面板基准参数已应用: gap=(%d,%d)", EXAMPLE_LCD_GAP_X, EXAMPLE_LCD_GAP_Y);
ESP_GOTO_ON_ERROR(esp_lcd_panel_invert_color(lcd_panel, EXAMPLE_LCD_INVERT_COLOR), err, TAG, "设置反色失败");
ESP_GOTO_ON_ERROR(esp_lcd_panel_disp_on_off(lcd_panel, true), err, TAG, "打开显示失败");
if (EXAMPLE_LCD_GPIO_BL != GPIO_NUM_NC) {
ESP_RETURN_ON_ERROR(gpio_set_level(EXAMPLE_LCD_GPIO_BL, EXAMPLE_LCD_BL_ON_LEVEL), TAG, "背光引脚置位失败");
ESP_LOGI(TAG, "背光已打开,电平=%d", EXAMPLE_LCD_BL_ON_LEVEL);
}
ESP_LOGI(TAG, "颜色参数: order=%s invert=%d swap_bytes=%d",
EXAMPLE_LCD_COLOR_ORDER_BGR ? "BGR" : "RGB",
EXAMPLE_LCD_INVERT_COLOR,
EXAMPLE_LCD_SWAP_BYTES);
return ret;
// 错误处理标签,用于清理资源
err:
if (lcd_panel) {
esp_lcd_panel_del(lcd_panel);
lcd_panel = NULL;
}
if (lcd_io) {
esp_lcd_panel_io_del(lcd_io);
lcd_io = NULL;
}
spi_bus_free(EXAMPLE_LCD_SPI_NUM);
return ret;
}
/**
* @brief 初始化LVGL图形库
*
* 该函数负责初始化LVGL库并配置显示设备
* 包括LVGL任务配置、显示缓冲区配置和旋转设置
*
* @return esp_err_t 初始化结果ESP_OK表示成功
*/
static esp_err_t app_lvgl_init(void)
{
const lvgl_port_cfg_t lvgl_cfg = {
.task_priority = 4,
.task_stack = 4096,
.task_affinity = -1,
.task_max_sleep_ms = 500,
.timer_period_ms = 5
};
ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_cfg), TAG, "LVGL端口初始化失败");
ESP_LOGI(TAG, "添加LCD屏幕");
const lvgl_port_display_cfg_t disp_cfg = {
.io_handle = lcd_io,
.panel_handle = lcd_panel,
.buffer_size = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_DRAW_BUFF_HEIGHT,
.double_buffer = EXAMPLE_LCD_DRAW_BUFF_DOUBLE,
.hres = EXAMPLE_LCD_H_RES,
.vres = EXAMPLE_LCD_V_RES,
.monochrome = false,
#if LVGL_VERSION_MAJOR >= 9
.color_format = LV_COLOR_FORMAT_RGB565,
#endif
.rotation = {
.swap_xy = EXAMPLE_LCD_ROT_SWAP_XY,
.mirror_x = EXAMPLE_LCD_ROT_MIRROR_X,
.mirror_y = EXAMPLE_LCD_ROT_MIRROR_Y,
},
.flags = {
.buff_dma = true,
#if LVGL_VERSION_MAJOR >= 9
.swap_bytes = EXAMPLE_LCD_SWAP_BYTES,
#endif
}};
lvgl_disp = lvgl_port_add_disp(&disp_cfg);
ESP_RETURN_ON_FALSE(lvgl_disp != NULL, ESP_FAIL, TAG, "添加LVGL显示设备失败");
ESP_LOGI(TAG, "LVGL旋转已应用: swap_xy=%d mirror_x=%d mirror_y=%d",
EXAMPLE_LCD_ROT_SWAP_XY, EXAMPLE_LCD_ROT_MIRROR_X, EXAMPLE_LCD_ROT_MIRROR_Y);
return ESP_OK;
}
/**
* @brief 创建并显示LVGL主界面
*
* 该函数负责创建LVGL的用户界面元素包括图像、标签和按钮
* 并设置它们的位置和属性
*/
static void app_main_display(void)
{
lv_obj_t *scr = lv_scr_act();
lvgl_port_lock(0);
lv_obj_set_style_bg_color(scr, lv_color_white(), 0);
lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0);
s_center_label = lv_label_create(scr);
lv_label_set_text(s_center_label, "BotanicalBuddy\nloading...");
lv_label_set_recolor(s_center_label, false);
lv_label_set_long_mode(s_center_label, LV_LABEL_LONG_WRAP);
lv_obj_set_size(s_center_label, EXAMPLE_LCD_H_RES - 6, EXAMPLE_LCD_V_RES - 6);
lv_obj_set_style_text_color(s_center_label, lv_color_black(), 0);
lv_obj_set_style_text_font(s_center_label, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_align(s_center_label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_style_pad_all(s_center_label, 0, 0);
lv_obj_align(s_center_label, LV_ALIGN_CENTER, 0, 0);
lvgl_port_unlock();
}
/**
* @brief 启动LVGL演示程序
*
* 该函数是程序的入口点负责初始化LCD硬件、LVGL库并显示主界面
*/
esp_err_t lvgl_st7789_set_center_text(const char *text)
{
ESP_RETURN_ON_FALSE(text != NULL, ESP_ERR_INVALID_ARG, TAG, "text is null");
ESP_RETURN_ON_FALSE(s_center_label != NULL, ESP_ERR_INVALID_STATE, TAG, "label not ready");
lvgl_port_lock(0);
lv_label_set_text(s_center_label, text);
lv_obj_align(s_center_label, LV_ALIGN_CENTER, 0, 0);
lvgl_port_unlock();
return ESP_OK;
}
esp_err_t start_lvgl_demo(void)
{
ESP_RETURN_ON_ERROR(app_lcd_init(), TAG, "LCD初始化失败");
#if EXAMPLE_LCD_ENABLE_COLOR_TEST
ESP_RETURN_ON_ERROR(app_lcd_color_test(), TAG, "LCD测试图绘制失败");
vTaskDelay(pdMS_TO_TICKS(300));
#endif
ESP_RETURN_ON_ERROR(app_lvgl_init(), TAG, "LVGL初始化失败");
app_main_display();
return ESP_OK;
}