mirror of
https://git.beihong.wang/wangbeihong/iot-bedroom-environment-controller.git
synced 2026-04-23 14:13:05 +08:00
- 环境监测:温湿度/光照/空气质量传感器采集 - 智能控制:时间段/降温/通风三种自动模式 - 闹钟系统:3个闹钟+温和唤醒功能 - 远程控制:MQTT双向通信 - 本地显示:LVGL图形界面 - 双MCU架构,FreeRTOS 10任务并行 - 完整的1250行README文档
237 lines
8.7 KiB
C
237 lines
8.7 KiB
C
#include <stdio.h>
|
||
#include "lvgl_st7735s_use.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" // 包含LVGL端口头文件,用于LVGL与ESP硬件的接口
|
||
|
||
#include "ui_display.h" // 添加新UI界面的头文件
|
||
static const char *TAG = "lvgl_st7735s_use"; // 用于日志输出的标签,便于调试时识别日志来源
|
||
|
||
|
||
/* LCD IO和面板句柄 */
|
||
// lcd_io: LCD面板IO句柄,用于与LCD进行通信
|
||
// lcd_panel: LCD面板句柄,用于控制LCD的各种操作
|
||
static esp_lcd_panel_io_handle_t lcd_io = NULL;
|
||
static esp_lcd_panel_handle_t lcd_panel = NULL;
|
||
|
||
/* LVGL显示和触摸 */
|
||
// lvgl_disp: LVGL显示设备句柄,用于LVGL库与显示设备的交互
|
||
static lv_display_t *lvgl_disp = NULL;
|
||
|
||
|
||
|
||
/**
|
||
* @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;
|
||
|
||
/* LCD背光配置 */
|
||
// 配置背光GPIO为输出模式,用于控制LCD的背光开关
|
||
gpio_config_t bk_gpio_config = {
|
||
.mode = GPIO_MODE_OUTPUT, // 设置GPIO为输出模式
|
||
.pin_bit_mask = 1ULL << EXAMPLE_LCD_GPIO_BL // 设置背光GPIO引脚
|
||
};
|
||
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config)); // 应用GPIO配置并检查错误
|
||
|
||
/* LCD初始化 */
|
||
ESP_LOGD(TAG, "初始化SPI总线"); // 输出调试日志
|
||
// 配置SPI总线参数,包括时钟、数据线和最大传输大小
|
||
const spi_bus_config_t buscfg = {
|
||
.sclk_io_num = EXAMPLE_LCD_GPIO_SCLK, // SPI时钟引脚
|
||
.mosi_io_num = EXAMPLE_LCD_GPIO_MOSI, // SPI主输出从输入引脚
|
||
.miso_io_num = GPIO_NUM_NC, // 未使用MISO引脚
|
||
.quadwp_io_num = GPIO_NUM_NC, // 未使用WP引脚
|
||
.quadhd_io_num = GPIO_NUM_NC, // 未使用HD引脚
|
||
.max_transfer_sz = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_DRAW_BUFF_HEIGHT * sizeof(uint16_t), // 最大传输大小
|
||
};
|
||
// 初始化SPI总线,使用DMA自动分配通道
|
||
ESP_RETURN_ON_ERROR(spi_bus_initialize(EXAMPLE_LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO), TAG, "SPI初始化失败");
|
||
|
||
ESP_LOGD(TAG, "安装面板IO");
|
||
// 配置LCD面板IO的SPI参数
|
||
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 = 3, // SPI模式
|
||
.trans_queue_depth = 10, // 传输队列深度
|
||
};
|
||
// 创建LCD面板IO,用于SPI通信
|
||
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_LOGD(TAG, "安装LCD驱动");
|
||
// 配置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 = LCD_RGB_ENDIAN_RGB, // RGB字节序(旧版本)
|
||
#else
|
||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR, // RGB元素顺序(新版本)
|
||
#endif
|
||
.bits_per_pixel = EXAMPLE_LCD_BITS_PER_PIXEL, // 每像素位数
|
||
};
|
||
// 创建ST7789 LCD面板驱动
|
||
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_st7789(lcd_io, &panel_config, &lcd_panel), err, TAG, "创建面板失败");
|
||
|
||
// 复位LCD面板
|
||
esp_lcd_panel_reset(lcd_panel);
|
||
// 初始化LCD面板
|
||
esp_lcd_panel_init(lcd_panel);
|
||
// 设置显示窗口,确保使用正确的分辨率(偏移)
|
||
esp_lcd_panel_set_gap(lcd_panel, 1, 26);
|
||
|
||
// 反转颜色
|
||
esp_lcd_panel_invert_color(lcd_panel, true);
|
||
// 打开LCD显示
|
||
esp_lcd_panel_disp_on_off(lcd_panel, true);
|
||
|
||
/* 打开LCD背光 */
|
||
ESP_ERROR_CHECK(gpio_set_level(EXAMPLE_LCD_GPIO_BL, EXAMPLE_LCD_BL_ON_LEVEL));
|
||
|
||
return ret;
|
||
|
||
// 错误处理标签,用于清理资源
|
||
err:
|
||
if (lcd_panel) {
|
||
esp_lcd_panel_del(lcd_panel); // 删除面板
|
||
}
|
||
if (lcd_io) {
|
||
esp_lcd_panel_io_del(lcd_io); // 删除面板IO
|
||
}
|
||
spi_bus_free(EXAMPLE_LCD_SPI_NUM); // 释放SPI总线资源
|
||
return ret;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief 初始化LVGL图形库
|
||
*
|
||
* 该函数负责初始化LVGL库,并配置显示设备
|
||
* 包括LVGL任务配置、显示缓冲区配置和旋转设置
|
||
*
|
||
* @return esp_err_t 初始化结果,ESP_OK表示成功
|
||
*/
|
||
static esp_err_t app_lvgl_init(void)
|
||
{
|
||
/* 初始化LVGL */
|
||
// 配置LVGL任务参数
|
||
const lvgl_port_cfg_t lvgl_cfg = {
|
||
.task_priority = 4, /* LVGL任务优先级,数值越高优先级越高 */
|
||
.task_stack = 4096, /* LVGL任务堆栈大小,单位为字节 */
|
||
.task_affinity = -1, /* LVGL任务绑定核心,-1表示不绑定特定核心 */
|
||
.task_max_sleep_ms = 500, /* LVGL任务最大睡眠时间,单位为毫秒 */
|
||
.timer_period_ms = 5 /* LVGL定时器周期,单位为毫秒,用于处理动画和输入 */
|
||
};
|
||
// 初始化LVGL端口
|
||
ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_cfg), TAG, "LVGL端口初始化失败");
|
||
|
||
/* 添加LCD屏幕 */
|
||
ESP_LOGD(TAG, "添加LCD屏幕");
|
||
// 配置LVGL显示设备参数
|
||
const lvgl_port_display_cfg_t disp_cfg = {
|
||
.io_handle = lcd_io, // LCD面板IO句柄
|
||
.panel_handle = lcd_panel, // LCD面板句柄
|
||
.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, // 颜色格式(LVGL v9及以上)
|
||
#endif
|
||
.rotation = { // 旋转设置
|
||
.swap_xy = 1, // 交换X和Y轴以实现横向显示
|
||
.mirror_x = 1, // 是否水平镜像
|
||
.mirror_y = 0, // 是否垂直镜像
|
||
},
|
||
.flags = { // 标志位
|
||
.buff_dma = true, // 是否使用DMA缓冲区
|
||
#if LVGL_VERSION_MAJOR >= 9
|
||
.swap_bytes = false, // 是否交换字节序(LVGL v9及以上)
|
||
#endif
|
||
}
|
||
};
|
||
// 添加LVGL显示设备
|
||
lvgl_disp = lvgl_port_add_disp(&disp_cfg);
|
||
|
||
return ESP_OK;
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* @brief 创建并显示LVGL主界面
|
||
*
|
||
* 该函数负责创建LVGL的用户界面元素,包括图像、标签和按钮
|
||
* 并设置它们的位置和属性
|
||
*/
|
||
static void app_main_display(void)
|
||
{
|
||
// 获取当前活动屏幕对象
|
||
lv_obj_t *scr = lv_scr_act();
|
||
|
||
/* 任务锁定 */
|
||
// 锁定LVGL任务,防止在创建UI对象时被中断
|
||
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);
|
||
|
||
|
||
/* 创建标签 */
|
||
// 创建标签对象
|
||
lv_obj_t *label = lv_label_create(scr);
|
||
// 设置标签文本为"ESP32C3-LVGL"
|
||
lv_label_set_text(label, "ESP32C3-LVGL1");
|
||
// 设置标签文本颜色为白色
|
||
lv_obj_set_style_text_color(label, lv_color_black(), 0);
|
||
// 设置标签文本字体大小为更小的字体
|
||
lv_obj_set_style_text_font(label, &lv_font_unscii_8, 0);
|
||
|
||
// 设置标签位置在屏幕中心
|
||
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
|
||
|
||
/* 任务解锁 */
|
||
// 解锁LVGL任务
|
||
lvgl_port_unlock();
|
||
}
|
||
|
||
/**
|
||
* @brief 启动LVGL演示程序
|
||
*
|
||
* 该函数是程序的入口点,负责初始化LCD硬件、LVGL库,并显示主界面
|
||
*/
|
||
void start_lvgl_demo(void)
|
||
{
|
||
/* LCD硬件初始化 */
|
||
// 初始化LCD硬件和SPI接口
|
||
ESP_ERROR_CHECK(app_lcd_init());
|
||
|
||
/* LVGL初始化 */
|
||
// 初始化LVGL图形库
|
||
ESP_ERROR_CHECK(app_lvgl_init());
|
||
|
||
/* 显示LVGL对象 */
|
||
// 创建并显示LVGL主界面
|
||
// app_main_display();
|
||
|
||
/* 显示LVGL对象 - 使用新的UI界面初始化函数 */
|
||
ui_display_init();
|
||
}
|