先读FPC型号,尺寸相同也不能套驱动
本篇锁Good Display GDEY0213B74黑白面板,控制器SSD1680Z,可见122×250;不带前光或触摸后缀。只知道“2.13寸”“V4”不能证明它就是这个面板,GDEH0213B73或三色屏也不能用本篇类试刷。
裸面板不能接几根GPIO就驱动,需要DESPI-C02上的升压外围。选择P3明确标2.2Ω/0.47Ω的当前型号并断电选2.2Ω,与面板手册p29参考电路匹配;旧DESPI-C02 Rev1.1文档列3Ω选项,不能把旧板丝印假定成当前2.2Ω。本任务不教带电更换FPC。
- 做到这里,你应该看到
- 把面板标签、转接板正反面与P3照片保存;身份不符就先停在核对步骤。
参考:[1] Good Display GDEY0213B74:面板/SSD1680Z/122×250[2] 厂家面板Rev1.0 2021-03-17 PDF(Seeed托管)[3] DESPI-C02当前2.2Ω/0.47Ω板、电压与接口[4] DESPI-C02旧Rev1.1文档索引:3Ω旧修订须区分
类名锁到面板,区分可见宽与RAM宽
使用GxEPD2 1.6.9的GxEPD2_213_GDEY0213B74;header定义WIDTH=128、WIDTH_VISIBLE=122、HEIGHT=250。完整1bpp缓冲是128×250/8=4000字节,而GxEPD2_BW的可见画布采用122宽,rotation1得到250×122。
setFullWindow/firstPage/nextPage走全窗口刷新;该固定类启用useFastFullUpdate的全窗口波形,本篇没有关闭这个fast全更设置,也没有把它称作局部刷新。首次写入可能先清屏、出现多次闪动,函数一次调用不等于肉眼只闪一次。
参考:[5] GxEPD2 1.6.9面板类:宽度、BUSY高与10s等待[6] GxEPD2 1.6.9面板具体刷新、hibernate实现[8] GxEPD2 1.6.9:BW画布与分页
只用3.3V,BUSY始终是输入
SCK12/J1-18、SDI11/J1-17、CS5/J1-5、D/C4/J1-4、RES6/J1-6,3V3/J1-1接转接板3.3V、GND/J1-22共地。CS加10kΩ至3V3;无MISO。SPI mode0、MSB先行、4MHz,DESPI-C02用10ms低有效复位。
模块BUSY经预装SPST接GPIO10/J1-16,在MCU侧另加10kΩ到3V3:正常闭合,高表示忙,断开后MCU端被拉高。GPIO10只输入,不能用输出硬拉模块BUSY。所有FPC/供电接线与P3切换均断电完成;没有开关可直连BUSY并跳过注入任务。
参考:[2] 厂家面板Rev1.0 2021-03-17 PDF(Seeed托管)[3] DESPI-C02当前2.2Ω/0.47Ω板、电压与接口[7] GxEPD2 1.6.9:_waitWhileBusy/回调/selectSPI[10] DevKitC-1 v1.1排针
完整依赖锁与固定构建命令
Arduino CLI1.5.1/core3.3.12、GxEPD2 1.6.9、Adafruit GFX1.12.4、BusIO1.17.4。下载的sketch.yaml仅定义profile,没有default_profile;正常复用已装全局工具,按完整FQBN编译。首次安装先按README固定每个库版本,GxEPD2用--no-deps避免自动漂移依赖。
IDE对应8MB Flash、OPI PSRAM、QIO、8M with spiffs、USB CDC On Boot Disabled、Hardware CDC and JTAG;连接USB-to-UART桥接口,115200串口。程序先epdSPI.begin显式指定引脚,再selectSPI;固定core里已初始化的SPI begin直接返回,不会被库的无参begin换到默认引脚。
作者实际编译Exit0,程序346380字节、全局变量28248字节;这些是构建工具报告,不是整机功耗或实板运行结果。
arduino-cli compile --fqbn 'esp32:esp32:esp32s3:FlashSize=8M,PSRAM=opi,FlashMode=qio,PartitionScheme=default_8MB,CDCOnBoot=default,USBMode=hwcdc' --build-path /tmp/d04-build .
arduino-cli upload --fqbn 'esp32:esp32:esp32s3:FlashSize=8M,PSRAM=opi,FlashMode=qio,PartitionScheme=default_8MB,CDCOnBoot=default,USBMode=hwcdc' -p /dev/cu.YOUR_UART_PORT --input-dir /tmp/d04-build参考:[7] GxEPD2 1.6.9:_waitWhileBusy/回调/selectSPI[9] Arduino-ESP32 3.3.12 SPI begin实现
完整程序:手动刷新、观察预算与停止标志
启动只打印ready,不自动刷新。发送r才尝试,先检查BUSY低;同次上电两次尝试至少180秒,这是本例保守教学限制,不是本面板寿命或最小刷新时间实测。原样helper有单独host fixture,覆盖时间回绕和锁存。
// ---- BusyBudget.h ----
// SPDX-License-Identifier: MIT
#pragma once
#include <stdint.h>
struct BusyBudget {
static constexpr uint32_t limitMs=8000;
uint32_t started=0;bool active=false;bool fault=false;
void begin(uint32_t now){started=now;active=true;fault=false;}
void observe(uint32_t now,bool busy){if(active&&busy&&uint32_t(now-started)>=limitMs)fault=true;}
bool finish(uint32_t now,bool busy){observe(now,busy);active=false;if(busy)fault=true;return !fault;}
};
inline bool refresh_due(uint32_t now,uint32_t last,bool attempted){return !attempted||uint32_t(now-last)>=180000;}
// ---- esp32-epaper-first-refresh.ino ----
// SPDX-License-Identifier: MIT
#include <Arduino.h>
#include <SPI.h>
#include <GxEPD2_BW.h>
#include <gdey/GxEPD2_213_GDEY0213B74.h>
#include "BusyBudget.h"
// Good Display GDEY0213B74 + DESPI-C02 (2.2/0.47 ohm variant, P3=2.2 ohm).
constexpr int PIN_SCK=12,PIN_MOSI=11,PIN_CS=5,PIN_DC=4,PIN_RST=6,PIN_BUSY=10;
SPIClass epdSPI(FSPI);
GxEPD2_BW<GxEPD2_213_GDEY0213B74,GxEPD2_213_GDEY0213B74::HEIGHT>
display(GxEPD2_213_GDEY0213B74(PIN_CS,PIN_DC,PIN_RST,PIN_BUSY));
BusyBudget budget;
bool attempted=false,halted=false;
uint32_t lastAttempt=0,sequence=0;
void busyCallback(const void*){
budget.observe(millis(),digitalRead(PIN_BUSY)==HIGH);
delay(1); // callback only observes; GxEPD2 owns its wait loop and timeout.
}
bool preflight(){
uint32_t start=millis();
while(digitalRead(PIN_BUSY)==HIGH&&uint32_t(millis()-start)<2000)delay(1);
return digitalRead(PIN_BUSY)==LOW;
}
void fullRefresh(){
if(halted){Serial.println("HALTED: power off, repair, then restart");return;}
if(!refresh_due(millis(),lastAttempt,attempted)){Serial.println("WAIT: 180 s between attempts");return;}
if(!preflight()){halted=true;Serial.println("STOP: BUSY high before call; no display transaction started");return;}
attempted=true;lastAttempt=millis();++sequence;
budget.begin(millis());
// Reset pulse 10 ms for DESPI-C02. Re-init after hibernate; initial=true.
display.init(115200,true,10,false);
display.setRotation(1); // visible canvas 250 x 122; controller RAM 128 x 250.
display.setFullWindow();
Serial.printf("BEGIN full-window sequence=%lu canvas=%dx%d\n",(unsigned long)sequence,display.width(),display.height());
display.firstPage();
do{
display.fillScreen(GxEPD_WHITE);
display.drawRect(0,0,display.width(),display.height(),GxEPD_BLACK);
display.fillRect(8,8,30,30,GxEPD_BLACK);
display.drawRect(display.width()-38,8,30,30,GxEPD_BLACK);
display.setTextColor(GxEPD_BLACK);display.setTextSize(1);
display.setCursor(48,14);display.print("GDEY0213B74");
display.setCursor(48,29);display.print("SSD1680 / FULL WINDOW");
display.setCursor(8,53);display.print("BLACK WHITE BORDER");
display.setCursor(8,70);display.print("SEQ ");display.print(sequence);
display.setCursor(8,88);display.print("CALL RETURN != OPTICAL PASS");
display.setCursor(8,105);display.print("250 x 122 / ROTATION 1");
}while(display.nextPage());
if(!budget.finish(millis(),digitalRead(PIN_BUSY)==HIGH)){
halted=true;
Serial.println("STOP: busy budget/level fault; driver call has returned; no hibernate or further display calls");
return;
}
Serial.println("CALL_RETURNED_BUSY_LOW: inspect screen and record; no optical verification");
budget.begin(millis());display.hibernate();
if(!budget.finish(millis(),digitalRead(PIN_BUSY)==HIGH)){
halted=true;Serial.println("STOP: hibernate call returned with fault; power off and repair");return;
}
Serial.println("HIBERNATE_CALL_RETURNED: MCU and board still powered; no current measurement");
}
void setup(){
Serial.begin(115200);delay(300);
pinMode(PIN_BUSY,INPUT); // external 10k pull-up on MCU side of optional BUSY SPST.
digitalWrite(PIN_CS,HIGH);pinMode(PIN_CS,OUTPUT);
if(!epdSPI.begin(PIN_SCK,-1,PIN_MOSI,PIN_CS)){halted=true;Serial.println("STOP: SPI begin failed");return;}
display.epd2.selectSPI(epdSPI,SPISettings(4000000,MSBFIRST,SPI_MODE0));
display.epd2.setBusyCallback(busyCallback);
Serial.println("D04 ready; verify panel ID, then send r for full-window refresh. No auto refresh.");
}
void loop(){
if(Serial.available()){char c=Serial.read();if(c=='r'||c=='R')fullRefresh();}
delay(1);
}
先看完整图案,再做断电保持任务
正常调用返回且BUSY低打印CALL_RETURNED_BUSY_LOW,程序立即调用hibernate,不等待人工确认。HIBERNATE_CALL_RETURNED之后用肉眼另验黑块、白底空心块、文字与四边并拍照。这两条日志都只是API与BUSY状态,ESP32和开发板仍通电,没有光学、深睡或电流测量。
完成正常图案和休眠后,才断开所有USB/外供并消除其他信号回灌,观察60秒和5分钟后的同一图像。记录是否保持和照片,不外推无限保存时间、刷新寿命或整机零功耗。
- 断电确认标签/P3、接线与BUSY开关闭合,打开115200串口,等待D04 ready。
- 发r,保存BEGIN至返回日志与实际调用耗时,核对250×122画布和图案;有闪动不自动判失败。
- 正常hibernate返回后断开全部供电,60s与5min拍照;observations.csv填真实观察。
- 恢复供电,程序仍只ready;需再次手动r,当前上电实例的180s门限重新开始。
- 做到这里,你应该看到
- 硬件维度仍pending:作者未进行刷新、BUSY测量、断电保持或电流实验。
参考:[1] Good Display GDEY0213B74:面板/SSD1680Z/122×250[2] 厂家面板Rev1.0 2021-03-17 PDF(Seeed托管)[6] GxEPD2 1.6.9面板具体刷新、hibernate实现[8] GxEPD2 1.6.9:BW画布与分页
8秒观察不是取消,10秒也是每个等待的时限
固定库的_waitWhileBusy在BUSY高时循环;超过10,000,000微秒打印Busy Timeout!后break,函数返回void,随后内部代码仍可能继续发命令。一个高层调用可包含多次等待,所以不能承诺“总调用最多10秒”或“没有返回错误就是成功”。
本例回调每轮delay(1),只观察当前调用开始后是否出现高BUSY且已达8秒,达到即锁存fault。它不能中断库,也不能阻止库超时后继续内部命令;应用仅在高层调用返回后检查fault或BUSY仍高,异常时停后续显示调用、不hibernate。
这不是严格8s/10s取消机制;真实控制器一直忙时库仍继续命令的风险并未由此修复。原样host fixture只验观察器状态和门限,未模拟GxEPD2、SSD1680或波形。
参考:[2] 厂家面板Rev1.0 2021-03-17 PDF(Seeed托管)[5] GxEPD2 1.6.9面板类:宽度、BUSY高与10s等待[6] GxEPD2 1.6.9面板具体刷新、hibernate实现[7] GxEPD2 1.6.9:_waitWhileBusy/回调/selectSPI
两种BUSY异常分别执行,别把预检查当库超时
以下均为待实板的操作方案,空记录表没有预填预期结果。开关必须预先断电安装,运行时仅断开/闭合BUSY信号;不注入电源短路,不用GPIO输出拉高。
调用前高:保持开关断开再上电,发r,约2秒后应STOP且不启动显示事务。这个用例只证明预检查路径。
调用中失联:开关闭合正常启动,发r,在BEGIN之后、面板正在刷新时断开BUSY信号。预期库日志出现Busy Timeout!并在高层调用终于返回后STOP;人工可能错过窗口,须记录断开的时刻与完整日志,不能只看最终BUSY高就判用例通过。
保存日志,等当前调用返回后断电,闭合开关,重查FPC/P3/3.3V再上电。异常r始终HALTED,不会自动重试;若调用未返回,可断电恢复,不能以关闭看门狗或欠压检测作为修复。
- 做到这里,你应该看到
- “调用前拒绝”“驱动内部超时”“正常光学刷新”“断电保持”四类证据互不替代。
参考:[2] 厂家面板Rev1.0 2021-03-17 PDF(Seeed托管)[7] GxEPD2 1.6.9:_waitWhileBusy/回调/selectSPI
保留原日志和照片,恢复后再谈下一种屏幕
若没有图案,按顺序查面板号/类、P3、3.3V、FPC方向与卡扣、RES/CS/DC/SCK/SDI、BUSY是否被错误悬空。不能从不刷新的旧画面推断程序仍在正常更新,也不能靠反复刷新未知控制器试运气。
复刷先关monitor,经F06的BOOT按住→RESET→松BOOT进入下载模式;改外设接线仍断电。下载原创源码与文档MIT,第三方说明在THIRD_PARTY.md;GxEPD2是GPL-3.0,组合固件的分发须遵守其许可,下载未附第三方库和二进制。
前置F07提供资料/器件清单,F06提供串口与恢复流程。下一步可以回到D03对照持续发光TFT与双稳态墨水屏的验收方式;后续刷新耗时/残影/电量需要单独实测,不能从本例推断。
参考:[1] Good Display GDEY0213B74:面板/SSD1680Z/122×250[2] 厂家面板Rev1.0 2021-03-17 PDF(Seeed托管)[3] DESPI-C02当前2.2Ω/0.47Ω板、电压与接口[11] GxEPD2 1.6.9 GPL-3.0许可
资料来源与版本说明
固定版本源与具体模块资料逐项核对;构建与主机逻辑检查通过,所有实板观察待测。资料核对日期:。资料核对、程序构建与实板测试分别记录,不互相替代。
- Good Display GDEY0213B74:面板/SSD1680Z/122×250
- 厂家面板Rev1.0 2021-03-17 PDF(Seeed托管)
- DESPI-C02当前2.2Ω/0.47Ω板、电压与接口
- DESPI-C02旧Rev1.1文档索引:3Ω旧修订须区分
- GxEPD2 1.6.9面板类:宽度、BUSY高与10s等待
- GxEPD2 1.6.9面板具体刷新、hibernate实现
- GxEPD2 1.6.9:_waitWhileBusy/回调/selectSPI
- GxEPD2 1.6.9:BW画布与分页
- Arduino-ESP32 3.3.12 SPI begin实现
- DevKitC-1 v1.1排针
- GxEPD2 1.6.9 GPL-3.0许可