Skip to content

Arduino硬件交互实战:OLED显示

一、 OLED显示模块简介

OLED(Organic Light-Emitting Diode,有机发光二极管)显示模块因其自发光、高对比度、低功耗和轻薄的特点,在Arduino项目中被广泛应用。常见的规格有0.96英寸和1.3英寸,分辨率通常为128x64或128x32。

主要特性

  • 自发光:无需背光源,黑色显示时像素点完全关闭,对比度极高。
  • 低功耗:仅在显示亮色时消耗电流,适合电池供电项目。
  • 宽视角:几乎可以从任何角度清晰观看。
  • 接口灵活:支持I2C和SPI两种主流通信协议,I2C接线更简单。

二、 硬件连接

以最常见的0.96寸I2C接口OLED为例,其与Arduino UNO的连接方式如下:

OLED引脚Arduino引脚说明
GNDGND电源地
VCC5V (或3.3V)电源正极,请根据模块规格选择
SCLA5I2C时钟线
SDAA4I2C数据线

注意:不同Arduino板卡的I2C引脚可能不同,例如Arduino Mega的SCL为21,SDA为20。

三、SSD1306 OLED 显示基础

SSD1306 是一款常见的单色 OLED 驱动芯片,支持 I2C 或 SPI 接口。在 Arduino 开发中,主要有两套主流库:

  • Adafruit_SSD1306:官方推荐,API 简洁,适合快速原型开发。
  • u8g2:功能更强大,支持更多屏幕型号和字体,内存优化更好,适合复杂项目。

四、Adafruit 库实战

完整代码示例

cpp
#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);
  
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306初始化失败,检查接线和地址。"));
    while (true);
  }

  Serial.println(F("SSD1306初始化成功!"));
  display.setRotation(0);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);

  display.println(F("Hello, World!"));
  display.println(F("SSD1306 OLED Test"));
  display.drawLine(0, 16, SCREEN_WIDTH, 16, SSD1306_WHITE);

  int rectSize = 20;
  int rectX = (SCREEN_WIDTH - rectSize) / 2;
  int rectY = (SCREEN_HEIGHT - rectSize) / 2;
  display.drawRect(rectX, rectY, rectSize, rectSize, SSD1306_WHITE);

  display.display();
  delay(2000);
}

void loop() {
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(5, 25);
  display.print("Time:");
  display.setCursor(5, 45);
  display.print(millis());
  display.display();
  delay(100);
}

核心逻辑

  1. 初始化:通过 display.begin() 初始化屏幕,使用 setRotation() 设置方向。
  2. 静态显示:绘制文本、线条、矩形后调用 display() 刷新。
  3. 动态刷新:在 loop() 中执行“清屏 → 绘制 → 刷新”循环,实现动态内容更新。

常见问题

  • 屏幕不亮:检查 VCC/GND 是否接反。
  • 初始化失败:尝试将 SCREEN_ADDRESS0x3C 改为 0x3D
  • 显示方向错误:调整 setRotation() 参数(0-3)。

五、u8g2 库实战

完整代码示例

cpp
#include <U8g2lib.h>
#include <Wire.h>

U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);

void setup() {
  u8g2.begin();
  u8g2.enableUTF8Print(); // 启用UTF-8支持
}

void loop() {
  u8g2.clearBuffer();

  u8g2.setFont(u8g2_font_ncenB08_tr);
  u8g2.drawStr(0, 10, "Hello, U8g2!");

  u8g2.setFont(u8g2_font_wqy12_t_gb2312a);
  u8g2.drawUTF8(0, 25, "这是一个中文示例");

  u8g2.drawLine(0, 30, 127, 30);
  u8g2.drawBox(10, 40, 20, 10);
  u8g2.drawCircle(50, 50, 8, U8G2_DRAW_ALL);

  u8g2.sendBuffer();
  delay(2000);
}

核心 API 速查表

初始化与控制

函数说明
U8G2_... u8g2(...)构造函数,根据屏幕型号和连接方式选择
u8g2.begin()初始化屏幕
u8g2.clearBuffer()清空内存缓冲区
u8g2.sendBuffer()刷新屏幕显示
u8g2.setDisplayRotation(r)设置旋转方向(R0/R1/R2/R3)

文本显示

