这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » [ESP32‑S3ReverseTFTFeather评测]+驱动TFT和lvgl

共1条 1/1 1 跳转至

[ESP32‑S3ReverseTFTFeather评测]+驱动TFT和lvgl功能

菜鸟
2026-08-24 18:52:32     打赏

Adafruit-ESP32-S3-TFT-Feather点亮板载ST7789屏幕的完整实现步骤与方法:

 

一、硬件准备与基础

‌确认板载ST7789屏幕已通过内部总线连接,避免外接引脚冲突。

‌供电通过USB-C接口供电,确保3.3V电源稳定,避免电压波动导致屏幕初始化失败。

板载ST7789默认分辨率为240×135,部分版本为135×240,提前确认屏幕像素规格。我刚开始这里就弄反了,一直出不来。

 

二、Arduino IDE环境配置

我这里是用Arduino进行开发的,所以来个Arduino步骤配置。

‌安装ESP32开发板包‌

打开Arduino IDE,进入「文件→首选项」,添加ESP32开发板管理器网址。

https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json

进入「工具→开发板→开发板管理器」,搜索并安装‌2.0.13~2.0.16版本的ESP32开发板包‌,避免版本不兼容导致程序重启。

‌安装所需驱动库‌

通过库管理器安装Adafruit GFX Library核心图形库。

安装Adafruit ST7789 Library屏幕专用驱动库。

可选安装TFT_eSPI库,适配更高刷新率的并口传输场景。

 

三、引脚与显示参数配置

‌板载固定引脚定义‌:该开发板板载ST7789的引脚已硬件固化,无需自定义外接引脚:

TFT_CS:对应片选控制引脚

TFT_DC:对应数据/命令选择引脚

TFT_RST:对应屏幕复位引脚

TFT_BL:对应背光控制引脚

// 引脚定义

#define TFT_I2C_POWER  7

#define TFT_CS         42

#define TFT_DC         40

#define TFT_RESET      41

#define TFT_BACKLIGHT  45

 

‌显示初始化配置‌:在代码中指定屏幕宽高,设置屏幕旋转参数,适配板载屏幕的物理安装方向。

‌背光开启配置‌:将背光引脚设置为输出模式,上电后直接拉高电平点亮背光。

 

 

 

流程图

image.png

五、代码

#include <Adafruit_ST7789.h>
#include <SPI.h>
#include <lvgl.h>

// 引脚定义
#define TFT_I2C_POWER  7
#define TFT_CS         42
#define TFT_DC         40
#define TFT_RESET      41
#define TFT_BACKLIGHT  45

// ST7789 分辨率 - 横屏模式下交换宽高
#define TFT_WIDTH  135
#define TFT_HEIGHT 240

// LVGL缓冲大小
#define LVGL_BUF_SIZE (TFT_WIDTH * 20)

// 硬件SPI
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RESET);

// LVGL显示缓冲
static lv_disp_draw_buf_t disp_buf;
static lv_color_t *buf;

// LVGL显示驱动
static lv_disp_drv_t disp_drv;

// 定时器数据结构
struct TimerData {
    lv_obj_t *bar;
    lv_obj_t *label;
};

// 显示刷新函数
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
    uint32_t w = (area->x2 - area->x1 + 1);
    uint32_t h = (area->y2 - area->y1 + 1);
   
    tft.startWrite();
    tft.setAddrWindow(area->x1, area->y1, w, h);
    tft.writePixels((uint16_t *)color_p, w * h, true, false);
    tft.endWrite();
   
    lv_disp_flush_ready(disp);
}

// 按钮事件回调
void btn_event_cb(lv_event_t *e) {
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t *btn = lv_event_get_target(e);
   
    if (code == LV_EVENT_CLICKED) {
        static bool clicked = false;
        if (!clicked) {
            lv_obj_set_style_bg_color(btn, lv_color_hex(0x4CAF50), LV_STATE_DEFAULT);
            clicked = true;
        } else {
            lv_obj_set_style_bg_color(btn, lv_color_hex(0x2196F3), LV_STATE_DEFAULT);
            clicked = false;
        }
       
        lv_obj_t *msg_label = lv_label_create(lv_scr_act());
        lv_label_set_text(msg_label, "Button Clicked!");
        lv_obj_set_style_text_color(msg_label, lv_color_hex(0x00FF00), LV_STATE_DEFAULT);
        lv_obj_set_style_text_font(msg_label, &lv_font_montserrat_14, LV_STATE_DEFAULT);
        lv_obj_align(msg_label, LV_ALIGN_CENTER, 0, 50);
       
        static lv_timer_t *timer = NULL;
        if (timer) {
            lv_timer_del(timer);
        }
        timer = lv_timer_create([](lv_timer_t *t) {
            lv_obj_del((lv_obj_t *)t->user_data);
        }, 1000, msg_label);
        lv_timer_set_repeat_count(timer, 1);
    }
}

