【摘要】通过将任意文本内容通过MCU串口进行输入读取后,将输入的内容传输到墨水屏模块上进行显示,可以用来自定义文本内容、便携提示牌显示等。
一、硬件介绍
1、开发板
Arduino GIGA R1开发板,提供76个GPIO引脚(3个I2C、2个SPI等)采用双核STM32H747XI微控制器;
(480MHz的Cortex-M7内核和240 MHz的Cortex-M4内核),配备2MB闪存、1MB内存,以及6MB外部闪存和8MB SDRAM;
2、墨水屏
2.9英寸三色墨水屏(黑白红),128×296像素,支持1位黑白/红显示功能



3、墨水屏转接板
24pinFFC_SPI转接板

二、功能实现
1、硬件介绍
Arduino GIGA R1开发板通过硬件SPI接口与连接墨水屏模块的SPI转接板进行连接;
MCU引脚图

2、功能效果
功能效果:通过串口读取功能实现文本内容的显示、以及指令控制与低功耗运行等效果;

主要功能如下:
1、自定义中文字库显示(UTF-8)
GxEPD2库本身不支持中文显示;
因此采用兼容Adafruit_GFX库格式的U8g2_for_Adafruit_GFX库进行UTF-8格式字体适配;
字体采用自定义的中文字体(30pt):支持常用中文、英文、数字、符号等显示;
2、自动换行 / 清屏
自动计算每个文字的宽度,当文本超出当前行宽度时自动换行;
当文本超出屏幕整体高度范围时,自动清屏并重新开始显示;
3、串口指令控制
通过串口工具发送指令 / 文本:
发送 clear:清空全屏显示内容;
发送 del:删除上一次显示的文本内容;
发送 文本内容:屏幕直接显示并自动排版,仅局部刷新文字所在的区域,每次操作后让墨水屏自动进入低功耗休眠模式;
3、实物效果
实物搭建效果图:

硬件引脚连接方式

三、代码编写
三、代码编写
主要用到 GxEPD2 / U8g2_for_Adafruit_GFX 库
主要相关代码(Arduino)
#include "GxEPD2_display_selection_new_style.h" // 定义 GxEPD2_DRIVER_CLASS / GxEPD2_DISPLAY_CLASS
#include <GxEPD2_3C.h> // 3色墨水屏定义
#include <U8g2_for_Adafruit_GFX.h>
#include "u8g2_font_cn_30.c" // 自定义字体
#define ENABLE_GxEPD2_GFX 1
U8G2_FOR_ADAFRUIT_GFX u8g2Fonts;
// 自定义SPI引脚
#define EPD_CS 10
#define EPD_DC 8
#define EPD_RST 9
#define EPD_BUSY 7
arduino::MbedSPI SPIn(12, 11, 13); // Arduino SPI自定义
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, GxEPD2_DRIVER_CLASS::HEIGHT> display(
GxEPD2_DRIVER_CLASS(/*CS=*/EPD_CS, /*DC=*/EPD_DC, /*RST=*/EPD_RST, /*BUSY=*/EPD_BUSY));
// 光标X Y位置
int cursorX = 5;
int cursorY = 5;
// ascent: 文字最高的高度 descent: 文字最低的高度
int ascent, descent, textHeight;
int lastX, lastY, lastW, lastH;
// 文本内容显示
void showText(const char* text) {
const char* remain = text; // 剩余未显示文本
while (*remain != '\0') {
int availW = display.width() - 5 - cursorX; // 当前行剩余宽度
int usedW = 0;
int charCount = 0;
// 剩余空间不够 / 换行
if (availW <= 0) {
cursorX = 5;
cursorY += textHeight + 10;
availW = display.width() - 5 - cursorX;
}
// 判断字符
const char* temp = remain;
while (*temp != '\0') {
int step = 1;
if ((unsigned char)*temp >= 0xC0) step = 3; // 中文占3字节
char single[4] = {0};
strncpy(single, temp, step);
int w = u8g2Fonts.getUTF8Width(single); //字符宽度
if (usedW + w > availW) break;
usedW += w + 2;
temp += step;
charCount = temp - remain;
}
// 当前行剩余空间
if (charCount == 0) {
cursorX = 5;
cursorY += textHeight + 10;
// 超出屏幕底部 清屏
if (cursorY + textHeight > display.height()) {
clearScreen();
}
continue;
}
// 截取当前行要显示的文本
char lineBuf[128];
if (charCount >= (int)sizeof(lineBuf)) charCount = sizeof(lineBuf) - 1;
strncpy(lineBuf, remain, charCount);
lineBuf[charCount] = '\0';
if (cursorY + textHeight > display.height()) {
clearScreen();
}
// 局部刷新范围
int rx = cursorX;
int ry = cursorY;
int rw = usedW + 4;
int rh = textHeight + 4;
display.setPartialWindow(rx, ry, rw, rh);
display.firstPage();
do {
u8g2Fonts.drawUTF8(cursorX, cursorY + textHeight, lineBuf);
lastX = rx; lastY = ry; lastW = rw; lastH = rh;
} while (display.nextPage());
cursorX += usedW;
remain += charCount;
}
}
// 删除上次显示内容
void deleteLast(){
display.setPartialWindow(
lastX, lastY,
lastW , lastH
);
display.firstPage();
do {
display.fillRect(lastX, lastY,lastW, lastH,GxEPD_WHITE);
} while (display.nextPage());
cursorX = lastX;
}
// 清屏
void clearScreen() {
display.setFullWindow();
display.firstPage();
do {
display.fillScreen(GxEPD_WHITE);
} while (display.nextPage());
// 重置光标
cursorX = 5;
cursorY = 5;
display.hibernate();
}
void setup() {
Serial.begin(115200);
display.epd2.selectSPI(SPIn, SPISettings(4000000, MSBFIRST, SPI_MODE0));
while (!Serial);
display.init(115200); // 初始化屏幕
display.setRotation(3); // 屏幕显示方向
// 字体显示设置
u8g2Fonts.begin(display);
u8g2Fonts.setFont(u8g2_font_cn_30);
u8g2Fonts.setForegroundColor(GxEPD_BLACK);
u8g2Fonts.setBackgroundColor(GxEPD_WHITE);
display.setFullWindow();
display.firstPage();
do {
display.fillScreen(GxEPD_WHITE); //清屏
} while (display.nextPage());
// 获取字体高度
ascent = u8g2Fonts.getFontAscent();
descent = u8g2Fonts.getFontDescent();
textHeight = ascent - descent;
display.hibernate();
}
void loop() {
if (Serial.available()) {
String buf = Serial.readStringUntil('\n');
if (buf.length() == 0) return;
// 清屏指令
if (buf.equalsIgnoreCase("clear")) {
clearScreen();
}else if (buf.equalsIgnoreCase("del")) { // 删除指令
deleteLast();
} else {
showText(buf.c_str());
Serial.print("显示内容: ");
Serial.println(buf);
display.hibernate(); // 休眠模式
}
}
}四、程序烧录
1、连接USB数据线至开发板;
2、选择端口号对应的开发板;
3、点击 上传 烧录程序到开发板上;

五、效果演示



我要赚赏金