函数说明
u8g2.setFont(*font)设置字体
u8g2.drawStr(x, y, "text")绘制ASCII字符串
u8g2.drawUTF8(x, y, "中文")绘制UTF8文本
u8g2.enableUTF8Print()启用UTF8支持
u8g2.getUTF8Width("text")获取UTF8字符串像素宽度

图形绘制

函数说明
u8g2.drawPixel(x, y)绘制像素点
u8g2.drawLine(x1, y1, x2, y2)绘制直线
u8g2.drawBox(x, y, w, h)绘制实心矩形
u8g2.drawFrame(x, y, w, h)绘制空心矩形
u8g2.drawCircle(x, y, r, option)绘制圆形
u8g2.drawEllipse(x, y, rx, ry, option)绘制椭圆

附录一、 u8g2 中文字体资源对比

在 u8g2 库中,中文字体以 UTF-8 编码支持,需通过 u8g2.drawUTF8(x, y, "中文") 绘制。以下为常用中文字体参数对比:

字体名称字号字符宽度 (px)字符高度 (px)闪存占用 (KB)RAM 占用 (B)推荐开发板
u8g2_font_wqy12_t_utf81261220约 500Mega / ESP32 / ESP8266
u8g2_font_wqy12_mf_utf81261220约 500同上
u8g2_font_wqy16_t_utf81681630约 800Mega / ESP32
u8g2_font_wqy16_mf_utf81681630约 800同上
u8g2_font_wqy20_t_utf820102040约 1200ESP32 / 资源充足开发板
u8g2_font_wqy20_mf_utf820102040约 1200同上

说明:

  • wqy12 / wqy16 / wqy20:文泉驿字体,字号分别为 12/16/20 点。
  • _t:精简版(部分字符可能缺失)。
  • _mf:完整版(支持更多汉字,文件更大)。

优化建议

  1. 资源受限开发板(如 Uno):使用 u8g2_font_wqy12_t_utf8 或更小字体,避免同时加载多个字体。
  2. 资源充足开发板(如 ESP32):可加载 _mf 完整版字体,使用分页模式显示长文本。
  3. 内存不足排查:若编译报错 "Out of memory",更换更小字体或升级开发板。
  4. 中文乱码排查:确认已调用 u8g2.enableUTF8Print(),检查字体名称是否正确。

掌握执行器控制与OLED显示,意味着你的Arduino项目从“数据展示”迈向了“物理交互+人机反馈”的完整闭环。建议先从低压、小功率设备开始调试,确认逻辑无误后再接入大功率负载,并始终注意高压安全。

附录二、 image2cpp 在线取模工具使用指南

工具名称:image2cpp
在线地址https://javl.github.io/image2cpp/
适用场景:OLED 图标、Logo、二维码、简单图形生成 C 数组
目标库:U8g2 / Adafruit GFX / SSD1306Ascii


一、完整工作流

Step 1:准备单色图片

项目要求
格式BMP 最佳(PNG/JPG 也可,但二值化不稳定)
尺寸≤ 屏幕分辨率(建议 16×16 / 32×32 / 48×48)
颜色纯黑白二值(无灰阶)
制作工具Windows 画图 → 属性设为 N×N 像素 → 800% 缩放 → 铅笔工具绘制

Step 2:上传图片

  • 打开网站 → 点击 "Choose file" 上传图片
  • 预览区确认:黑 = 不亮,白 = 亮(SSD1306 逻辑:1=点亮)

Step 3:参数设置(⚠️ 关键)

参数推荐值说明
Canvas size与原图一致等比缩放会导致模糊,建议原尺寸
BackgroundWhite / Black让图标主体呈黑色
Draw modeBlack pixels only仅转换黑色像素为 1
Invert image按需勾选图标显示反色时勾选
Brightness threshold128默认即可,仅对灰阶图片有效
Output formatArduino code (PROGMEM)直接生成可用代码
Code output (Layout)Vertical - 1 byte per column (LSB first)SSD1306 + U8g2 drawXBM 标准配置

🚨 Layout 是重灾区

  • SSD1306 的 GDDRAM 是列优先,LSB 在上
  • 选错会导致图标躺倒、拉伸、乱码
  • 认准:Vertical - 1 byte per column (LSB first)

Step 4:生成代码

  1. 点击 "Generate code"
  2. 复制下方文本框中的数组代码
  3. 粘贴到 Arduino 项目中

二、代码示例

生成的数组示例

