这块巴掌大小的液晶屏在创客圈里可是个老熟人,我第一次用它做温湿度监测器时,就被它清晰的点阵显示惊艳到了。ST7920控制器驱动的LCD12864模块,本质上是个128列×64行的点阵画布,相当于在邮票大小的区域塞进了8192个可控像素点。不同于常见的1602字符液晶,它能显示任意图形和汉字,就像给你的Arduino项目配了块微型黑板。
我手头这块蓝底白字的经典款,工作电压5V,功耗不到5mA。背面的20针排针乍看吓人,实际常用引脚就7个:VCC和GND负责供电,PSB引脚接地选择串行模式(省下5个IO口),剩下的CS、SID、SCLK接任意数字引脚即可通信。特别提醒新手朋友:RST复位引脚最好接Arduino的RESET,否则上电时可能出现乱码,这个坑我当年踩了三次才找到原因。
为什么选择U8g2而不是其他库?三年前我做智能家居控制器时,试过至少四种液晶驱动库,最终U8g2以三个优势胜出:首先是跨平台支持,同一套代码稍作修改就能驱动OLED和各类LCD;其次是内置了中文等非ASCII字符的完美支持;最重要的是它的页面缓冲机制,在内存有限的Arduino上实现了流畅的动态刷新。
安装过程比想象中简单:打开Arduino IDE 2.3.2,点击「工具」-「管理库」,输入"U8g2"搜索,认准Oliver Kraus开发的版本(当前最新是2.34.24)。安装时有个隐藏技巧——勾选"包含依赖项",这样会自动安装配套的U8x8库。遇到网络问题的话,可以到GitHub下载zip包,通过「项目」-「加载库」-「添加.ZIP库」手动安装。
根据我调试过三十多块不同厂家LCD的经验,ST7920的接线陷阱主要集中在三点:一是PSB引脚必须接GND选择串行模式,接VCC会导致通信失败;二是背光LED需要串联220Ω电阻,直接接5V容易烧毁;三是部分国产模块的引脚顺序可能与标称不符。
推荐这个经过验证的接线方案:
用杜邦线连接时,建议用不同颜色区分电源(红)、地线(黑)和信号线(黄),这样排查故障时一目了然。曾有个学员因为全用灰色线导致短路,烧坏了LCD的驱动芯片。
先来看这个经过优化的基础示例代码:
cpp复制#include <U8g2lib.h>
// 软件SPI配置:时钟D4,数据D3,片选D2
U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0, 4, 3, 2);
void setup() {
u8g2.begin(); // 初始化显示
u8g2.enableUTF8Print(); // 启用UTF-8字符支持
}
void loop() {
u8g2.firstPage(); // 开始页面缓冲
do {
u8g2.setFont(u8g2_font_unifont_t_chinese2); // 中文字体
u8g2.setCursor(0, 16);
u8g2.print("你好世界"); // 中文显示
u8g2.setFont(u8g2_font_profont12_tf); // 英文字体
u8g2.drawStr(0, 32, "Temperature:");
} while (u8g2.nextPage()); // 结束页面缓冲
delay(100);
}
这段代码展示了三个关键技巧:一是使用enableUTF8Print()激活中文支持;二是混合使用两种字体提升显示效果;三是采用页面缓冲机制避免闪屏。实际测试发现,unifont_t_chinese2字体能完美显示常用汉字,而profont12_tf等比例字体适合数据展示。
现在升级到真正的动态监控系统。假设我们连接了DHT11温湿度传感器,以下是实时刷新的核心代码:
cpp复制#include <DHT.h>
#define DHTPIN 5
DHT dht(DHTPIN, DHT11);
void setup() {
dht.begin();
}
void loop() {
float h = dht.readHumidity();
float t = dht.readTemperature();
u8g2.firstPage();
do {
// 绘制边框
u8g2.drawFrame(0,0,128,64);
// 显示标题
u8g2.setFont(u8g2_font_helvB08_tf);
u8g2.drawUTF8(15, 12, "环境监测仪表盘");
// 显示温度数据
u8g2.drawRBox(10,20,45,20,5); // 圆角背景
u8g2.setColorIndex(0); // 白色文字
u8g2.setCursor(15, 35);
u8g2.print("Temp:");
u8g2.print(t,1);
u8g2.print("C");
// 显示湿度数据(恢复黑色文字)
u8g2.setColorIndex(1);
u8g2.drawRBox(70,20,45,20,5);
u8g2.setColorIndex(0);
u8g2.setCursor(75, 35);
u8g2.print("Humi:");
u8g2.print(h,1);
u8g2.print("%");
// 添加进度条效果
u8g2.drawBox(10,50,map(t,0,50,0,108),5);
} while (u8g2.nextPage());
delay(2000); // 2秒刷新一次
}
这个案例实现了:带标题的仪表盘框架、数据反白显示、温度进度条等实用功能。其中map()函数将温度值映射为进度条长度,drawRBox()创建了现代化的圆角面板。实测在Arduino Uno上,整个界面刷新耗时约28ms,完全不会影响传感器读取。
当需要显示更多数据时,可以通过按钮切换页面。这里给出经过验证的三页面方案:
cpp复制int page = 0;
const int btnPin = 6;
void setup() {
pinMode(btnPin, INPUT_PULLUP);
}
void loop() {
// 检测按钮切换页面
if(digitalRead(btnPin)==LOW){
page = (page+1)%3;
delay(300); // 防抖
}
u8g2.firstPage();
do {
switch(page){
case 0: // 实时数据页
drawDashboard();
break;
case 1: // 历史曲线页
drawGraph();
break;
case 2: // 设置页
drawSettings();
break;
}
} while(u8g2.nextPage());
}
void drawDashboard(){
// 仪表盘绘制代码
}
void drawGraph(){
// 折线图绘制代码
u8g2.drawHLine(10,50,100); // X轴
u8g2.drawVLine(10,20,30); // Y轴
// 这里添加数据点连线
}
void drawSettings(){
// 参数设置界面
}
通过digitalRead()检测按钮状态,用取模运算实现页面循环切换。每个页面的绘制代码封装成独立函数,保持主循环整洁。实际项目中,可以在drawGraph()里加入SD卡模块读取历史数据,用drawPixel()或drawLine()绘制曲线。
在长期使用中,我总结了这些优化经验:
刷新速率:ST7920的SPI时钟最高1MHz,实测500kHz时稳定性最好。通过u8g2.setBusClock(500000)设置时钟速度,刷新速度可从15fps提升到22fps。
内存管理:启用U8G2_R0旋转模式最省内存。若出现显示残影,尝试在loop()开头添加u8g2.clearDisplay()。
字体选择:推荐使用u8g2_font_profont10_mf等位图字体,比矢量字体快3倍。中文显示优先选择unifont系列。
干扰处理:当屏幕出现条纹干扰时,在VCC和GND之间并联100μF电容,信号线串联100Ω电阻。
遇到白屏时,按这个检查流程排查:
最后分享一个显示优化技巧:动态数据更新时,先用setColorIndex(0)白色擦除原有内容,再绘制新数据,能有效避免残影。对于频繁更新的数值,可以只重绘变化部分而非整个页面。