当前位置: 首页 > news >正文

ESP32驱动ST7920液晶屏:硬件连接、U8g2库配置与常见问题解决

1. 项目概述与核心价值

如果你手头正好有一块闲置的ST7920驱动的128x64点阵液晶屏,又恰好想用ESP32做个显示终端,比如温湿度监测站、小型游戏机或者设备状态面板,那么你很可能正面临一个经典问题:资料太少,无从下手。网上关于Arduino Uno驱动ST7920的教程不少,但一换到ESP32,引脚定义、库的配置方法似乎都变了样,直接照搬往往不灵。我自己在折腾这个小项目时,就深有体会,翻遍了论坛和代码库,才把这条路走通。今天,我就把自己从硬件连接到软件调试的全过程,包括中间踩过的坑和最终验证有效的方案,详细记录下来。这不仅仅是一个“接线-上传代码”的快速指南,我更想和你聊聊为什么这么接,库函数背后做了什么,以及当屏幕一片空白时,我们该如何一步步排查。无论你是刚接触嵌入式显示的爱好者,还是需要在ESP32项目里快速集成一个低成本显示屏的开发者,这篇内容都能给你提供一个清晰、可靠且深度优化的实现路径。

2. 硬件连接详解与原理剖析

2.1 认识ST7920液晶屏与接口定义

ST7920是一款非常经典的液晶显示控制器,它最大能驱动128x64的点阵,并且内部集成了中文字库(GB2312),对于显示中文信息非常友好。我们常见的带蓝色背光、绿色或黄绿色显示的12864屏幕,很多都是基于这颗芯片。它的接口方式比较灵活,支持8位/4位并行总线、串行SPI以及I2C(需转接板)模式。为了节省ESP32宝贵的GPIO引脚,我们通常选择SPI(串行外设接口)模式。

在SPI模式下,ST7920只需要4根线就能工作:

  1. SCLK (Serial Clock): 串行时钟线,由主设备(ESP32)产生,用于同步数据传输。
  2. SID (Serial Data): 串行数据线,用于传输指令或显示数据。
  3. CS (Chip Select) / PSB: 片选信号。这是关键!对于ST7920,当PSB引脚接低电平(GND)时,芯片进入串行模式。我们通常就直接用一根线将其固定接地,而不再用ESP32的GPIO去控制它。但有些教程或库代码中提到的“CS”引脚,在软件SPI模拟下,实际上可以复用为一个使能控制脚,不过ST7920在串行模式下对此要求不严格,这也是后面配置代码时的一个注意点。
  4. VCC & GND: 电源,通常是5V。但很多模块也兼容3.3V逻辑电平。

此外,屏幕还有一个V0引脚,用于调节对比度。它需要接一个可变电阻(电位器)的中心抽头,通过改变分压来调整屏幕显示的深浅。如果对比度不合适,即使程序正确,你也可能什么都看不到。

2.2 ESP32引脚选择与连接方案

ESP32的引脚功能非常灵活,但并非所有GPIO都适合用于模拟SPI。我们需要选择那些中断干扰少、输出稳定的引脚。根据广泛测试和我的实际经验,以下是一个可靠且通用的连接方案:

ST7920 引脚功能连接至 ESP32 引脚备注与原理
VCC电源正极5V 或 3.3V电压选择是关键。虽然ST7920逻辑电平可兼容3.3V,但其内部驱动和背光(如果直接供电)可能需要5V才能达到最佳对比度和亮度。如果使用3.3V,屏幕可能非常暗淡甚至不显示。建议优先尝试连接ESP32的5V引脚
GND电源地GND共地是必须的。
V0对比度调节10kΩ电位器中心抽头电位器另外两端分别接VCC和GND。旋转电位器即可调节对比度。
PSB并行/串行选择GND必须接GND,强制屏幕进入串行SPI模式。
RST复位ESP32 GPIO22 (可选)可接可不接。如果连接,可以通过程序控制复位;不接时,通常模块内部有上电复位电路。为求稳定,建议连接。
SCLK串行时钟ESP32 GPIO18这是硬件SPI的默认SCK引脚之一,输出稳定。
SID串行数据ESP32 GPIO23这是硬件SPI的默认MOSI引脚之一。
CS片选ESP32 GPIO5在软件SPI模拟中,此引脚被库用作通信使能。