cpp
// 'wifi_icon', 16x16px
const unsigned char wifi_icon_16x16 [] PROGMEM = {
    0x00, 0x00, 0x0e, 0x70, 0x18, 0x18, 0x20, 0x04,
    0x40, 0x02, 0x40, 0x02, 0x41, 0x82, 0x42, 0x42,
    0x44, 0x22, 0x48, 0x12, 0x48, 0x12, 0x50, 0x0a,
    0x60, 0x06, 0x60, 0x06, 0x40, 0x02, 0x00, 0x00
};

U8g2 显示代码

cpp
#include <U8g2lib.h>

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

void setup() {
    u8g2.begin();
}

void loop() {
    u8g2.clearBuffer();
    u8g2.drawXBM(0, 0, 16, 16, wifi_icon_16x16);
    u8g2.sendBuffer();
    delay(1000);
}

参数说明drawXBM(x, y, width, height, array)

  • x, y:图标左上角坐标
  • width, height:必须与 image2cpp 中的 Canvas size 一致
  • array:PROGMEM 数组名

三、常见问题(FAQ)

问题现象原因解决方案
图标躺倒 90°Layout 选错改为 Vertical - 1 byte per column (LSB first)
图标反色取模逻辑相反image2cpp 勾选 Invert image 或代码用 setDrawColor(0)
图标被压扁尺寸不匹配Canvas size 与 drawXBM 参数保持一致
低位缺失MSB/LSB 不匹配尝试切换 LSB/MSB 选项
编译报错 PROGMEM缺少头文件添加 #include <Arduino.h>

四、工具对比

工具平台优点缺点适用场景
image2cpp浏览器免安装、界面直观、直接出 PROGMEM单张处理、无法批量单个图标、快速验证
LCD Image ConverterWin/macOS/Linux功能强、支持批量、参数精细需安装、界面老旧整套 UI 图标包
PCtoLCD2017Windows国内老牌、资料多仅 Windows、停止更新传统 Arduino 项目

五、进阶技巧

1. 多图标合成精灵图

  • 将多个同尺寸图标横向拼接(如 4 个 16×16 → 64×16)
  • 一次取模生成单个数组
  • 显示时通过计算偏移量截取不同部分

2. 内存优化

  • 使用 PROGMEM 强制存储到 Flash
  • 大数组拆分为多个小数组
  • 考虑动态生成简单图形而非存储位图

3. 反色显示

cpp
// 反色显示图标(黑底白图)
u8g2.setDrawColor(0);
u8g2.drawXBM(x, y, w, h, icon);
u8g2.setDrawColor(1);  // 恢复默认颜色

六、最佳实践总结

BMP 格式 + 原尺寸 + Vertical LSB first
✅ 图标尺寸控制在 16×16 / 32×32
✅ 使用 PROGMEM 节省 RAM
✅ U8g2 全缓冲模式直接 drawXBM
❌ 避免 JPG/PNG 灰阶图片
❌ 不要随意缩放单色位图


适用硬件:Arduino UNO R4 WiFi / R3 + SSD1306 128×64 OLED

这份指南涵盖了 image2cpp 的核心用法。接下来,你是否想了解如何使用 bdfconv 工具 来自制或修改 U8g2 的中文字库,以实现极致的 Flash 空间优化?


附录三、 Arduino UNO R4 WiFi 精简字库生成指南

前言:为何需要精简字库

在 Arduino UNO R4 WiFi 上开发 U8g2 显示应用时,经常会遇到内存紧张的问题。完整的中文字库体积庞大,如果直接加载,很容易超出微控制器的 SRAM 或 Flash 限制。通过 U8g2 官方工具 bdfconv 按需提取汉字(如“温度”、“光照”),可生成精简的自定义字库头文件,降低内存占用并提升效率。

具体操作步骤

1. 下载 bdfconv 工具

请按以下步骤操作:

bash
# 1. 克隆 U8g2 仓库
git clone https://github.com/olikraus/u8g2.git

# 2. 进入工具目录
cd u8g2/tools/font/bdfconv

# 3. 编译工具
make

编译成功后,您就可以在当前目录下使用 ./bdfconv 命令了。

2. 准备 BDF 字体文件

你需要一个包含所需汉字的 BDF 格式字体文件。可以从开源字体项目(如 Unifont)中提取,或者直接使用包含完整字符集的 BDF 文件。确保该文件中包含你计划显示的所有汉字(例如:“温”、“度”、“光”、“照”)。

3. 运行命令生成头文件

