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
显示初始化配置:在代码中指定屏幕宽高,设置屏幕旋转参数,适配板载屏幕的物理安装方向。
背光开启配置:将背光引脚设置为输出模式,上电后直接拉高电平点亮背光。
四、流程图

五、代码
#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) 构成简单的主循环。
七、实现效果


我要赚赏金
