Appearance
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引脚 | 说明 |
|---|---|---|
| GND | GND | 电源地 |
| VCC | 5V (或3.3V) | 电源正极,请根据模块规格选择 |
| SCL | A5 | I2C时钟线 |
| SDA | A4 | I2C数据线 |
注意:不同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);
}核心逻辑
- 初始化:通过
display.begin()初始化屏幕,使用setRotation()设置方向。 - 静态显示:绘制文本、线条、矩形后调用
display()刷新。 - 动态刷新:在
loop()中执行“清屏 → 绘制 → 刷新”循环,实现动态内容更新。
常见问题
- 屏幕不亮:检查 VCC/GND 是否接反。
- 初始化失败:尝试将
SCREEN_ADDRESS从0x3C改为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_utf8 | 12 | 6 | 12 | 20 | 约 500 | Mega / ESP32 / ESP8266 |
u8g2_font_wqy12_mf_utf8 | 12 | 6 | 12 | 20 | 约 500 | 同上 |
u8g2_font_wqy16_t_utf8 | 16 | 8 | 16 | 30 | 约 800 | Mega / ESP32 |
u8g2_font_wqy16_mf_utf8 | 16 | 8 | 16 | 30 | 约 800 | 同上 |
u8g2_font_wqy20_t_utf8 | 20 | 10 | 20 | 40 | 约 1200 | ESP32 / 资源充足开发板 |
u8g2_font_wqy20_mf_utf8 | 20 | 10 | 20 | 40 | 约 1200 | 同上 |
说明:
- wqy12 / wqy16 / wqy20:文泉驿字体,字号分别为 12/16/20 点。
- _t:精简版(部分字符可能缺失)。
- _mf:完整版(支持更多汉字,文件更大)。
优化建议
- 资源受限开发板(如 Uno):使用
u8g2_font_wqy12_t_utf8或更小字体,避免同时加载多个字体。 - 资源充足开发板(如 ESP32):可加载
_mf完整版字体,使用分页模式显示长文本。 - 内存不足排查:若编译报错 "Out of memory",更换更小字体或升级开发板。
- 中文乱码排查:确认已调用
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 | 与原图一致 | 等比缩放会导致模糊,建议原尺寸 |
| Background | White / Black | 让图标主体呈黑色 |
| Draw mode | Black pixels only | 仅转换黑色像素为 1 |
| Invert image | 按需勾选 | 图标显示反色时勾选 |
| Brightness threshold | 128 | 默认即可,仅对灰阶图片有效 |
| Output format | Arduino 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:生成代码
- 点击 "Generate code"
- 复制下方文本框中的数组代码
- 粘贴到 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 Converter | Win/macOS/Linux | 功能强、支持批量、参数精细 | 需安装、界面老旧 | 整套 UI 图标包 |
| PCtoLCD2017 | Windows | 国内老牌、资料多 | 仅 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 头。建议在代码中手动重新输入“温度”、“光照”等字样以排除编码干扰。
- 字库覆盖范围测试:若特定汉字不显示,可以尝试在代码中打印一些生僻字(如“焱”、“淼”)来测试字库的实际覆盖范围,确认是否因为漏字导致显示失败。
您好,您在终端里看到的 bdf2gdfont、bdftopcf 等命令,是系统自带的字体处理工具,并不能用来为 U8g2 库生成字库。
您需要的正确工具是 bdfconv,它需要从 U8g2 的源代码中获取并自行编译,或使用我们为你准备的现成可执行文件。
您好,您在终端里看到的 bdf2gdfont、bdftopcf 等命令,是系统自带的字体处理工具,并不能用来为 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.c5. 生成的文件
| 文件 | 路径 | 用途 |
|---|---|---|
| 字库代码文件 | ./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 等资源受限平台显示中文的推荐实践。