实操心得:关于电源的坑我最开始将VCC接在了ESP32的3.3V引脚上,结果屏幕背光亮了,但无论如何调节对比度都没有任何显示。排查了很久代码和接线,最后用万用表量了一下屏幕VCC对地的电流,发现很小,怀疑是驱动电压不足。换成5V引脚后,屏幕立刻正常显示。所以,如果你的屏幕不亮,第一个要检查的就是电源电压。有些ESP32开发板(如NodeMCU-32S)的5V引脚实际上是USB输入电压(约5V),而VIN引脚是板载稳压器的输入,接电池或外部电源时用。这里我们接5V即可。

连接实物时,建议使用杜邦线在面包板上完成。确保电位器连接正确,这是导致“白屏”或“全黑屏”的第二大常见原因。接线完成后,先不要着急上传代码,检查一遍所有连接,尤其是GND和PSB是否可靠接地。

3. U8g2库的配置与深度解析

3.1 为什么选择U8g2库?

在Arduino生态中,驱动显示屏的库有很多,比如Adafruit_GFX配合特定控制器库、TFT_eSPI等。我选择U8g2库的原因有三点:

  1. 支持极其广泛:U8g2几乎支持所有你能想到的单色OLED和LCD控制器,ST7920只是其中一员。学会用它,以后换屏幕会非常省事。
  2. 功能全面:内置了多种字体(包括中文)、绘图函数(点、线、圆、矩形)、位图显示等功能,API设计相对统一。
  3. 双缓冲支持:库支持“全缓冲(Full Buffer)”模式,即先在内存中构建完整的画面,再一次性发送给屏幕。这虽然耗内存,但可以完全避免画面撕裂,对于动态图形显示非常重要。

3.2 库的安装与构造函数选择

在Arduino IDE中,通过“库管理器”搜索“U8g2”,由olikraus开发的那个就是,点击安装即可。安装完成后,我们面临第一个关键选择:在示例代码GraphicsTest中,该使用哪个构造函数?

打开文件->示例->U8g2->full_buffer->GraphicsTest。 在代码开头,你会看到一堆被注释掉的构造函数。我们的任务是找到并修改适合ESP32软件SPI驱动ST7920的那一行。

原始代码中通常有这样一行:

// U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* reset=*/ 8);

这行代码是针对Arduino Uno等AVR芯片的引脚示例。我们需要根据之前的硬件连接表修改它:

U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0, /* clock=*/ 18, /* data=*/ 23, /* cs=*/ 5, /* reset=*/ 22);

让我们拆解这个构造函数:

  • U8G2_ST7920_128X64_F_SW_SPI: 这是类名,指明了控制器型号(ST7920)、分辨率(128x64)、缓冲模式(F代表全缓冲)和通信方式(SW_SPI代表软件模拟SPI)。
  • U8G2_R0: 屏幕旋转参数。R0表示0度旋转,R1为90度,R2为180度,R3为270度。你可以根据屏幕实际安装方向调整。
  • clock=18: 对应我们连接的SCLK引脚(GPIO18)。
  • data=23: 对应我们连接的SID引脚(GPIO23)。
  • cs=5: 对应我们连接的CS引脚(GPIO5)。注意:在软件SPI模式下,这个CS引脚是必须的,库会用它来控制通信时序。
  • reset=22: 对应我们连接的RST复位引脚(GPIO22)。如果没接,这里可以填U8X8_PIN_NONE

注意事项:硬件SPI与软件SPI的选择在评论区有朋友提到可以使用硬件SPI以获得更快速度。这完全正确。硬件SPI由ESP32的专用外设处理,不占用CPU时间。你可以使用以下构造函数:

U8G2_ST7920_128X64_F_HW_SPI u8g2(U8G2_R0, /* cs=*/ 5, /* reset=*/ 22);

注意,此时只需要指定csreset引脚,clockdata引脚固定使用ESP32的默认硬件SPI引脚(VSPI: SCK=18, MOSI=23)。使用硬件SPI时,务必确保这些引脚没有被其他功能占用,并且接线对应(SCLK接SCK,SID接MOSI)。对于绝大多数应用,软件SPI已足够流畅,但如果你需要极高的刷新率或进行复杂的动画,硬件SPI是更好的选择。

3.3 GraphicsTest代码解析与上传

修改好构造函数后,你就可以直接上传GraphicsTest示例代码了。这个代码会循环运行一系列图形测试,包括画线、画圆、显示字体等,是验证屏幕是否正常工作的最佳选择。

在上传前,请务必:

  1. 在Arduino IDE的“工具”菜单中,正确选择你的ESP32开发板型号(如“ESP32 Dev Module”)。
  2. 选择正确的端口。
  3. 如果ESP32是第一次使用,你可能需要按住板上的“BOOT”按钮再点击上传,以进入下载模式。

上传成功后,ESP32会自动复位运行。此时观察你的屏幕,应该会看到各种图形和文字依次显示。如果屏幕有背光,背光应该常亮。

4. 从测试到应用:编写你自己的显示程序

4.1 程序框架与初始化

通过了测试,接下来我们就要编写自己的程序了。一个最基本的U8g2显示程序包含以下结构:

#include <U8g2lib.h> // 包含U8g2库 // 根据你的连接方式选择构造函数 U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0, /* clock=*/ 18, /* data=*/ 23, /* cs=*/ 5, /* reset=*/ 22); void setup(void) { u8g2.begin(); // 初始化显示屏 // 其他初始化代码... } void loop(void) { u8g2.clearBuffer(); // 清除内部缓冲区 // 在此处调用各种绘图函数... u8g2.sendBuffer(); // 将缓冲区内容发送到显示屏 delay(1000); // 延时 }

核心要点:

  • u8g2.begin(): 必须调用,用于初始化与屏幕的通信。
  • 绘制流程:U8g2采用“先绘制,后发送”的模式。所有drawXxx()函数(如drawStr,drawLine)都是在内存缓冲区里操作,屏幕并不会立即变化。
  • u8g2.clearBuffer(): 清空缓冲区,为绘制新一帧做准备。
  • u8g2.sendBuffer():这是关键!只有执行了这个函数,缓冲区里绘制好的内容才会被一次性发送到屏幕上显示出来。这保证了画面的完整性。

4.2 常用绘图函数示例

下面展示几个最常用的函数,你可以把它们放在loop()中的clearBuffer()sendBuffer()之间。

1. 显示文字:

u8g2.setFont(u8g2_font_ncenB08_tr); // 设置字体(这里是一种8像素高的字体) u8g2.drawStr(0, 10, "Hello World"); // 在坐标(0,10)处绘制字符串

U8g2内置了数十种字体,u8g2_font_ncenB08_tr只是其中之一。你可以在示例代码u8g2_full_buffer中的F部分找到所有字体预览。坐标(0,10)表示从左边界开始,距离顶部10个像素的位置。

2. 显示变量数值:

int sensorValue = 1234; char buffer[20]; // 定义一个字符数组作为缓冲区 sprintf(buffer, "Value: %d", sensorValue); // 将数值格式化成字符串 u8g2.drawStr(0, 20, buffer); // 绘制该字符串

3. 绘制图形:

u8g2.drawFrame(5, 15, 40, 20); // 绘制一个矩形框 (x, y, 宽, 高) u8g2.drawRFrame(50, 15, 40, 20, 5); // 绘制圆角矩形框,最后一个参数是圆角半径 u8g2.drawDisc(70, 50, 15); // 绘制实心圆 (圆心x, 圆心y, 半径) u8g2.drawCircle(30, 50, 15); // 绘制空心圆 u8g2.drawLine(0, 63, 127, 63); // 绘制一条从(0,63)到(127,63)的直线,作为底部边框

4. 显示中文:U8g2库对中文的支持需要额外步骤。库本身不包含完整中文字库(因为太大),但支持从外部存储(如SD卡)加载,或者使用“裁剪”后的自定义字体。对于初学者,更简单的方法是使用u8g2.setFont()设置一个包含所需中文的字体,但这类字体文件通常较大。一个折中的方案是,如果只需要少量汉字,可以将其转换为位图数组来显示。这涉及其他工具,超出了本文基础范围,但知道这个方向很重要。

4.3 一个完整的温湿度显示示例

假设我们有一个DHT11温湿度传感器,我们可以结合U8g2库,在屏幕上创建一个简单的信息面板。

#include <U8g2lib.h> #include <DHT.h> #define DHTPIN 4 // DHT数据引脚接GPIO4 #define DHTTYPE DHT11 U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0, 18, 23, 5, 22); DHT dht(DHTPIN, DHTTYPE); void setup() { Serial.begin(115200); u8g2.begin(); dht.begin(); u8g2.setFont(u8g2_font_helvB10_tf); // 设置一种稍大的字体 } void loop() { delay(2000); // DHT11读取间隔至少2秒 float h = dht.readHumidity(); float t = dht.readTemperature(); if (isnan(h) || isnan(t)) { Serial.println("读取DHT失败!"); return; } u8g2.clearBuffer(); // 绘制标题 u8g2.setFont(u8g2_font_helvB12_tf); u8g2.drawStr(20, 15, "Env Monitor"); u8g2.drawLine(0, 18, 128, 18); // 显示温度 u8g2.setFont(u8g2_font_helvB10_tf); u8g2.drawStr(10, 40, "Temp:"); u8g2.setCursor(70, 40); u8g2.print(t, 1); // 显示一位小数 u8g2.drawStr(110, 40, "C"); // 显示湿度 u8g2.drawStr(10, 60, "Humi:"); u8g2.setCursor(70, 60); u8g2.print(h, 1); u8g2.drawStr(110, 60, "%"); u8g2.sendBuffer(); }

这个例子展示了如何结合传感器库、使用不同字体、绘制文本和线条,以及格式化输出浮点数。u8g2.setCursor()用于设置接下来print()函数输出的起始位置,非常方便。

5. 常见问题排查与性能优化技巧

5.1 屏幕无任何显示(背光也不亮)

  1. 检查电源:用万用表测量屏幕VCC和GND之间的电压,确保在5V左右。检查ESP32的5V引脚是否有输出。
  2. 检查接地:确保屏幕的GND和ESP32的GND可靠连接。这是所有数字电路的基础。
  3. 检查PSB引脚必须确保PSB引脚连接到了GND,否则屏幕处于并行模式,无法响应SPI信号。
  4. 检查电位器:调节电位器,尝试整个旋转范围。对比度极端情况下(一端VCC,一端GND)屏幕可能全黑或全白。

5.2 屏幕背光亮但无内容(白屏)

  1. 对比度问题:这是最常见的原因。缓慢旋转电位器,观察屏幕是否有变化。有时合适的对比度点非常窄。
  2. 代码未上传/ESP32未运行:检查ESP32上的用户LED是否在闪烁,或者通过串口监视器输出调试信息,确认程序在运行。
  3. 引脚定义错误:仔细核对代码中的clockdatacsreset引脚号是否与实际接线一一对应。ESP32的GPIO编号有时在板子上标注的不是数字,需要查对原理图。
  4. 库构造函数错误:确认你取消注释并修改的是正确的构造函数。U8G2_ST7920_128X64_F_SW_SPI是针对全缓冲软件SPI的。如果你用了U8G2_ST7920_128X64_1_SW_SPI(非全缓冲),代码逻辑会有所不同。

5.3 显示乱码、错位或部分显示

  1. 通信速率问题:软件SPI模拟可能因为CPU忙于其他任务(如WiFi、复杂计算)导致时序出错。尝试在setup()中加入u8g2.setBusClock(1000000)来降低SPI时钟频率(单位Hz)。默认值可能较高,在某些接线质量下不稳定。
  2. 电源噪声:在ESP32��电源引脚(特别是5V)和GND之间并联一个100uF的电解电容和一个0.1uF的陶瓷电容,可以滤除电源噪声,使显示更稳定。
  3. 复位问题:如果接了RST引脚,尝试在setup()的最开始,手动控制一下复位:digitalWrite(22, LOW); delay(50); digitalWrite(22, HIGH); delay(50);然后再调用u8g2.begin()
  4. 缓冲区溢出:确保你的绘图操作没有超出屏幕范围(128x64)。虽然库可能不会报错,但越界写入可能导致不可预知的行为。

5.4 显示刷新慢、闪烁或卡顿

  1. 切换到硬件SPI:如前所述,使用U8G2_ST7920_128X64_F_HW_SPI构造函数可以极大提升刷新速度,并释放CPU资源。
  2. 优化绘图代码
    • 避免在loop()中频繁设置字体。如果字体不变,将setFont移到setup()中。
    • 只重绘变化的部分。如果界面只有部分区域更新,可以不用每次clearBuffer()全屏,而是用drawBox函数覆盖旧内容再画新的。但这需要更精细的逻辑控制。
    • 减少loop()中不必要的延迟delay()。对于需要定时更新的数据,使用millis()进行非阻塞定时。
  3. 使用更大的字体和图形谨慎:复杂的字体和大量图形填充(如drawDisc)会消耗更多的计算时间。在性能敏感的场合,使用小字体和线框图形。

