前面我们学了舵机、超声波、红外遥控,Arduino 已经能"动"、能"看"、能"遥控"了。但有个问题一直没解决——所有数据只能靠串口监视器看,你得连电脑才能知道 Arduino 在干什么。
今天这篇,我们要给 Arduino 装上一块OLED 显示屏,让它能独立显示文字、数字、图形,彻底摆脱电脑的束缚。
学完这篇,你会完成三个项目:
- 文字显示:在 OLED 上显示自定义文字和实时数据
- 图形绘制:画线、画圆、画矩形,制作简易仪表盘
- 综合项目:智能测距仪:超声波测距 + OLED 实时显示,做成一个独立的小设备
一、OLED 显示屏是什么?
OLED(有机发光二极管)是一种自发光显示技术,和 LCD 不同,它不需要背光,每个像素自己发光。
OLED 的优势
- 自发光:黑色区域完全不亮,对比度极高
- 超薄:模块非常薄,适合嵌入各种项目
- 低功耗:只点亮需要的像素,省电
- 响应快:刷新速度快,适合动态显示
常见规格
Arduino 最常用的 OLED 模块是0.96 英寸 128×64 像素,有蓝色、白色、双色(上黄下蓝)等颜色可选。
二、通信方式:I2C
我们使用的 OLED 模块通过I2C 协议与 Arduino 通信,只需要 4 根线:
| 引脚 | 接到 | 作用 |
|---|---|---|
| VCC | 5V(或 3.3V) | 供电 |
| GND | GND | 接地 |
| SCL | A5 | I2C 时钟线 |
| SDA | A4 | I2C 数据线 |
I2C 是一种"总线"协议,多个设备可以共用 SCL 和 SDA 两根线。每个设备有自己的地址,Arduino 通过地址来区分它们。
查找 I2C 地址
OLED 模块的 I2C 地址通常是0x3C或0x3D。如果不确定,可以用 I2C 扫描程序查找:
#include <Wire.h> void setup() { Serial.begin(9600); Wire.begin(); Serial.println("=== I2C 地址扫描 ==="); for (byte address = 1; address < 127; address++) { Wire.beginTransmission(address); if (Wire.endTransmission() == 0) { Serial.print("找到设备,地址: 0x"); Serial.println(address, HEX); } } Serial.println("扫描完成"); } void loop() {}上传后打开串口监视器,它会列出所有连接在 I2C 总线上的设备地址。
三、安装必要的库
需要安装两个库:
- Adafruit SSD1306:OLED 驱动库
- Adafruit GFX Library:图形绘制库
安装步骤:
- 菜单工具 → 管理库
- 搜索Adafruit SSD1306,安装
- 搜索Adafruit GFX Library,安装
四、硬件清单
| 元件 | 数量 | 作用 |
|---|---|---|
| Arduino Uno | 1块 | 主控板 |
| 面包板 | 1块 | 搭建电路 |
| 0.96寸 OLED(I2C) | 1个 | 显示屏 |
| HC-SR04 超声波模块 | 1个 | 综合项目测距 |
| 杜邦线 | 若干 | 连接元件 |
五、电路接线
OLED 接线
- VCC→ 接 5V
- GND→ 接 GND
- SCL→ 接 Arduino 的A5
- SDA→ 接 Arduino 的A4
HC-SR04 接线(综合项目用)
- VCC→ 接 5V
- GND→ 接 GND
- Trig→ 接 Arduino 的D9
- Echo→ 接 Arduino 的D10
六、实验一:显示文字
在 OLED 上显示"Hello, Arduino!"和实时更新的计数器。
#include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> // 屏幕参数 #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 // 没有复位引脚,设为 -1 // 创建显示对象 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); int counter = 0; void setup() { Serial.begin(9600); // 初始化 OLED if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println("OLED 初始化失败!"); for (;;); // 初始化失败,停在这里 } Serial.println("OLED 初始化成功"); // 清屏 display.clearDisplay(); // 设置文字大小(1~5,数字越大字越大) display.setTextSize(1); // 设置文字颜色(SSD1306_WHITE 表示点亮像素) display.setTextColor(SSD1306_WHITE); // 设置光标位置(x, y) display.setCursor(0, 0); // 输出文字 display.println("Hello, Arduino!"); display.println("----------------"); display.println("OLED 显示测试"); // 将缓冲区内容刷新到屏幕 display.display(); } void loop() { // 清屏 display.clearDisplay(); display.setTextSize(2); display.setCursor(0, 10); display.println("Counter:"); display.setTextSize(3); display.setCursor(20, 35); display.println(counter); // 刷新显示 display.display(); counter++; delay(500); }代码解读
display.begin(SSD1306_SWITCHCAPVCC, 0x3C):初始化 OLED,参数是 I2C 地址display.clearDisplay():清空屏幕(只是清空缓冲区,还没显示)display.setTextSize(n):设置文字大小,1 是最小,数字越大字越大display.setCursor(x, y):设置下一个文字的起始位置display.println():写入文字到缓冲区display.display():把缓冲区的内容真正刷新到屏幕上
注意:所有
display.println()只是写入缓冲区,必须调用display.display()才会在屏幕上显示。这样设计是为了提高刷新效率——你可以一次性准备好所有内容,再一次性刷新。
七、实验二:图形绘制
OLED 不仅能显示文字,还能画各种图形。我们用 Adafruit GFX 库提供的绘图函数来画。
#include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); void setup() { display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.clearDisplay(); // 画矩形边框 display.drawRect(0, 0, 128, 64, SSD1306_WHITE); // 画填充矩形 display.fillRect(5, 5, 40, 20, SSD1306_WHITE); // 画圆形 display.drawCircle(90, 32, 20, SSD1306_WHITE); // 画填充圆形 display.fillCircle(45, 45, 12, SSD1306_WHITE); // 画直线 display.drawLine(65, 45, 120, 45, SSD1306_WHITE); // 画三角形 display.drawTriangle(70, 55, 85, 45, 100, 55, SSD1306_WHITE); // 显示文字 display.setTextSize(1); display.setCursor(8, 8); display.setTextColor(SSD1306_BLACK); // 在白色背景上用黑色字 display.println("GFX"); display.setTextColor(SSD1306_WHITE); display.setCursor(70, 58); display.println("Shapes"); display.display(); } void loop() {}常用绘图函数
| 函数 | 作用 |
|---|---|
drawPixel(x, y, color) | 画一个点 |
drawLine(x1, y1, x2, y2, color) | 画直线 |
drawRect(x, y, w, h, color) | 画矩形边框 |
fillRect(x, y, w, h, color) | 画填充矩形 |
drawCircle(x, y, r, color) | 画圆形边框 |
fillCircle(x, y, r, color) | 画填充圆形 |
drawTriangle(x1,y1, x2,y2, x3,y3, color) | 画三角形 |
drawRoundRect(x, y, w, h, radius, color) | 画圆角矩形 |
坐标系:左上角是 (0, 0),x 向右增大,y 向下增大。屏幕右下角是 (127, 63)。
八、综合项目:智能测距仪
把超声波和 OLED 结合起来,做一个独立的测距仪——不需要连电脑,开机就能实时显示距离。
#include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); const int trigPin = 9; const int echoPin = 10; void setup() { Serial.begin(9600); pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); // 初始化 OLED display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.clearDisplay(); // 开机画面 display.setTextSize(2); display.setCursor(10, 10); display.println("Smart"); display.setCursor(10, 35); display.println("Ranger"); display.display(); delay(1500); } void loop() { // 测量距离 float distance = measureDistance(); // 清屏 display.clearDisplay(); // 画边框 display.drawRect(0, 0, 128, 64, SSD1306_WHITE); // 标题 display.setTextSize(1); display.setCursor(30, 5); display.println("Distance Meter"); // 分隔线 display.drawLine(0, 16, 128, 16, SSD1306_WHITE); // 显示距离(大字体) display.setTextSize(3); display.setCursor(10, 25); display.print(distance, 1); // 保留1位小数 // 单位 display.setTextSize(2); display.setCursor(95, 30); display.print("cm"); // 状态指示 display.setTextSize(1); display.setCursor(5, 55); if (distance < 20) { display.println("[!!!] DANGER"); } else if (distance < 50) { display.println("[!!] WARNING"); } else if (distance < 100) { display.println("[!] CAUTION"); } else { display.println("[OK] SAFE"); } // 刷新显示 display.display(); delay(200); } float measureDistance() { digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); long duration = pulseIn(echoPin, HIGH, 30000); if (duration == 0) return 999.9; // 超时返回 999.9 return duration / 58.0; }效果
- 开机:显示 "Smart Ranger" 启动画面,1.5 秒后进入测距模式
- 测距模式:实时显示距离数值(大字体),下方显示安全状态
- 状态判断:
< 20cm:显示DANGER20~50cm:显示WARNING50~100cm:显示CAUTION> 100cm:显示SAFE
这就是一个完整的独立设备了——不需要电脑,不需要串口,开机即用。
九、OLED 显示技巧
自定义大数字
默认字体最大只有 5 号,如果想要更大的数字,可以手动绘制像素点阵,或者使用 U8g2 库(支持更多字体)。
显示中文
Adafruit SSD1306 默认不支持中文。要显示中文,需要:
- 准备中文字库(取模工具生成)
- 将字库数据存入 Arduino 的 Flash 存储
- 用
display.drawBitmap()绘制
中文显示比较复杂,因为 Arduino Uno 的 Flash 空间有限(32KB),放不下完整的中文字库。如果需要中文显示,建议使用 U8g2 库,它内置了多种中文字体。
动态刷新技巧
如果整个屏幕都刷新,会有闪烁感。可以只刷新变化的部分:
// 只清除数字区域,不重绘边框和标题 display.fillRect(10, 25, 80, 25, SSD1306_BLACK); display.setTextSize(3); display.setCursor(10, 25); display.print(distance, 1); display.display();十、常见问题排查
Q:OLED 什么都不显示?
- 检查 I2C 地址是否正确(用扫描程序确认)
- 检查 SCL 接 A5,SDA 接 A4
- 确认
display.begin()没有返回 false
Q:显示内容模糊或有残影?
- 每次刷新前先调用
display.clearDisplay() - 如果只更新部分内容,确保清除区域覆盖了旧内容
Q:屏幕亮度太低?
可以在setup()中调整对比度:
display.ssd1306_command(SSD1306_SETCONTRAST); display.ssd1306_command(255); // 0~255,255 最亮Q:刷新速度太慢?
减少display.display()的调用频率,或者只刷新变化的区域。
十一、本篇小结
回顾一下今天学到的核心知识:
- OLED 原理:自发光显示,高对比度,低功耗
- I2C 通信:只需 SCL + SDA 两根线,通过地址区分设备
- Adafruit SSD1306 库:初始化、清屏、设置光标、输出文字、刷新显示
- Adafruit GFX 库:画点、线、矩形、圆形、三角形等图形
- 缓冲区机制:先写入缓冲区,再一次性刷新到屏幕
- 综合项目:超声波 + OLED = 独立测距仪
OLED 显示屏让 Arduino 项目从"只能看串口"升级为"自带屏幕的独立设备"。下一篇,我们学习温湿度传感器 DHT11——让 Arduino 能够感知环境温度和湿度,做一个气象站。