// 进度条更新定时器回调
void update_bar_cb(lv_timer_t *t) {
    static int32_t val = 0;
    TimerData *data = (TimerData *)t->user_data;
   
    val = (val + 5) % 101;
    lv_bar_set_value(data->bar, val, LV_ANIM_ON);
   
    char buf[20];
    snprintf(buf, sizeof(buf), "Loading: %d%%", (int)val);
    lv_label_set_text(data->label, buf);
}

void setup(void) {
    Serial.begin(115200);
    Serial.println("LVGL ST7789 Landscape Mode Starting...");

    // 开启屏幕电源
    pinMode(TFT_I2C_POWER, OUTPUT);
    digitalWrite(TFT_I2C_POWER, HIGH);
    delay(20);

    // 背光引脚
    pinMode(TFT_BACKLIGHT, OUTPUT);
    digitalWrite(TFT_BACKLIGHT, HIGH);
    delay(10);

    // ===== 初始化ST7789 - 设置横屏模式 =====
    tft.init(TFT_WIDTH, TFT_HEIGHT);
   
    // setRotation 参数:
    // 0 = 竖屏 (240x135)
    // 1 = 横屏 (135x240) - 顺时针旋转90度
    // 2 = 竖屏翻转 (240x135)
    // 3 = 横屏翻转 (135x240) - 逆时针旋转90度
    tft.setRotation(1);  // 横屏模式
   
    tft.fillScreen(ST77XX_BLACK);

    // 初始化LVGL
    lv_init();

    // 分配显示缓冲区
    buf = (lv_color_t *)malloc(LVGL_BUF_SIZE * sizeof(lv_color_t));
    if (buf == NULL) {
        Serial.println("Failed to allocate LVGL buffer!");
        return;
    }
    Serial.println("LVGL buffer allocated");

    // 初始化显示缓冲区
    lv_disp_draw_buf_init(&disp_buf, buf, NULL, LVGL_BUF_SIZE);

    // ===== 初始化显示驱动 - 横屏模式下交换宽高 =====
    lv_disp_drv_init(&disp_drv);
    // 横屏模式:宽 = 135, 高 = 240
    disp_drv.hor_res = TFT_HEIGHT;  // 135 -> 横屏时作为宽度
    disp_drv.ver_res = TFT_WIDTH;   // 240 -> 横屏时作为高度
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.draw_buf = &disp_buf;
    lv_disp_drv_register(&disp_drv);

    // 创建UI界面
    create_ui();
}

// 创建UI界面 - 横屏模式布局优化
void create_ui(void) {
    lv_obj_t *scr = lv_scr_act();

    lv_obj_set_style_bg_color(scr, lv_color_hex(0x1A1A2E), LV_STATE_DEFAULT);

    // ===== 标题 =====
    lv_obj_t *label_title = lv_label_create(scr);
    lv_label_set_text(label_title, "LVGL Landscape");
    lv_obj_set_style_text_color(label_title, lv_color_hex(0xFFFFFF), LV_STATE_DEFAULT);
    lv_obj_set_style_text_font(label_title, &lv_font_montserrat_16, LV_STATE_DEFAULT);
    lv_obj_align(label_title, LV_ALIGN_TOP_MID, 0, 10);

    // ===== 子标题 =====
    lv_obj_t *label_sub = lv_label_create(scr);
    lv_label_set_text(label_sub, "ESP32-S3 + LVGL v8");
    lv_obj_set_style_text_color(label_sub, lv_color_hex(0x88CCFF), LV_STATE_DEFAULT);
    lv_obj_set_style_text_font(label_sub, &lv_font_montserrat_12, LV_STATE_DEFAULT);
    lv_obj_align(label_sub, LV_ALIGN_TOP_MID, 0, 35);

    // ===== 按钮 (左侧) =====
    lv_obj_t *btn = lv_btn_create(scr);
    lv_obj_set_size(btn, 100, 40);
    lv_obj_align(btn, LV_ALIGN_CENTER, -50, 10);
    lv_obj_set_style_bg_color(btn, lv_color_hex(0x2196F3), LV_STATE_DEFAULT);
    lv_obj_set_style_radius(btn, 10, LV_STATE_DEFAULT);
    lv_obj_set_style_shadow_width(btn, 8, LV_STATE_DEFAULT);
    lv_obj_set_style_shadow_color(btn, lv_color_hex(0x2196F3), LV_STATE_DEFAULT);
   
    lv_obj_t *btn_label = lv_label_create(btn);
    lv_label_set_text(btn_label, "Click Me!");
    lv_obj_set_style_text_color(btn_label, lv_color_hex(0xFFFFFF), LV_STATE_DEFAULT);
    lv_obj_set_style_text_font(btn_label, &lv_font_montserrat_14, LV_STATE_DEFAULT);
    lv_obj_center(btn_label);

    lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL);

    // ===== 进度条 (右侧) =====
    lv_obj_t *bar = lv_bar_create(scr);
    lv_obj_set_size(bar, 100, 15);
    lv_obj_align(bar, LV_ALIGN_CENTER, 50, 10);
    lv_bar_set_range(bar, 0, 100);
    lv_bar_set_value(bar, 0, LV_ANIM_OFF);
    lv_obj_set_style_bg_color(bar, lv_color_hex(0x333355), LV_STATE_DEFAULT);
    lv_obj_set_style_bg_opa(bar, LV_OPA_80, LV_STATE_DEFAULT);
    lv_obj_set_style_radius(bar, 8, LV_STATE_DEFAULT);

    // ===== 进度条标签 (显示在进度条上方) =====
    lv_obj_t *bar_label = lv_label_create(scr);
    lv_label_set_text(bar_label, "0%");
    lv_obj_set_style_text_color(bar_label, lv_color_hex(0xCCCCCC), LV_STATE_DEFAULT);
    lv_obj_set_style_text_font(bar_label, &lv_font_montserrat_12, LV_STATE_DEFAULT);  // 改用12号字体
    lv_obj_align_to(bar_label, bar, LV_ALIGN_OUT_TOP_MID, 0, -5);

    // 创建定时器数据
    TimerData *data = (TimerData *)malloc(sizeof(TimerData));
    if (data) {
        data->bar = bar;
        data->label = bar_label;
       
        lv_timer_t *timer = lv_timer_create(update_bar_cb, 500, data);
        lv_timer_set_repeat_count(timer, -1);
    }
   
    // ===== 旋转指示器 (右下角) =====
    lv_obj_t *spinner = lv_spinner_create(scr, 1000, 60);
    lv_obj_set_size(spinner, 25, 25);
    lv_obj_align(spinner, LV_ALIGN_BOTTOM_RIGHT, -15, -15);
    lv_obj_set_style_arc_color(spinner, lv_color_hex(0x00CCFF), LV_STATE_DEFAULT);
    lv_obj_set_style_arc_width(spinner, 3, LV_STATE_DEFAULT);

    // ===== 状态标签 (左下角) =====
    lv_obj_t *status_label = lv_label_create(scr);
    lv_label_set_text(status_label, "Landscape Mode");
    lv_obj_set_style_text_color(status_label, lv_color_hex(0x88CCFF), LV_STATE_DEFAULT);
    lv_obj_set_style_text_font(status_label, &lv_font_montserrat_12, LV_STATE_DEFAULT);  // 改用12号字体
    lv_obj_align(status_label, LV_ALIGN_BOTTOM_LEFT, 15, -15);
}

void loop() {
    lv_timer_handler();
    delay(5);
}


六、代码介绍

硬件配置与初始化模块

功能:定义屏幕引脚(电源、CS、DC、复位、背光)和分辨率(135x240),并在 setup() 中初始化串口、屏幕电源和背光。

关键代码:pinMode() 和 digitalWrite() 控制电源;tft.init() 和 tft.setRotation(1) 驱动ST7789屏幕进入横屏模式。


2. LVGL 图形库集成模块

功能:为LVGL分配内存缓冲区,并配置显示驱动,使LVGL的绘图命令能通过 my_disp_flush() 函数输出到ST7789屏幕。

关键代码:lv_init();malloc() 分配缓冲区;lv_disp_draw_buf_init() 和 lv_disp_drv_register() 注册自定义显示驱动。


3. UI界面创建模块 (create_ui 函数)

功能:构建了完整的用户界面,包含标题、可交互按钮、动态进度条及其标签、旋转指示器和状态标签等7个核心元素。

关键代码:使用 lv_*_create() 系列函数创建对象,并用 lv_obj_align() 和 lv_obj_set_style_*() 设置位置与样式。


4. 事件响应与交互模块

功能:通过 btn_event_cb() 函数处理按钮点击事件,实现按钮颜色切换和弹出临时提示消息,并自动在1秒后消失。

关键代码:lv_obj_add_event_cb() 绑定事件;lv_event_get_code() 和 lv_event_get_target() 判断事件类型并获取对象。


5. 动态更新与定时器模块

功能:利用LVGL定时器,在 update_bar_cb() 函数中每500毫秒更新一次进度条的值和显示文本,实现动态效果。

关键代码:lv_timer_create() 创建定时器;lv_bar_set_value() 和 lv_label_set_text() 更新UI。


6. 主循环与系统调度模块 (loop 函数)

功能:不断调用 lv_timer_handler() 处理LVGL的内核事件、动画和定时器回调,是系统持续运行的驱动力。

关键代码:lv_timer_handler() 和 delay(5) 构成简单的主循环。

 

七、实现效果

ebe48c8990f0ee357b86e63f2ed343fb.jpg

5f69eab55f7819382a275aac5d75b7de.jpg





共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]