5.5 关于电位器的补充说明

评论区有朋友说“10k pot does not do anything”。这通常是因为电位器接错了线。正确的接法是:电位器的三个引脚,两端分别接VCC和GND,中间的可变端接屏幕的V0。如果接错,调节当然无效。另外,有些屏幕模块已经集成了合适的偏压电路,V0直接接一个固定电阻到GND也可能工作,但为了灵活性,接电位器仍是推荐做法。

最后,驱动ST7920这类屏幕,最需要的就是耐心。硬件连接是基础,务必扎实。软件配置上,U8g2库已经封装得非常完善,我们的工作主要是正确初始化和调用API。当屏幕成功点亮并显示出第一行“Hello World”时,那种成就感就是驱动我们继续探索嵌入式世界的最佳动力。希望这篇详细的指南能帮你扫清障碍,顺利让ESP32和你的12864屏幕对话。如果在实践中遇到新问题,不妨从电源、地线、对比度这三个最基础的物理层面重新检查,往往能事半功倍。

http://www.jsqmd.com/news/950052/

相关文章:

  • 2026沈阳名表回收行业测评!5家正规机构实力盘点 - 奢侈品回收评测
  • 3个关键步骤:如何高效部署Visual C++运行库合集
  • 为什么83%的AI招聘工具在真实场景失效?深度拆解语义理解断层与上下文坍缩问题
  • 基于TPS61221的CR2032升压稳压模块设计:实现物联网传感器超长续航
  • 【央行新规倒计时60天】:AI转账系统必须通过的3项穿透式审计指标与2套压测验证模板
  • 终极免费方案:在PC上完美运行Switch游戏的完整指南
  • RTAB-Map完整指南:如何用开源SLAM库实现实时3D建图与定位
  • 注册环节的AI化已成生死线:2024Q2行业基准报告显示,未完成智能注册整合的企业获客成本高出2.8倍
  • 2026年四川膜结构厂家推荐榜:5家靠谱品牌深度评测 - 资讯纵览
  • 如何快速掌握LeagueAkari战绩分析工具:从零到精通的完整实战指南
  • 硅光芯片设计避坑指南:聊聊SOI脊型波导、Slot波导那些反直觉的特性与应用
  • AI工具接入信托业务前必须完成的9项穿透式验证(含FATF反洗钱AI审计清单)
  • QMC-Decoder 终极指南:专业音频解密与格式转换完整教程
  • Python自动化抢票实战:300行代码构建大麦网秒杀系统架构
  • 高通AEC10
  • 基于Arduino与WS2812B的智能RGB眼镜DIY:从硬件焊接、蓝牙控制到手机App开发
  • 如何快速掌握微信视频号直播数据采集工具:5步搭建实时监控系统
  • 3个关键步骤掌握GSE高级宏编译器:魔兽世界技能序列的革命性工具
  • 新手福音:用快马把论坛资料变成你的第一个可运行项目
  • 汽车电子EMC测试不过?别急着改板!先试试这5个‘土办法’定位干扰源
  • 济南品牌首饰回收哪家靠谱?六家正规平台权威排名 - 薛定谔的梨花猫
  • ai辅助开发:让快马平台的智能体成为你随问随答的“活体matlab帮助文档”
  • LPC2148 ARM7 SPI通信实战:从寄存器配置到主从模式调试
  • QrazyBox:专业级二维码修复工具,让不可扫描的二维码重获新生
  • Lingtrain Aligner:基于机器学习的智能文本对齐与平行语料库构建工具完全指南
  • NoFences:用开源智慧重构Windows桌面秩序的革命性方案
  • 2026武汉爱马仕回收实测测评——本地六家奢侈品回收门店横向对比 - 奢侈品回收测评
  • 如何快速构建现代化企业管理系统:Vue3+FastAPI完整实战教程
  • 毫米级精度怎么来的?聊聊相位式激光测距里的‘多把尺子’与混频技术
  • ControlNet-v1.1 FP16模型完全指南:从入门到精通的AI图像控制终极教程