ESP32S3 使用LVGL驱动LCD屏(ST7789主控)

news2025/2/25 12:40:08

ESP32S3 使用LVGL驱动LCD屏(ST7789主控)


目录

1 分析原理图
2 驱动、点亮LCD(ST7789)
  2.1 在工程中添加目录、文件
  2.2 添加esp_lvgl_port组件
  2.3 对工程进行必要的配置
  2.4 编写必要代码
3 烧录、验证


1 分析原理图

    要使用SOC驱动LCD屏,首先需要看的就是硬件原理图,主要要看的是用的什么驱动芯片、驱动接口类型、具体的引脚连接等。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

    通过这部分原理图,可以知道,主控用的是8MB Flash的ESP32S3-WROOM模组,LCD驱动芯片为ST7789,驱动接口为SPI接口。IO9控制的是LCD的亮度、IO10为ST7789的SPI片选、IO12为ST7789的SPI时钟、IO11和IO13则为ST7789的SPI数据输入输出,其他的还有一些复位信号、背光信号。


2 驱动、点亮LCD(ST7789)

    我们在博文

IDF项目添加LVGL组件 创建的工程的基础上,进一步实现LCD屏幕的驱动点亮。

2.1 在工程中添加目录、文件

    在工程中的main路径下创建一个文件夹lvgl_options,并在其中创建文件lvgl_options.h、lvgl_options.c。

在这里插入图片描述

    修改main路径下的CMakeLists.txt文件,将lvgl_options路径下的文件添加进构建系统。

在这里插入图片描述

2.2 添加esp_lvgl_port组件

    在ESP组件中心搜索esp_lvgl_port组件,并通过idf 命令将其添加进工程。
idf.py clean
idf.py add-dependency "espressif/esp_lvgl_port^2.4.3"
idf.py set-target esp32s3

在这里插入图片描述

在这里插入图片描述

2.3 对工程进行必要的配置

    根据模组的实际flash情况配置flash。

在这里插入图片描述

    根据模组的实际ram情况配置ram。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

    根据LCD情况和实际内存资源情况,调整LVGL相关配置。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.4 编写必要代码

    首先需要初始化对应的SPI 总线,通过创建一个spi_bus_config_t类型的对象,调用接口 spi_bus_initialize() 可以完成 SPI 总线的初始化。
spi_bus_config_t buscfg = {
        .sclk_io_num = EXAMPLE_LCD_GPIO_SCLK,
        .mosi_io_num = EXAMPLE_LCD_GPIO_MOSI,
        .miso_io_num = EXAMPLE_LCD_GPIO_MISO,
        .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 init failed");

    在完成了SPI 总线的初始化后,就可以开始初始化LCD设备了,LCD设备的初始化需要先创建一个esp_lcd_panel_io_spi_config_t类型的对象完成实际SPI的配置,再通过esp_lcd_new_panel_io_spi()接口完成lcd_io对象的创建,再通过lcd_io对象完成lcd_panel的创建。

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_CLOCK_SPEED_HZ,
        .lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
        .lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
        .spi_mode = EXAMPLE_LCD_SPI_MODE,
        .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, "New panel IO failed");

const esp_lcd_panel_dev_config_t panel_config = {
        //.reset_gpio_num = EXAMPLE_LCD_GPIO_RST,
        .reset_gpio_num = GPIO_NUM_NC,
        .color_space = EXAMPLE_LCD_COLOR_SPACE,
        .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, "New panel failed");

    在完成了SPI和LCD设备的初始化后,就可以初始化lvgl组件了。主要就是调用lvgl_port_init()和lvgl_port_add_disp()。

    完整的lvgl_options.h

#ifndef _MY_LVGL_OPTIONS_H_
#define _MY_LVGL_OPTIONS_H_

#ifdef __cplusplus
extern "C" {
#endif

void lvgl_options_test(void);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif 

    完整的lvgl_options.c

#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"

#include "driver/gpio.h"
#include "driver/spi_master.h"

#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"

#include "esp_lvgl_port.h"

#include "lvgl_options.h"

// ST7789 参数
#define SPI_TFT_ST7789_CLOCK_SPEED_HZ      (60*1000*1000) 
#define SPI_TFT_ST7789_SPI_MODE            (2)

/* LCD size */
#define EXAMPLE_LCD_H_RES   (240)
#define EXAMPLE_LCD_V_RES   (320)

/* LCD settings */
#define EXAMPLE_LCD_SPI_NUM         (SPI3_HOST)
#define EXAMPLE_LCD_CMD_BITS        (8)
#define EXAMPLE_LCD_PARAM_BITS      (8)
#define EXAMPLE_LCD_COLOR_SPACE     (ESP_LCD_COLOR_SPACE_BGR)
#define EXAMPLE_LCD_BITS_PER_PIXEL  (16)
#define EXAMPLE_LCD_DRAW_BUFF_DOUBLE (1)
#define EXAMPLE_LCD_DRAW_BUFF_HEIGHT (320)
#define EXAMPLE_LCD_BL_ON_LEVEL      (1)

#define EXAMPLE_LCD_SPI_MODE        SPI_TFT_ST7789_SPI_MODE
#define EXAMPLE_LCD_CLOCK_SPEED_HZ  SPI_TFT_ST7789_CLOCK_SPEED_HZ
#define EXAMPLE_LCD_GPIO_SCLK       (GPIO_NUM_12)
#define EXAMPLE_LCD_GPIO_MOSI       (GPIO_NUM_11)
#define EXAMPLE_LCD_GPIO_MISO       (GPIO_NUM_13)
//#define EXAMPLE_LCD_GPIO_RST        (GPIO_NUM_48)
#define EXAMPLE_LCD_GPIO_DC         (GPIO_NUM_9)
#define EXAMPLE_LCD_GPIO_CS         (GPIO_NUM_10)
//#define EXAMPLE_LCD_GPIO_BL         (GPIO_NUM_45)

static const char *TAG = "lvgl_options";

// LVGL image declare
LV_IMG_DECLARE(esp_logo)

/* LCD IO and panel */
static esp_lcd_panel_io_handle_t lcd_io = NULL;
static esp_lcd_panel_handle_t lcd_panel = NULL;

/* LVGL display and touch */
static lv_display_t *lvgl_disp = NULL;

static esp_err_t spi_bus_init(void)
{
    esp_err_t ret = ESP_OK;

    spi_bus_config_t buscfg = {
        .sclk_io_num = EXAMPLE_LCD_GPIO_SCLK,
        .mosi_io_num = EXAMPLE_LCD_GPIO_MOSI,
        .miso_io_num = EXAMPLE_LCD_GPIO_MISO,
        .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),
    };

    // 初始化SPI 总线
    ESP_LOGD(TAG, "Initialize SPI bus");
    ESP_RETURN_ON_ERROR(spi_bus_initialize(EXAMPLE_LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO), TAG, "SPI init failed");

    return ret;
}

static esp_err_t app_lcd_init(void)
{
    esp_err_t ret = ESP_OK;

    ESP_LOGD(TAG, "Install LCD driver");
    // 初始化 LCD SPI
    ESP_LOGD(TAG, "Install panel 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_CLOCK_SPEED_HZ,
        .lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
        .lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
        .spi_mode = EXAMPLE_LCD_SPI_MODE,
        .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, "New panel IO failed");

    const esp_lcd_panel_dev_config_t panel_config = {
        //.reset_gpio_num = EXAMPLE_LCD_GPIO_RST,
        .reset_gpio_num = GPIO_NUM_NC,
        .color_space = EXAMPLE_LCD_COLOR_SPACE,
        .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, "New panel failed");

    esp_lcd_panel_reset(lcd_panel);
    esp_lcd_panel_init(lcd_panel);
    //esp_lcd_panel_mirror(lcd_panel, true, true);
    esp_lcd_panel_disp_on_off(lcd_panel, true);

    /* LCD backlight on 
    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);
    }
    spi_bus_free(EXAMPLE_LCD_SPI_NUM);
    return ret;
}

static esp_err_t app_lvgl_init(void)
{
    /* Initialize LVGL */
    const lvgl_port_cfg_t lvgl_cfg = {
        .task_priority = 4,         /* LVGL task priority */
        .task_stack = 6144,         /* LVGL task stack size */
        .task_affinity = -1,        /* LVGL task pinned to core (-1 is no affinity) */
        .task_max_sleep_ms = 500,   /* Maximum sleep in LVGL task */
        .timer_period_ms = 5        /* LVGL timer tick period in ms */
    };
    ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_cfg), TAG, "LVGL port initialization failed");

    /* Add LCD screen */
    ESP_LOGD(TAG, "Add LCD screen");
    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,
        .trans_size = EXAMPLE_LCD_H_RES * (EXAMPLE_LCD_DRAW_BUFF_HEIGHT / 2),
        .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 = false,
            .mirror_x = false,
            .mirror_y = true,
        },
        .flags = {
            //.buff_dma = true,
            .buff_spiram = true,
#if LVGL_VERSION_MAJOR >= 9
            //.swap_bytes = true,
#endif  
        }
    };
    lvgl_disp = lvgl_port_add_disp(&disp_cfg);
    

    return ESP_OK;
}

