Arduino Arduino OLED 显示屏完全教程:SSD1306 I2C 接线、绘图与传感器数据实时显示(2026)
为什么你需要学会用 OLED 显示屏
在嵌入式开发中,给项目加一块小屏幕能解决 80% 的调试痛点。你不再需要串口打印数据、打开串口监视器反复看——传感器读数直接显示在屏幕上,一目了然。
而 SSD1306 是 Arduino 生态中最流行的 OLED 屏幕驱动芯片,没有之一。它的优势很明显:
| 规格 | SSD1306 OLED |
|---|---|
| 分辨率 | 128×64 或 128×32 |
| 尺寸 | 0.96 英寸(主流) |
| 通信协议 | I2C 或 SPI |
| 工作电压 | 3.3V-5V(兼容 Arduino) |
| 功耗 | 约 20mA |
| 价格 | ¥8-15 |
硬件清单
| 组件 | 型号 | 数量 | 预估价格 |
|---|---|---|---|
| Arduino 开发板 | Uno/Nano/Mega 均可 | 1 | ¥15-25 |
| OLED 显示屏 | 0.96” SSD1306 I2C 版 | 1 | ¥8-15 |
| 面包板 | 400 孔 | 1 | ¥5-8 |
| 杜邦线 | 公对母 | 4 根 | ¥2 |
总计 ¥30-50。
I2C 接线方法
SSD1306 OLED 用 I2C 通信只需要 4 根线:
Arduino UNO/Nano SSD1306 OLED
------------- ------------
5V (或 3.3V) ────────── VCC
GND ────────────── GND
A4 (SDA) ────────────── SDA
A5 (SCL) ────────────── SCL
Arduino Mega:
SDA → Pin 20
SCL → Pin 21
Arduino Nano:
SDA → A4
SCL → A5
ESP32(如果你用 ESP32 驱动 OLED):
SDA → GPIO21 (默认)
SCL → GPIO22 (默认)
注意:大部分 SSD1306 模块内置上拉电阻,不需要额外接 4.7kΩ 上拉电阻。如果屏幕不工作,再考虑加上拉。
I2C 地址确认
SSD1306 的 I2C 地址通常是 0x3C 或 0x3D。如果你不确定,运行这个扫描脚本:
#include <Wire.h>
void setup() {
Serial.begin(9600);
Wire.begin();
Serial.println("I2C 地址扫描中...");
for (byte addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
if (Wire.endTransmission() == 0) {
Serial.print("找到设备地址: 0x");
Serial.println(addr, HEX);
}
}
Serial.println("扫描完成");
}
void loop() {}
环境配置
安装 Arduino 库
在 Arduino IDE 中:工具 → 管理库,安装以下两个库:
- Adafruit SSD1306 — OLED 屏幕驱动
- Adafruit GFX Library — 图形绘制基础库
验证屏幕工作
先用官方示例测试:
文件 → 示例 → Adafruit SSD1306 → ssd1306_128x64_i2c
上传后你应该能看到屏幕上出现 Adafruit logo 和一系列图形演示。
如果屏幕不亮,检查:
- 接线是否正确(SDA/SCL 不要接反)
- I2C 地址是否正确(0x3C vs 0x3D)
- 在代码开头确认:
#define SCREEN_ADDRESS 0x3C
核心代码详解
初始化与基本设置
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void setup() {
Serial.begin(9600);
// 初始化 OLED
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println("SSD1306 初始化失败!");
for (;;); // 卡在这里
}
display.clearDisplay(); // 清屏
display.setTextSize(1); // 字体大小 1(默认 6×8 像素)
display.setTextColor(SSD1306_WHITE); // 白色文字
display.setCursor(0, 0); // 光标位置 (x=0, y=0)
display.println("Hello!");
display.display(); // 刷新屏幕(必须调用)
}
void loop() {}
关键 API 说明:
| 函数 | 作用 |
|---|---|
display.begin() | 初始化屏幕 |
display.clearDisplay() | 清空显示缓冲区 |
display.display() | 将缓冲区内容刷新到屏幕 |
display.setTextSize(n) | 设置字体大小(n=1,2,3…) |
display.setCursor(x,y) | 设置文字起始位置 |
display.println(text) | 输出文字(自动换行到下一行) |
display.print(text) | 输出文字(不换行) |
重要:所有绘制操作(文字、图形)都先写入内存缓冲区,只有调用 display.display() 才会真正刷新到屏幕。这避免了频繁刷新造成的闪烁。
显示温湿度传感器数据
接好 DHT22 传感器后(参考 ESP32 温湿度监控教程),用以下代码将数据显示到 OLED:
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <DHT.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define SCREEN_ADDRESS 0x3C
#define DHTPIN 2
#define DHTTYPE DHT22
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
DHT dht(DHTPIN, DHTTYPE);
void setup() {
Serial.begin(9600);
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
for (;;);
}
dht.begin();
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("DHT22 + OLED");
display.println("系统启动中...");
display.display();
delay(2000);
}
void loop() {
float temp = dht.readTemperature();
float hum = dht.readHumidity();
if (isnan(temp) || isnan(hum)) {
return;
}
// 清屏并重绘
display.clearDisplay();
// 标题
display.setTextSize(1);
display.setCursor(0, 0);
display.println("== DHT22 Monitor ==");
display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
// 温度(大字体)
display.setTextSize(2);
display.setCursor(0, 16);
display.print(temp, 1);
display.setTextSize(1);
display.print(" C");
// 湿度(大字体)
display.setTextSize(2);
display.setCursor(0, 40);
display.print(hum, 1);
display.setTextSize(1);
display.print(" %");
// 刷新屏幕
display.display();
delay(2000); // 每 2 秒刷新一次
}
绘制图形
SSD1306 支持点、线、圆、矩形等基础图形:
// 画点
display.drawPixel(x, y, SSD1306_WHITE);
// 画线
display.drawLine(x0, y0, x1, y1, SSD1306_WHITE);
// 画矩形(空心)
display.drawRect(x, y, width, height, SSD1306_WHITE);
// 画矩形(实心)
display.fillRect(x, y, width, height, SSD1306_WHITE);
// 画圆(空心)
display.drawCircle(cx, cy, radius, SSD1306_WHITE);
// 画圆(实心)
display.fillCircle(cx, cy, radius, SSD1306_WHITE);
// 画圆角矩形
display.drawRoundRect(x, y, w, h, cornerRadius, SSD1306_WHITE);
// 画三角形
display.drawTriangle(x0, y0, x1, y1, x2, y2, SSD1306_WHITE);
// 画进度条
void drawProgressBar(int x, int y, int w, int h, int percent) {
display.drawRect(x, y, w, h, SSD1306_WHITE);
int fillW = map(percent, 0, 100, 0, w - 2);
display.fillRect(x + 1, y + 1, fillW, h - 2, SSD1306_WHITE);
}
// 画简单图表(折线图)
void drawLineChart(int x, int y, int w, int h, int* data, int len, int maxVal) {
display.drawRect(x, y, w, h, SSD1306_WHITE);
for (int i = 0; i < len - 1 && i < w; i++) {
int y1 = y + h - map(data[i], 0, maxVal, 0, h);
int y2 = y + h - map(data[i+1], 0, maxVal, 0, h);
display.drawLine(x + i, y1, x + i + 1, y2, SSD1306_WHITE);
}
}
自定义中文字符显示
默认情况下,Adafruit GFX 库只支持 ASCII 字符。如果需要显示中文,有几种方案:
方案 1:字库取模法(推荐,无需外挂 Flash)
- 用 PCtoLCD2002 或在线取模工具生成字模数据
- 将字模数据存入 Arduino 代码数组
- 用
display.drawBitmap()绘制
// "温" 字的 16×16 字模(示例)
static const uint8_t PROGMEM font_wen[] = {
0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
// 显示在指定位置
display.drawBitmap(0, 0, font_wen, 16, 16, SSD1306_WHITE);
方案 2:使用 U8g2 库替代
U8g2 库内置中文字库支持,但代码体积更大:
#include <U8g2lib.h>
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);
void setup() {
u8g2.begin();
u8g2.setFont(u8g2_font_wqy12_t_chinese3); // 中文字体
u8g2.firstPage();
do {
u8g2.drawStr(0, 20, "温度: 25.3°C");
} while (u8g2.nextPage());
}
void loop() {}
常见问题
1. 屏幕白屏或全黑
- I2C 地址错误:用扫描脚本确认是 0x3C 还是 0x3D
- 初始化失败:检查
display.begin()返回值
2. 显示花屏/乱码
- I2C 线太长或接触不良:杜邦线控制在 20cm 以内
- 供电不足:尝试从 3.3V 改到 5V(或反过来)
3. 刷新太慢
display.display()本身需要几毫秒,不要每秒调用太多次- 如果只需要更新局部数字,用
display.fillRect()先擦除旧数字区域
4. 内存不够(Arduino Uno)
- SSD1306 缓冲区占用 128×64/8 = 1024 字节
- Uno 只有 2KB SRAM,加上其他变量容易溢出
- 解决方案:用局部刷新或换用 Nano/Mega
5. 中文显示不了
- Adafruit GFX 默认只有 ASCII 字库
- 用 U8g2 库或字库取模法解决(见上文)
性能优化技巧
局部刷新:如果只更新数字部分,不需要清屏重绘:
// 只擦除数字区域并重写
display.fillRect(0, 16, 80, 16, SSD1306_BLACK);
display.setTextSize(2);
display.setCursor(0, 16);
display.print(temp, 1);
display.display();
双缓冲:如果你用 ESP32 或内存较大的板子,可以开双缓冲避免撕裂:
// ESP32 双缓冲初始化
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
与其他传感器的组合
你可以用同样的模式接入任何传感器:
| 传感器 | 数据 | 显示内容 |
|---|---|---|
| DHT22 | 温湿度 | 温度值 + 湿度值 |
| BMP280 | 气压/温度 | 气压值 + 天气预报图标 |
| BH1750 | 光照度 | 流明值 + 亮度条 |
| MQ-135 | 空气质量 | CO2 浓度 + 警告标志 |
| 超声波 HC-SR04 | 距离 | 距离值 + 标尺图 |
接线和代码逻辑完全一样:读取传感器数据 → 格式化字符串 → display.print() 显示。
总结
本文覆盖了 Arduino OLED 显示屏的全部核心知识:
- SSD1306 硬件选型与 I2C 接线
- Adafruit SSD1306 库的安装与基本使用
- 文字、图形绘制方法
- 传感器数据实时显示实战
- 中文字符显示方案
- 常见问题排查与性能优化
SSD1306 + Arduino 是嵌入式入门的最佳组合——便宜、简单、资料多。掌握了这块小屏幕,你的项目立刻从”串口调试”升级到”有界面的产品”。
下一步推荐:尝试将 OLED 与 ESP32 WiFi 项目结合,做一个带本地显示屏的物联网终端。