Files
Smart-granary-code/components/lvgl_st7789_use/lvgl_st7789_use.c
Wang Beihong 30e95387e5 添加 UI 变量管理并重构主应用程序结构
引入 vars.c 和 vars.h 用于管理与 UI 相关的全局变量。

新增 get_var_weigt_ui 和 set_var_weigt_ui 函数,用于访问和修改 UI 重量变量。

更新 CMakeLists.txt 以包含新的 UI 和 LVGL 依赖项。

将 main.c 转换为 main.cpp 以支持 C++ 特性,并重构了应用程序入口点。

实现了使用 FreeRTOS 定期更新 UI 的 UI 任务。

在 partitions.csv 中创建自定义分区表,用于管理闪存空间。

添加 update_sdkconfig.sh 脚本,自动更新 SDK 配置中的闪存大小和 SPIRAM 设置。

移除旧的 main.c 文件以精简项目结构。
2026-04-20 21:13:00 +08:00

258 lines
9.0 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 && EXAMPLE_LCD_GPIO_BL >= 0) {
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 或 <0)跳过背光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 = 8192,
.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;
}