U8g2lib 是一个用于单色图形显示器 (monochrome graphics displays) 的开源嵌入式图形库。它支持各种 OLED 和 LCD 显示器,例如经典的 128x64 SSD1306 OLED 显示器。U8g2lib 以其广泛的硬件支持、丰富的字体集、高效的内存使用以及强大的图形绘制功能而闻名。它是 U8glib 的继任者,相比 U8glib,U8g2lib 在内存效率和功能上进行了优化和扩展,支持帧缓冲模式 (full buffer) 和页缓冲模式 (page buffer)。

核心思想:提供一个统一的 API 接口,驱动各种单色图形显示器,并提供一套完整的图形绘制和字体渲染功能,同时兼顾嵌入式设备的资源限制。 开发者无需关心底层显示器的驱动细节,专注于界面设计。


一、为什么需要 U8g2lib?嵌入式显示器的痛点

在嵌入式系统中集成单色图形显示器时,开发者常常面临以下挑战:

  1. 硬件驱动复杂:不同的显示器控制器 (如 SSD1306, SH1106, ST7920) 有不同的初始化序列、命令集和数据传输协议(SPI, I2C, 并行)。手动编写驱动非常耗时且容易出错。
  2. 字体渲染困难:在低分辨率单色屏幕上显示文字需要专业的字体处理,包括位图字体存储、字符渲染算法和抗锯齿处理。
  3. 图形绘制基础功能缺失:画点、线、矩形、圆等基本图形功能需要从零开始实现。
  4. 内存资源限制:嵌入式微控制器(如 Arduino UNO 的 ATmega328P, ESP32/ESP8266)通常内存有限。全屏帧缓冲可能会消耗大量 RAM。
  5. 缺乏统一 API:每个显示器可能需要不同的库,导致项目更换显示器时需要大量代码修改。

U8g2lib 通过提供一个统一、强大且内存优化的解决方案,解决了这些痛点。

二、U8g2lib 的主要特性

2.1 广泛的硬件支持

  • 控制器:支持 SSD1306, SH1106, ST7920, PCD8544 (Nokia 5110), ILI9341 (作为单色模式) 等数十种流行的 OLED/LCD 控制器。
  • 接口:支持 I2C, SPI (3线/4线), 并行接口。
  • 开发板:高度兼容 Arduino, ESP32, ESP8266, STM32 等各种微控制器平台。

2.2 两种缓冲模式

U8g2lib 提供了两种核心的屏幕更新策略,以适应不同内存需求的场景:

  1. 全帧缓冲模式 (Full Buffer Mode)

    • 工作原理:在微控制器的 RAM 中分配一块与整个显示器屏幕大小一致的位图缓冲区。所有绘制操作都在这个内存缓冲区中进行,然后一次性将整个缓冲区的内容传输到显示器。
    • 优点
      • 绘制性能高:绘制操作在内存中进行,速度快。
      • 图形效果好:支持复杂的图形效果和动画,无闪烁。
    • 缺点
      • 内存消耗大:一个 128x64 像素的屏幕需要 128 * 64 / 8 = 1024 字节的 RAM。对于内存有限的微控制器(如 Arduino UNO 的 2KB RAM),这可能是个问题。
    • 适用场景:ESP32/ESP8266 等拥有较多 RAM 的设备,需要复杂动画或流畅用户界面的应用。

    初始化示例 (Full Buffer):

    1
    2
    U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_UNUSED, /* clock=*/ SCL, /* data=*/ SDA);
    // 其中 '_F_' 表示 Full Buffer 模式
  2. 页缓冲模式 (Page Buffer Mode)

    • 工作原理:将显示器屏幕分成若干“页”,每次只在 RAM 中分配一个页大小的缓冲区。绘制操作针对当前页进行,然后将该页的内容传输到显示器。重复此过程直到所有页都被更新。
    • 优点
      • 内存消耗极低:一个 128x64 的屏幕通常分为 8 页 (每页 8 行)。每页只需要 128 * 8 / 8 = 128 字节的 RAM。
      • 高度内存优化:非常适合内存受限的微控制器。
    • 缺点
      • 绘制速度相对慢:每次更新都需要循环绘制所有页,可能会有轻微闪烁或绘制延迟。
      • 复杂图形处理受限:复杂的图形操作(如跨页的动画)可能需要更复杂的逻辑。
    • 适用场景:Arduino UNO 等内存极其有限的设备,静态或简单图形显示。

    初始化示例 (Page Buffer):

    1
    2
    U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_UNUSED, /* clock=*/ SCL, /* data=*/ SDA);
    // 其中 '_1_' 表示 Page Buffer 模式 (1页)

2.3 丰富的字体库

  • 内置字体:U8g2lib 提供了大量高质量的位图字体,包括各种大小、风格和语言(支持中文等)。
  • 字体编辑器:提供了工具(如 bdfconv)可以将标准字体转换为 U8g2lib 兼容格式。

2.4 强大的图形绘制功能

  • 基本图形:画点 (drawPixel), 线 (drawLine), 矩形 (drawBox, drawFrame), 圆 (drawCircle), 椭圆 (drawEllipse)。
  • 高级图形:多边形 (drawPolygon), 位图 (drawBitmap), XBM (drawXBM)。
  • 文本输出:设置字体 (setFont), 绘制字符串 (drawStr), 获取文本宽度 (getStrWidth)。

2.5 国际化支持

  • 支持 UTF-8 编码,可以显示各种语言字符,包括中文、日文、韩文等。
  • 需要选择包含对应字符集的字体。

三、U8g2lib 的基本使用流程 (Arduino/ESP32 为例)

3.1 1. 安装库

在 Arduino IDE 或 PlatformIO 中搜索 U8g2 并安装。

3.2 2. 选择合适的显示器构造函数

这是 U8g2lib 使用的关键一步。根据你的显示器型号、分辨率、接口类型和缓冲模式,选择一个合适的构造函数。

构造函数命名规则: U8G2_<控制器>_<分辨率>_<模式>_<接口> [参数]

  • <控制器>:如 SSD1306, SH1106, ST7920
  • <分辨率>:如 128X64, 128X32
  • <模式>
    • _F_:全帧缓冲 (Full Buffer)
    • _N_:不使用帧缓冲 (No Buffer),直接写入硬件,适用于较小的显示器或极度内存受限场景。
    • _1_:页缓冲 (Page Buffer),每次传输一个页。
    • _2_:页缓冲,每次传输 2 个页。
  • <接口>
    • _HW_I2C:硬件 I2C
    • _SW_I2C:软件 I2C
    • _HW_SPI:硬件 SPI
    • _SW_SPI:软件 SPI
    • _8BIT_PARALLEL:8位并行接口
  • [参数]:如 U8G2_R0 (屏幕旋转 0 度), U8G2_R1 (旋转 90 度), U8G2_R2 (旋转 180 度), U8G2_R3 (旋转 270 度)。

示例:

  • U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_UNUSED, SCL, SDA);
    • SSD1306 控制器,128x64 分辨率,全帧缓冲,硬件 I2C 接口,不旋转。
    • U8X8_UNUSED 表示没有硬件复位引脚。SCL, SDA 是 I2C 引脚,对于硬件 I2C,通常会自动使用默认引脚,但明确指定也无妨。
  • U8G2_SH1106_128X64_NONAME_1_HW_SPI u8g2(U8G2_R0, 10, 9, 8, 7);
    • SH1106 控制器,128x64 分辨率,页缓冲 (1 页),硬件 SPI 接口,不旋转。
    • 引脚参数顺序通常是:U8G2_R0, cs, dc, res, sck, mosi (对于 SPI)。这里 cs=10, dc=9, res=8, (sck=默认), (mosi=默认)

3.3 3. 基本代码结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <U8g2lib.h>

// 1. 根据你的显示器和接口选择合适的构造函数
// 例如:128x64 SSD1306 OLED, 硬件I2C, 全帧缓冲模式
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_UNUSED, SCL, SDA);
// 或者对于ESP32/ESP8266,I2C引脚可以这样指定:
// U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_UNUSED, D1, D2); // ESP8266为例

void setup() {
u8g2.begin(); // 初始化U8g2库和显示器
u8g2.setFont(u8g2_font_ncenB14_tr); // 设置字体
}

void loop() {
u8g2.clearBuffer(); // 清除内部缓冲区,全帧缓冲模式下有效

// 绘制内容
u8g2.drawStr(0, 15, "Hello, World!"); // 在(0, 15)坐标绘制字符串
u8g2.drawBox(10, 20, 30, 10); // 绘制一个填充矩形

u8g2.sendBuffer(); // 将内部缓冲区的内容发送到显示器,全帧缓冲模式下有效

// 对于页缓冲模式,通常在一个循环中处理绘制和发送
// u8g2.firstPage();
// do {
// u8g2.setFont(u8g2_font_ncenB14_tr);
// u8g2.drawStr(0, 15, "Hello, Page!");
// } while (u8g2.nextPage());

delay(1000); // 延迟1秒
}