static void app_main_display(void)
{
    lv_obj_t *scr = lv_scr_act();

    /* Task lock */
    lvgl_port_lock(0);

    /* Label */
    lv_obj_t *label = lv_label_create(scr);
    lv_obj_set_width(label, EXAMPLE_LCD_H_RES);
    lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
#if LVGL_VERSION_MAJOR == 8
    lv_label_set_recolor(label, true);
    lv_label_set_text(label, "#FF0000 "LV_SYMBOL_BELL" Hello world Espressif and LVGL "LV_SYMBOL_BELL"#\n#FF9400 "LV_SYMBOL_WARNING" For simplier initialization, use BSP "LV_SYMBOL_WARNING" #");
#else
    lv_label_set_text(label, LV_SYMBOL_BELL" Hello world Espressif and LVGL "LV_SYMBOL_BELL"\n "LV_SYMBOL_WARNING" For simplier initialization, use BSP "LV_SYMBOL_WARNING);
#endif
    lv_obj_align(label, LV_ALIGN_CENTER, 0, 20);


    /* Task unlock */
    lvgl_port_unlock();
}

void lvgl_options_test(void)
{
    ESP_LOGD(TAG, "lvgl_options_test");

    /* SPI BUS  initialization*/
    ESP_ERROR_CHECK(spi_bus_init());

    /* LCD HW initialization */
    ESP_ERROR_CHECK(app_lcd_init());

    /* LVGL initialization */
    ESP_ERROR_CHECK(app_lvgl_init());

    /* Show LVGL objects */
    app_main_display();

    return;
}


3 烧录、验证

    经过上述的添加依赖组件、设定工程配置、编写必要代码后,就可以编译、烧录进设备进行验证了。测试结果显然是符合预期的。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2264604.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【hackmyvm】Zday靶机wp

HMVrbash绕过no_root_squash静态编译fogproject 1. 基本信息^toc 这里写目录标题 1. 基本信息^toc2. 信息收集2.1. 端口扫描2.2. 目录扫描 3. fog project Rce3.1. ssh绕过限制 4. NFS no_root_squash5. bash运行不了怎么办 靶机链接 https://hackmyvm.eu/machines/machine.ph…

neo4j console 报错

项目场景: neo4j 开启失败 问题描述 在终端打开 neo4j 失败打开cmd, 输入: neo4j console 报错 原因分析: 1 可能是没有配置环境变量2 当前脚本的执行策略有问题 解决方案: 解决没有配置环境变量 添加环境变量 在path路径中将变量添加进去…

范德蒙矩阵(Vandermonde 矩阵)简介:意义、用途及编程应用

参考: Introduction to Applied Linear Algebra – Vectors, Matrices, and Least Squares Stephen Boyd and Lieven Vandenberghe 书的网站: https://web.stanford.edu/~boyd/vmls/ Vandermonde 矩阵简介:意义、用途及编程应用 在数学和计算科学中&a…

编译原理复习---正则表达式+有穷自动机

适用于电子科技大学编译原理期末考试复习。 1. 正则表达式 正则表达式(Regular Expression,简称regex或regexp)是一种用于描述、匹配和操作文本模式的强大工具。它由一系列字符和特殊符号组成,这些字符和符号定义了一种搜索模式…

CAD跨图纸复制与粘贴怎么操作?教程来了

在过去,图纸的复制粘贴工作大多依赖于电脑完成,手机则因运行内存等硬件限制,难以像电脑那样轻松实现多图同开,以及图纸内容的跨图复制粘贴。为解决这一痛点,CAD看图王手机端推出了跨图复制与粘贴功能,为用户…

算法训练第二十三天|93. 复原 IP 地址 78. 子集 90. 子集 II

93. 复原 IP 地址--分割 题目 有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 . 分隔。 例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址&…

Go怎么做性能优化工具篇之基准测试

一、什么是基准测试(Benchmark) 在 Go 中,基准测试是通过创建以 Benchmark 开头的函数,并接收一个 *testing.B 类型的参数来实现的。testing.B 提供了控制基准测试执行的接口,比如设置测试执行的次数、记录每次执行的…

【贪吃蛇小游戏 - JavaIDEA】基于Java实现的贪吃蛇小游戏导入IDEA教程

