|
Arduino OLED 显示屏完全教程:SSD1306 I2C 接线、绘图与传感器数据实时显示(2026)

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 地址通常是 0x3C0x3D。如果你不确定,运行这个扫描脚本:

#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 中:工具 → 管理库,安装以下两个库:

  1. Adafruit SSD1306 — OLED 屏幕驱动
  2. 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)

  1. PCtoLCD2002 或在线取模工具生成字模数据
  2. 将字模数据存入 Arduino 代码数组
  3. 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 显示屏的全部核心知识:

  1. SSD1306 硬件选型与 I2C 接线
  2. Adafruit SSD1306 库的安装与基本使用
  3. 文字、图形绘制方法
  4. 传感器数据实时显示实战
  5. 中文字符显示方案
  6. 常见问题排查与性能优化

SSD1306 + Arduino 是嵌入式入门的最佳组合——便宜、简单、资料多。掌握了这块小屏幕,你的项目立刻从”串口调试”升级到”有界面的产品”。

下一步推荐:尝试将 OLED 与 ESP32 WiFi 项目结合,做一个带本地显示屏的物联网终端。