打开命令行终端,进入 bdfconv 所在目录,执行以下命令生成自定义字库头文件:

bash
bdfconv -v -f 1 -M "温度光照" -o my_font.h -n myFont unifont.bdf

参数说明:

  • -v:开启详细输出模式,便于查看转换过程。
  • -f 1:指定字体格式(1 表示 U8g2 格式)。
  • -M "温度光照":指定需要提取的汉字列表(双引号包裹)。
  • -o my_font.h:指定输出的头文件名称。
  • -n myFont:指定在代码中使用的字体变量名。
  • unifont.bdf:输入的 BDF 字体文件路径。

4. 代码集成

将生成的 my_font.h 文件放入你的 Arduino 项目文件夹中,并在代码中引入并使用:

cpp
#include "my_font.h"

// 在 U8g2 初始化或设置字体时使用新生成的字体名
u8g2.setFont(myFont);
u8g2.setCursor(10, 20);
u8g2.print("温度: 25℃");

额外检查项

如果在集成后汉字仍然无法正常显示,请依次排查以下问题:

  • UTF-8 打印启用:确保在代码中已调用 u8g2.enableUTF8Print();,这是正确解析中文字符串的前提。
  • 字符串编码格式:确认代码中的字符串编码为 UTF-8。Arduino IDE 默认支持 UTF-8,但如果文本是从网页或其他编辑器复制而来,可能会带有 BOM 头。建议在代码中手动重新输入“温度”、“光照”等字样以排除编码干扰。
  • 字库覆盖范围测试:若特定汉字不显示,可以尝试在代码中打印一些生僻字(如“焱”、“淼”)来测试字库的实际覆盖范围,确认是否因为漏字导致显示失败。

您好,您在终端里看到的 bdf2gdfontbdftopcf 等命令,是系统自带的字体处理工具,并不能用来为 U8g2 库生成字库。

您需要的正确工具是 bdfconv,它需要从 U8g2 的源代码中获取并自行编译,或使用我们为你准备的现成可执行文件。


您好,您在终端里看到的 bdf2gdfontbdftopcf 等命令,是系统自带的字体处理工具,并不能用来为 U8g2 库生成字库。

您需要的正确工具是 bdfconv,它需要从 U8g2 的源代码中获取并自行编译,或使用我们为你准备的现成可执行文件。


附录四、 Easy-u8g2-font-generate-tools 精简中文字库生成指南

1. 背景与原理

在 Arduino UNO(Flash 32KB)等存储受限的嵌入式平台上,使用 U8g2 完整中文字库(如 u8g2_font_wqy16_t_chinese3,约 148KB)会导致存储空间不足。精简字库的核心思想是 “按需提取” :只将项目中实际用到的中文字符生成字库,从而将字库体积从上百 KB 缩减到几 KB。

本项目使用 Easy-u8g2-font-generate-tools 工具链,自动从源代码中提取中文字符并生成 U8g2 兼容的 .c 字库文件。


2. 环境准备

系统环境:Ubuntu 22.04 / 20.04

依赖安装

bash
# 克隆工具仓库
git clone https://github.com/createskyblue/Easy-u8g2-font-generate-tools.git
cd Easy-u8g2-font-generate-tools

# 安装 Python3 和必要依赖
sudo apt update
sudo apt install python3 python3-pip

# 安装 otf2bdf(系统包)
sudo apt install otf2bdf

# 注意:此工具自带 bdfconv 可执行文件,无需单独安装

文件结构(工具自带的文件):

Easy-u8g2-font-generate-tools/
├── main.py              # 主脚本
├── config.json          # 配置文件(需用户创建)
├── bdfconv              # bdfconv 可执行文件(Linux 版)
├── otf2bdf              # otf2bdf 可执行文件(Linux 版)
├── example/             # 示例目录
│   ├── example.c        # 示例源码(用于提取字符)
│   └── font/
│       └── MapleMono-NF-CN-Light.ttf  # 示例中文字体
└── ...

3. 配置文件 (config.json)

在项目根目录创建或编辑 config.json 文件:

json
{
  "font_name": "myFont",                              // 生成字库的名称
  "font_path": "./example/font/MapleMono-NF-CN-Light.ttf", // 中文字体文件路径
  "font_dpi": 72,                                     // 字体 DPI
  "font_size_px": 16,                                 // 字体大小(像素)
  "font_spacing_percent": 100,                        // 字体间距(%)
  "filter_ascii": true,                               // 是否过滤 ASCII 字符
  "map_include_ascii": false,                         // 映射文件是否包含 ASCII
  "output_dir": "./example",                          // 输出目录
  "file_paths": [                                     // 源代码文件路径(支持通配符)
    "./example/*.c"
  ]
}