有问题请留言或私信 步骤 下载项目源码:项目源码 解压项目源码到本地 打开IDEA 左上角:文件 → 新建 → 来自现有源代码的项目 找到解压在本地的项目源代码文件,点击确定 选择“从现有项目创建项目”。点击“下一步” 点击下一步&a…

LabVIEW手机屏幕耐冲击测试

开发了一个基于LabVIEW的智能手机屏幕耐冲击测试系统。系统利用LabVIEW软件与高精度传感器,对手机屏幕进行落球冲击试验,以测定其耐冲击性能。这项技术不仅提高了测试的精度和效率,而且对智能手机屏幕的质量控制和改进提供了科学依据。 项目背…

Python + 深度学习从 0 到 1(01 / 99)

希望对你有帮助呀!!💜💜 如有更好理解的思路,欢迎大家留言补充 ~ 一起加油叭 💦 欢迎关注、订阅专栏 【深度学习从 0 到 1】谢谢你的支持! ⭐ 深度学习之前:机器学习简史 什么要了解…

FPGA学习(基于小梅哥Xilinx FPGA)学习笔记

相关资源网站(小梅哥FPGA) https://www.corecourse.cn/forum.php?modviewthread&tid27978 https://www.corecourse.cn/forum.php?modviewthread&tid28730 本篇文章使用的开发板为: 小梅哥 Xilinx FPGA 型号:XC7A35T 芯…

网安瞭望台第17期:Rockstar 2FA 故障催生 FlowerStorm 钓鱼即服务扩张现象剖析

国内外要闻 Rockstar 2FA 故障催生 FlowerStorm 钓鱼即服务扩张现象剖析 在网络安全的复杂战场中,近期出现了一个值得关注的动态:名为 Rockstar 2FA 的钓鱼即服务(PhaaS)工具包遭遇变故,意外推动了另一个新生服务 Flo…

aws(学习笔记第十九课) 使用ECS和Fargate进行容器开发

aws(学习笔记第十九课) 使用ECS和Fargate进行容器开发 学习内容: 使用本地EC2中部署docker应用使用ECS的EC2模式进行容器开发使用ECS的Fargate模式进行容器开发 1. 使用本地EC2中部署docker应用 docker整体 这里展示了docker的整体流程。 开发阶段 编写dockerfile…

【Python 图片下载器】一款专门为爬虫制作的图片下载器,多线程下载,速度快,支持续传/图片缩放/图片压缩/图片转换

文章日期:2024.12.23 使用工具:Python 本章知识:制作一款图片下载器_DOS窗口(爬虫专用) 文章难度:低等(没难度) 文章全程已做去敏处理!!! 【需要…

关于博客系统的自动化功能测试报告

1.项目背景 基于 SSM 的个人博客系统测试 博客系统采用前后端分离的方法来实现,同时使用了数据库来存储相关的数据,前端主要有四个页面构成:登录页、列表页、详情页以及编辑页,模拟实现了个人博客列表页面,其结合后端实现了以下的…

计算机的错误计算(一百八十九)

摘要 用大模型计算 tan(12.345) . 自变量取弧度。结果保留10位有效数字。不同于前面两节的大模型,本节调用了新的两个大模型。然而,很遗憾,它们给出的答案似乎仍然是“匹配”出来的,不是计算出来的。当然,均是错误的。…

IP地址数据信息和爬虫拦截的关联

IP地址数据信息和爬虫拦截的关联主要涉及到两方面的内容,也就是数据信息和爬虫。IP 地址数据信息的内容丰富,包括所属地域、所属网络运营商、访问时间序列、访问频率等。 从IP地址信息中可以窥见多样的数据,那么我们应该怎么利用IP地址信息来…

springboot+logback学习文档

目录 1、前提说明2、引入依赖、将logback配置文件打到classes下2.1、引入依赖2.2、将logback配置文件打到classes下 3、使用说明3.1、配置文件名称和位置3.2、常规用法3.2.1、property标签(普通变量)3.2.2、springProperty标签(spring变量&am…

Laya ios接入goole广告,开始接入 2

开始使用 | iOS | Google for Developers 谷歌广告的官网,需要搭梯子,API你说详细吧,也就那样,主要是没接过 一步步来吧 0.laya导包 前端出包原生 screenorientation 全部 portrait,我这个是竖屏的 注意这个&a…

详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用

目录 详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用 一、什么是柯里化? 1、原理解析 2、一个直观的例子 二、如何实现柯里化? 1、底层实现 2、工作原理解析 3…