3.4 4. 常用函数

  • u8g2.begin(): 初始化显示器。
  • u8g2.clearBuffer(): 清空内部帧缓冲区(仅全帧缓冲模式)。
  • u8g2.sendBuffer(): 将帧缓冲区内容发送到显示器(仅全帧缓冲模式)。
  • u8g2.firstPage() / u8g2.nextPage(): 用于页缓冲模式的循环。
  • u8g2.setFont(const u8g2_font_t *font): 设置当前字体。
  • u8g2.drawStr(u8g2_uint_t x, u8g2_uint_t y, const char *s): 在指定位置绘制字符串。
  • u8g2.drawUTF8(u8g2_uint_t x, u8g2_uint_t y, const char *s): 绘制 UTF-8 编码的字符串。
  • u8g2.drawPixel(u8g2_uint_t x, u8g2_uint_t y): 画点。
  • u8g2.drawLine(u8g2_uint_t x1, u8g2_uint_t y1, u8g2_uint_t x2, u8g2_uint_t y2): 画线。
  • u8g2.drawBox(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h): 绘制填充矩形。
  • u8g2.drawFrame(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h): 绘制矩形边框。
  • u8g2.drawCircle(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t opt): 绘制圆。
  • u8g2.setCursor(u8g2_uint_t x, u8g2_uint_t y): 设置文本光标位置。
  • u8g2.print(...): 类似 Serial.print(),用于在当前光标位置打印数据。
  • u8g2.getDisplayWidth() / u8g2.getDisplayHeight(): 获取显示器宽度和高度。

四、高级特性与优化

  1. 字体选择:U8g2lib 的字体名称通常包含信息,如 u8g2_font_ncenB14_tr

    • ncen:字体家族 (New Century Schoolbook)
    • B:粗体 (Bold)
    • 14:字高 (14 像素)
    • tr:透明 (Transparent),即背景不绘制。tf 表示填充 (Filled) 背景。
      选择合适的字体对于优化显示效果和内存占用很重要。
  2. 自定义字体:使用 bdfconv 工具将 .bdf 字体文件转换为 C 数组,并集成到项目中。

  3. 电源管理u8g2.setPowerSave(0) (打开屏幕), u8g2.setPowerSave(1) (关闭屏幕),在需要省电时非常有用。

  4. 对比度设置u8g2.setContrast(value) 可以调整 OLED 显示器的亮度。

  5. 旋转与翻转:在构造函数中选择 U8G2_R0U8G2_R3 进行旋转,u8g2.setFlipMode(1) 可以进行水平/垂直翻转。

  6. 内存优化 (Page Buffer)

    • 在内存受限的设备上,优先使用页缓冲模式 (_1__N_)。
    • u8g2.firstPage(); do { ... } while(u8g2.nextPage()); 是页缓冲模式的标准循环结构。
  7. 动画

    • 全帧缓冲模式更适合动画,因为可以在内存中高效绘制多帧,然后快速刷新。
    • 页缓冲模式下实现动画需要更精细的绘制控制,可能需要在每个 nextPage() 迭代中更新动画元素。

五、U8g2lib 与 U8glib 的区别

U8g2lib 是 U8glib 的现代版本,主要改进包括:

  • 内存优化:U8g2lib 提供了更灵活的内存管理(全帧缓冲、页缓冲、无缓冲),特别是在页缓冲模式下更加高效。
  • 统一 API:U8g2lib 的 API 设计更加统一和简洁。
  • 字体改进:支持的字体更多,并且对字体的渲染和管理进行了优化。
  • 国际化:更好的 UTF-8 支持。
  • 硬件支持:支持更多新型显示器控制器。

对于新项目,强烈推荐使用 U8g2lib。

六、总结

U8g2lib 是嵌入式领域中一个非常成熟和强大的单色图形库。它以其广泛的硬件兼容性、灵活的缓冲模式、丰富的字体和全面的图形绘制功能,极大地简化了单色显示器的开发。无论是对于内存有限的 Arduino UNO,还是资源更丰富的 ESP32,U8g2lib 都能提供高效且高质量的显示解决方案。掌握 U8g2lib 的使用,将使您能够轻松地为您的嵌入式项目创建富有表现力的用户界面。