电子墨水屏文本渲染
电子墨水屏文本渲染
本节介绍使用 GxEPD2 和 AdaFruit GFX 库在电子纸显示屏上渲染文本。正确的文本布局对于创建可读的工厂仪表板至关重要。完成本节后,你将能够:
- 在电子纸屏幕的各个位置显示文本
- 使用内置字体更改字体大小和样式
- 水平和垂直对齐文本
- 在单个屏幕上组合多个文本元素
开始本节前,请确保:
- 已安装 GxEPD2 库(参见 02-03)
- 显示屏已接线并验证
- 基本理解显示坐标
电子纸显示屏使用标准的笛卡尔坐标系,原点在左上角:
(0,0) ──────────────────────────── X(宽度) │ │ +-------------------+ │ | | │ | 显示区域 | │ | | │ +-------------------+ │ Y(高度)对于 2.13” 显示屏(250×122 像素):
- X 范围:0 到 249(水平)
- Y 范围:0 到 121(垂直)
- 文本使用
display.setCursor(x, y)放置
AdaFruit GFX 库提供多种内置字体:
| 字体名称 | 大小 | 用例 |
|---|---|---|
| (默认) | 5×7 px | 最小文本、调试输出 |
&FreeMono9pt7b | 9pt 等宽 | 表格数据 |
&FreeMonoBold12pt7b | 12pt 粗体等宽 | 标题、KPI |
&FreeMonoBold18pt7b | 18pt 粗体等宽 | 大数字 |
&FreeMonoBold24pt7b | 24pt 粗体等宽 | 非常大数值 |
&FreeSans9pt7b | 9pt 无衬线 | 正文 |
&FreeSansBold12pt7b | 12pt 粗体无衬线 | 章节标题 |
&FreeSerif12pt7b | 12pt 衬线 | 装饰性文本 |
&TomThumb | 3×5 px | 超小标签 |
字体命名约定:&FontNameSizept7b 其中:
&是引用字体时必需的前缀- 7b 表示 7 位字符编码(标准 ASCII)
步骤 1:基本文本输出
Section titled “步骤 1:基本文本输出”#include <GxEPD2_BW.h>
#define EPD_CS 5#define EPD_DC 17#define EPD_RST 16#define EPD_BUSY 4
GxEPD2_BW<GxEPD2_213_B72, GxEPD2_213_B72::HEIGHT> display( GxEPD2_213_B72(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY));
void setup() { Serial.begin(115200); display.init(115200); display.setRotation(1); // 横向
// 清除屏幕 display.fillScreen(GxEPD_WHITE);
// 在指定坐标绘制文本 display.setCursor(10, 20); display.print("第1行:工厂显示屏");
display.setCursor(10, 35); display.print("第2行:温度:25.5°C");
display.setCursor(10, 50); display.print("第3行:湿度:60%");
// 推送显示 display.display();}
void loop() {}预期结果:左侧显示三行小文本。
步骤 2:使用不同字体
Section titled “步骤 2:使用不同字体”#include <GxEPD2_BW.h>
GxEPD2_BW<GxEPD2_213_B72, GxEPD2_213_B72::HEIGHT> display( GxEPD2_213_B72(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY));
void setup() { display.init(115200); display.setRotation(1); display.fillScreen(GxEPD_WHITE);
// 默认 5x7 字体 display.setCursor(0, 10); display.print("默认字体(5x7)");
// 9pt 等宽 display.setFont(&FreeMono9pt7b); display.setCursor(0, 30); display.print("9pt 等宽");
// 12pt 粗体等宽 display.setFont(&FreeMonoBold12pt7b); display.setCursor(0, 55); display.print("12pt 粗体");
// 18pt 粗体等宽(大数字) display.setFont(&FreeMonoBold18pt7b); display.setCursor(0, 85); display.print("42.5");
display.display();}
void loop() {}注意:使用
setFont()更改字体后,光标位置行为会变化。基于字体的光标使用字体的基线作为 Y 参考点,而默认字体使用左上角。
步骤 3:文本居中
Section titled “步骤 3:文本居中”要水平居中文本,使用文本边界计算位置:
#include <GxEPD2_BW.h>
GxEPD2_BW<GxEPD2_213_B72, GxEPD2_213_B72::HEIGHT> display( GxEPD2_213_B72(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY));
void centerText(const char* text, int y, const GFXfont* font) { display.setFont(font);
// 获取文本边界 int16_t x1, y1; uint16_t w, h; display.getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
// 计算居中的 X 位置 int16_t x = (display.width() - w) / 2;
// 在居中位置绘制文本 display.setCursor(x, y); display.print(text);}
void setup() { display.init(115200); display.setRotation(1); display.fillScreen(GxEPD_WHITE);
// 标题居中 display.setTextColor(GxEPD_BLACK); display.setFont(&FreeMonoBold12pt7b); centerText("工厂状态", 25, &FreeMonoBold12pt7b);
// 温度值居中 centerText("温度:25.5°C", 60, &FreeMono9pt7b);
// 湿度值居中 centerText("湿度:60%", 85, &FreeMono9pt7b);
display.display();}
void loop() {}步骤 4:文本旋转
Section titled “步骤 4:文本旋转”电子纸显示屏支持 4 种旋转模式:
// 旋转值display.setRotation(0); // 0° - 竖屏(默认)display.setRotation(1); // 90° - 横屏display.setRotation(2); // 180° - 倒置竖屏display.setRotation(3); // 270° - 倒置横屏工厂仪表板通常使用:
- 旋转 1(横屏):用于更宽的数据表显示
- 旋转 0(竖屏):用于 KPI 式单值显示
步骤 5:多行显示函数
Section titled “步骤 5:多行显示函数”void displayText(const char* title, const char* value1, const char* value2) { display.fillScreen(GxEPD_WHITE);
// 标题 display.setFont(&FreeMonoBold12pt7b); display.setCursor(5, 25); display.print(title);
// 水平分隔线 display.drawFastHLine(0, 35, display.width(), GxEPD_BLACK);
// 值 1 display.setFont(&FreeMono9pt7b); display.setCursor(5, 55); display.print(value1);
// 值 2 display.setCursor(5, 80); display.print(value2);
display.display();}
// 在 setup() 中使用:// displayText("工厂 A", "温度:25.5°C", "湿度:60%");上传文本渲染草图后,验证:
- 文本出现在预期位置
- 不同字体正确显示
- 居中文本正确对齐
- 旋转按预期改变方向
- 文本可读(白底黑字)
问题 1:文本显示为乱码
Section titled “问题 1:文本显示为乱码”症状:
- 随机字符而非可读文本
解决方案:
- 确保字体引用时带有
&前缀(例如&FreeMono9pt7b) - 检查字体是否在草图作用域内定义
- 不要在字体更改之间意外使用
setFont(NULL)
问题 2:文本被截断
Section titled “问题 2:文本被截断”症状:
- 文本超出显示边界
- 文本底部缺失
解决方案:
- 在绘图前使用
display.getTextBounds()测量文本宽度 - 确保 Y 位置考虑了字体高度(较大字体需要更多垂直空间)
- 减小字体大小或文本行长度
问题 3:文本对齐偏移
Section titled “问题 3:文本对齐偏移”症状:
- 居中文本未真正居中
- 多行文本未垂直对齐
解决方案:
// 使用辅助函数实现一致对齐int16_t getCenteredX(const char* text, const GFXfont* font) { display.setFont(font); int16_t x1, y1; uint16_t w, h; display.getTextBounds(text, 0, 0, &x1, &y1, &w, &h); return (display.width() - w) / 2;}- ✅ 表格数据使用等宽字体——确保列正确对齐
- ✅ KPI 首选粗体字体——远距离阅读效果更好
- ✅ 预先计算文本边界以实现可靠居中
- ❌ 避免在工厂车间使用小字体(< 9pt)
- ❌ 不要在单个屏幕上混合过多字体大小——最多保持在 2-3 种
- ❌ 避免不必要的字体更改——每次
setFont()调用都会增加处理开销
常见布局模板
Section titled “常见布局模板”模板 1:单一 KPI(工厂 KPI 看板)
┌─────────────────────────────────────┐│ 生产线 A │ ← 12pt 粗体,居中├─────────────────────────────────────┤│ ││ 25,847 │ ← 24pt 粗体,居中│ 今日产量 │ ← 9pt,居中│ ││ 目标:30,000 │ 同比 +5.3% │ ← 9pt 等宽,两列└─────────────────────────────────────┘模板 2:多传感器读数
┌─────────────────────────────────────┐│ 环境监测 │ 14:30 │ ← 带时间的标题├─────────────────────────────────────┤│ 温度 │ 25.5°C │ 正常 ││ 湿度 │ 60% │ 正常 ││ 气压 │ 1013hPa │ 稳定 ││ 光照 │ 450 lux │ 良好 │└─────────────────────────────────────┘- 文本渲染使用 AdaFruit GFX 原语——
setCursor()、setFont()、print() - 字体选择影响光标行为——基于字体的字体使用基线 Y 坐标
- 使用
getTextBounds()进行精确定位——居中和对齐的关键 - 显示旋转改变坐标系——在初始化时设置一次
- 保持文本布局简洁——最多 2-3 种字体大小以确保可读性