关键参数说明

参数说明
font_path必须是一个包含中文字符的 TrueType 字体文件(.ttf
font_size_px生成字库的点阵大小,通常 12-24px
filter_ascii设为 true 可只生成中文,进一步缩减体积
file_paths工具会扫描这些文件,自动提取所有中文字符

4. 执行生成

bash
# 进入工具目录
cd ~/workspace/oled/Easy-u8g2-font-generate-tools

# 确保可执行文件有权限
chmod +x bdfconv otf2bdf

# 执行生成
python3 main.py config.json

成功输出示例

=============U8g2字库生成器 V3.0=============
加载配置文件: config.json
  字体名称: myFont
  字体大小: 16px
找到 1 个文件:
  - ./example/example.c
提取到的中文字符(共 51 个唯一字符):
  {'电', '校', '关', '池', '更', ...}
> ./otf2bdf -v -r 72 -p 16.0 -o ./example/bdf/...
> ./bdfconv -v -b 0 -f 1 ...
RLE Compress: Font size 1792
 [操作完成] 
生成的文件:
  - ./example/map/myFont.map
  - ./example/bdf/MapleMono-NF-CN-Light_16.bdf
  - ./example/code/myFont.c

5. 生成的文件

文件路径用途
字库代码文件./example/code/myFont.c复制到 Arduino 项目中使用
映射文件./example/map/myFont.map字符映射表(参考用)
BDF 中间文件./example/bdf/*.bdf中间格式(可删除)

字库大小:约 1.7KB(51 个字符),相比完整中文字库(148KB)节省了 98% 的存储空间。


6. 在 Arduino 项目中使用

步骤 1:将 myFont.c 复制到 Arduino 项目文件夹中。

步骤 2:在 ino 文件中声明并调用:

cpp
#include <U8g2lib.h>
#include <Wire.h>

// 声明外部字体
extern const uint8_t myFont_16[];

U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

void setup() {
  u8g2.begin();
  u8g2.enableUTF8Print();  // 启用 UTF-8 支持
}

void loop() {
  u8g2.firstPage();
  do {
    u8g2.setFont(myFont_16);  // 使用自定义精简字库
    u8g2.setCursor(0, 20);
    u8g2.print("你好世界");
    
    u8g2.setCursor(0, 45);
    u8g2.print("Arduino UNO");
  } while (u8g2.nextPage());
  
  delay(2000);
}

注意事项

  • 字体名称规则:{font_name}_{font_size},如 myFont_16
  • 必须调用 u8g2.enableUTF8Print() 启用 UTF-8 支持
  • 代码文件需保存为 UTF-8 编码

7. 添加新字符

如需增加新字符,在 file_paths 指定的源文件中添加包含该字符的字符串或注释,然后重新运行:

bash
python3 main.py config.json

工具会自动提取新增字符并重新生成字库。


8. 字库选择参考

场景推荐字体说明
少量常用汉字(10-50 字)u8g2_font_wqy12_t_chinese1 或自定义精简字库节省内存
大量中文字符u8g2_font_wqy16_t_chinese3覆盖最全,但占用空间较大
自定义项目Easy-u8g2-font-generate-tools 生成按需生成,体积最小

9. 常见问题

问题解决方法
otf2bdf: not found执行 sudo apt install otf2bdf 或使用工具自带的 ./otf2bdf
bdfconv: not found确保 bdfconv 有执行权限:chmod +x bdfconv
生成的字体显示为方块检查代码中是否调用 u8g2.enableUTF8Print()
部分字符未显示确认该字符在源文件中存在,重新运行生成脚本
编译时提示 multiple definition检查是否有多个字库文件,只保留一个

10. 总结

项目完整中文字库精简字库(本文方案)
字符数6763(GB2312)按需提取(51 个)
体积~148KB~1.7KB
适用场景通用显示定制化项目
Arduino UNO 兼容性❌ Flash 不足✅ 完全兼容

通过 Easy-u8g2-font-generate-tools,可以轻松实现 “按需生成、最小体积” 的中文字库方案,是 Arduino 等资源受限平台显示中文的推荐实践。