电子说
一、项目概述
本文介绍如何使用ESP32微控制器驱动ST7789 TFT液晶屏和XPT2046触摸芯片,通过LVGL图形库实现主题切换功能,并开发一个实用的触摸屏示波器应用。项目包含两大核心功能:
LVGL多主题切换:支持8种不同风格的UI主题
示波器功能:
模拟/数字信号采集、触摸控制面板、光标测量系统、自动频率检测
二、硬件准备
| 组件 | 型号 | 说明 |
|---|---|---|
| 主控 | 零知ESP32 | 双核240MHz处理器 |
| 屏幕 | ST7789 2.4寸 | 240×320分辨率 |
| 触摸芯片 | XPT2046 | 电阻式触摸控制器 |
| 接线 | SPI | 使用硬件SPI接口 |
接线图

三、环境搭建
1. 安装库
lv_arduino v3.0.1
TFT_eSPI
XPT2046_Touchscreen
2. TFT_eSPI配置(User_Setup.h):
#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display #define TFT_WIDTH 240 // ST7789 240 x 240 #define TFT_HEIGHT 320 // ST7789 240 x 320 #define TFT_MISO 19 #define TFT_MOSI 23 #define TFT_SCLK 18 #define TFT_CS 15 Chip select control pin #define TFT_DC 2 Data Command control pin #define TFT_RST 4 Reset pin (could connect to RST pin)
四、核心代码解析
4.1 LVGL主题设置
// 主题初始化
void setup() {
lv_test_theme(); // 默认主题
// 可选主题:
// lv_test_theme_1(lv_theme_night_init(210, NULL));
// lv_test_theme_1(lv_theme_material_init(210, NULL));
}
// 显示驱动回调
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
tft.setAddrWindow(area- >x1, area- >y1, area- >x2, area- >y2);
for(int y=area- >y1; y<=area- >y2; y++){
for(int x=area- >x1; x<=area- >x2; x++){
tft.writeColor(color_p- >full, 1);
color_p++;
}
}
lv_disp_flush_ready(disp);
}
4.2 触摸驱动(带消抖)
bool my_touchpad_read(lv_indev_drv_t *indev, lv_indev_data_t *data) {
static lv_coord_t last_x = 0, last_y = 0;
bool is_touched = ts.touched();
if(is_touched){
TS_Point p = ts.getPoint();
// 坐标转换与校准
last_x = map(p.x, cal_x_min, cal_x_max, 0, 320);
last_y = map(p.y, cal_y_max, cal_y_min, 0, 240);
// 滑动检测
if(abs(last_x - prev_x) >5 || abs(last_y - prev_y) >5){
is_sliding = true;
}
// 消抖处理
if(!is_sliding && (millis()-last_touch_time) >CLICK_DEBOUNCE_MS){
last_touch_time = millis();
}
}
data- >point.x = last_x;
data- >point.y = last_y;
return false;
}
4.3 简易示波器核心逻辑
波形采样:
void takeSample() {
if(!digitalMode){ // 模拟模式
uint16_t raw = analogRead(ADC_PIN);
samplesBuffer[sampleIndex] = raw * amplitudeScale;
}else{ // 数字模式
bool state = digitalRead(DIGITAL_PIN);
samplesBuffer[sampleIndex] = state ? 4095 : 0;
}
sampleIndex = (sampleIndex+1) % MAX_SAMPLES;
}
波形绘制:
void updateWaveform() {
waveSprite.fillSprite(BG_COLOR);
// 绘制网格
for(int x=0; x< GRAPH_WIDTH; x+=32){
waveSprite.drawFastVLine(x,0,GRAPH_HEIGHT,GRID_COLOR);
}
// 绘制波形
for(int i=0; i< visibleSamples; i++){
uint16_t y = map(samplesBuffer[i],0,4095,GRAPH_HEIGHT,0);
waveSprite.drawPixel(i*2, y, WAVE_COLOR);
}
// 绘制光标
if(cursorMode){
waveSprite.drawFastVLine(cursor1Pos,0,GRAPH_HEIGHT,CURSOR1_COLOR);
waveSprite.drawFastVLine(cursor2Pos,0,GRAPH_HEIGHT,CURSOR2_COLOR);
}
waveSprite.pushSprite(0, GRAPH_TOP);
}
五、触摸控制面板实现
void create_ui(){
// 创建选项卡式控制面板
lv_obj_t *tabview = lv_tabview_create(rightPanel, NULL);
lv_obj_t *timeTab = lv_tabview_add_tab(tabview, "时基");
// 时基选择滚轮
lv_obj_t *timebaseRoller = lv_roller_create(timeTab);
lv_roller_set_options(timebaseRoller, "0.5msn1.0msn2.0ms", LV_ROLLER_MODE_NORMAL);
// 幅度控制滑块
lv_obj_t *ampSlider = lv_slider_create(ampTab);
lv_slider_set_range(ampSlider, 0, 400);
// 模式切换开关
modeSwitch = lv_switch_create(controlPanel);
lv_obj_set_event_cb(modeSwitch, mode_switch_event_cb);
}
六、性能优化技巧
双缓冲机制:使用TFT_eSPI的Sprite功能减少闪烁
异步采样:在loop()中分离采样和显示逻辑
动态刷新:根据时基自动调整刷新率
内存管理:
static lv_color_t buf[LV_HOR_RES_MAX * 10]; // LVGL缓冲区 TFT_eSprite waveSprite = TFT_eSprite(&tft); // 波形缓冲
七、实测效果
7.1 LVGL不同主题切换

演示视频:
https://www.bilibili.com/video/BV1CwjAziEKj/?spm_id_from=888.80997.embed_other.whitelist&bvid=BV1CwjAziEKj&vd_source=a31e3d8d8ce008260eee442534c2f63d
7.2示波器触控界面效果

演示视频:
https://www.bilibili.com/video/BV1FxjwzwEyF/?spm_id_from=888.80997.embed_other.whitelist&bvid=BV1FxjwzwEyF&vd_source=a31e3d8d8ce008260eee442534c2f63d
八、常见问题解决
触摸校准异常
修改cal_x_min/max和cal_y_min/max值
使用校准例程获取实际参数
LVGL内存不足
// 修改lv_conf.h配置 #define LV_MEM_SIZE (48*1024) #define LV_DISP_DEF_REFR_PERIOD 30
完整代码获取:
Github仓库:https://github.com/Leeri1y/esp32-st7789
百度网盘:https://pan.baidu.com/s/19TVS2PBJpYqExxsannnH3w?pwd=epyd 提取码: epyd
互动交流: 欢迎在评论区留言讨论,遇到问题可提交Issues
附录:关键函数速查表
| 函数 | 功能 | 调用示例 |
|---|---|---|
| lv_btn_create() | 创建按钮 | lv_obj_t * btn = lv_btn_create(parent, NULL); |
| lv_sw_get_state() | 获取开关状态 | bool state = lv_sw_get_state(sw); |
| lv_roller_set_options() | 设置滚轮选项 | lv_roller_set_options(roller, "1n2n3", true); |
| waveSprite.pushSprite() | 刷新显示 | waveSprite.pushSprite(0, 0); |
| ts.touched() | 检测触摸 | if(ts.touched()) { ... } |
✅ 动手实践建议
先运行基础显示示例验证硬件连接
逐步添加功能模块(先显示后触摸)
使用串口监视器调试输出
保存不同版本的代码备份
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !