Przeglądaj źródła

Version 1.2.0

- added "fast partial update" (differential update) for GDEW0371W7 3.7" b/w 240x416
- improved differential update waveform for GDEW026T0 2.6" b/w 152x256
- fixed init code & improved differential update for GDEW042T2 4.2" b/w 300x400
ZinggJM 7 lat temu
rodzic
commit
dddb0a9a45

BIN
MyEPDs_UpdateInfos.pdf


+ 9 - 1
README.md

@@ -72,7 +72,15 @@
 #### other supported panels
 - ED060SCT        6" grey levels, on Waveshare e-Paper IT8951 Driver HAT
 
-### Version 1.1.10
+### Version 1.2.0
+- added "fast partial update" (differential update) for GDEW0371W7 3.7" b/w 240x416
+- improved differential update waveform for GDEW026T0 2.6" b/w 152x256
+- fixed init code & improved differential update for GDEW042T2 4.2" b/w 300x400
+- note that all differential refresh waveforms are a compromise (ghosting, big font use)
+- parameters for differential waveform for these display can easily be changed for experimenting
+- GDEW042T2 would have greyed background without sustain phase
+- GDEW042T2 needs multiple full refreshes after extended use of partial updates
+#### Version 1.1.10
 - added support for GDEH0213B73 2.13" b/w, replacement for GDE0213B1, GDEH0213B72
 - added support for GDEW026T0 2.6" b/w 152x256
 - added support for GDEW0371W7 3.7" b/w 240x416

+ 1 - 1
extras/Particle/examples/GxEPD2_ParticleExample/project.properties

@@ -1,2 +1,2 @@
 name=GxEPD2_ParticleExample
-dependencies.GxEPD2=1.1.10
+dependencies.GxEPD2=1.2.0

+ 4 - 0
extras/Particle/examples/GxEPD2_ParticleExample/src/GxEPD2_ParticleExample.ino

@@ -40,10 +40,13 @@
 //GxEPD2_BW<GxEPD2_154, GxEPD2_154::HEIGHT> display(GxEPD2_154(/*CS=A2*/ SS, /*DC=*/ A1, /*RST=*/ A0, /*BUSY=*/ D4));
 //GxEPD2_BW<GxEPD2_213, GxEPD2_213::HEIGHT> display(GxEPD2_213(/*CS=A2*/ SS, /*DC=*/ A1, /*RST=*/ A0, /*BUSY=*/ D4)); // GDE0213B1, phased out
 GxEPD2_BW<GxEPD2_213_B72, GxEPD2_213_B72::HEIGHT> display(GxEPD2_213_B72(/*CS=A2*/ SS, /*DC=*/ A1, /*RST=*/ A0, /*BUSY=*/ D4)); // GDEH0213B72
+//GxEPD2_BW<GxEPD2_213_B73, GxEPD2_213_B73::HEIGHT> display(GxEPD2_213_B73(/*CS=A2*/ SS, /*DC=*/ A1, /*RST=*/ A0, /*BUSY=*/ D4)); // GDEH0213B73
 //GxEPD2_BW<GxEPD2_213_flex, GxEPD2_213_flex::HEIGHT> display(GxEPD2_213_flex(/*CS=A2*/ SS, /*DC=*/ A1, /*RST=*/ A0, /*BUSY=*/ D4)); // GDEW0213I5F
 //GxEPD2_BW<GxEPD2_290, GxEPD2_290::HEIGHT> display(GxEPD2_290(/*CS=A2*/ SS, /*DC=*/ A1, /*RST=*/ A0, /*BUSY=*/ D4));
 //GxEPD2_BW<GxEPD2_290_T5, GxEPD2_290_T5::HEIGHT> display(GxEPD2_290_T5(/*CS=A2*/ SS, /*DC=*/ A1, /*RST=*/ A0, /*BUSY=*/ D4)); // GDEW029T5
+//GxEPD2_BW<GxEPD2_260, GxEPD2_260::HEIGHT> display(GxEPD2_260(/*CS=A2*/ SS, /*DC=*/ A1, /*RST=*/ A0, /*BUSY=*/ D4));
 //GxEPD2_BW<GxEPD2_270, GxEPD2_270::HEIGHT> display(GxEPD2_270(/*CS=A2*/ SS, /*DC=*/ A1, /*RST=*/ A0, /*BUSY=*/ D4));
+//GxEPD2_BW<GxEPD2_371, GxEPD2_371::HEIGHT> display(GxEPD2_371(/*CS=A2*/ SS, /*DC=*/ A1, /*RST=*/ A0, /*BUSY=*/ D4));
 //GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> display(GxEPD2_420(/*CS=A2*/ SS, /*DC=*/ A1, /*RST=*/ A0, /*BUSY=*/ D4));
 //GxEPD2_BW < GxEPD2_583, GxEPD2_583::HEIGHT / 2 > display(GxEPD2_583(/*CS=A2*/ SS, /*DC=*/ A1, /*RST=*/ A0, /*BUSY=*/ D4));
 //GxEPD2_BW < GxEPD2_750, GxEPD2_750::HEIGHT / 2 > display(GxEPD2_750(/*CS=A2*/ SS, /*DC=*/ A1, /*RST=*/ A0, /*BUSY=*/ D4));
@@ -55,6 +58,7 @@ GxEPD2_BW<GxEPD2_213_B72, GxEPD2_213_B72::HEIGHT> display(GxEPD2_213_B72(/*CS=A2
 //GxEPD2_3C<GxEPD2_420c, GxEPD2_420c::HEIGHT> display(GxEPD2_420c(/*CS=A2*/ SS, /*DC=*/ A1, /*RST=*/ A0, /*BUSY=*/ D4));
 //GxEPD2_3C<GxEPD2_583c, GxEPD2_583c::HEIGHT / 4 > display(GxEPD2_583c(/*CS=A2*/ SS, /*DC=*/ A1, /*RST=*/ A0, /*BUSY=*/ D4));
 //GxEPD2_3C<GxEPD2_750c, GxEPD2_750c::HEIGHT / 4 > display(GxEPD2_750c(/*CS=A2*/ SS, /*DC=*/ A1, /*RST=*/ A0, /*BUSY=*/ D4));
+//GxEPD2_3C<GxEPD2_750c_Z08, GxEPD2_750c_Z08::HEIGHT / 4 > display(GxEPD2_750c_Z08(/*CS=A2*/ SS, /*DC=*/ A1, /*RST=*/ A0, /*BUSY=*/ D4)); // GDEW075Z08 800x480
 #endif
 
 // comment out unused bitmaps to reduce code space used

+ 1 - 1
library.properties

@@ -1,5 +1,5 @@
 name=GxEPD2
-version=1.1.10
+version=1.2.0
 author=Jean-Marc Zingg
 maintainer=Jean-Marc Zingg
 sentence=Arduino Display Library for SPI E-Paper displays from Dalian Good Display and Waveshare. 

+ 1 - 1
src/GxEPD2_3C.h

@@ -116,7 +116,7 @@ class GxEPD2_3C : public Adafruit_GFX
     }
 
     // init method with additional parameters:
-    // initial true for re-init after processor deep sleep wake up, if display power supply was kept
+    // initial false for re-init after processor deep sleep wake up, if display power supply was kept
     // only relevant for b/w displays with fast partial update
     // pulldown_rst_mode true for alternate RST handling to avoid feeding 5V through RST pin
     void init(uint32_t serial_diag_bitrate, bool initial, bool pulldown_rst_mode = false)

+ 1 - 1
src/GxEPD2_BW.h

@@ -124,7 +124,7 @@ class GxEPD2_BW : public Adafruit_GFX
     }
 
     // init method with additional parameters:
-    // initial true for re-init after processor deep sleep wake up, if display power supply was kept
+    // initial false for re-init after processor deep sleep wake up, if display power supply was kept
     // this can be used to avoid the repeated initial full refresh on displays with fast partial update
     // NOTE: garbage will result on fast partial update displays, if initial full update is omitted after power loss
     // pulldown_rst_mode true for alternate RST handling to avoid feeding 5V through RST pin

+ 1 - 1
src/GxEPD2_GFX.h

@@ -24,7 +24,7 @@ class GxEPD2_GFX : public Adafruit_GFX
     virtual bool mirror(bool m) = 0;
     virtual void init(uint32_t serial_diag_bitrate = 0) = 0; // serial_diag_bitrate = 0 : disabled
     // init method with additional parameters:
-    // initial true for re-init after processor deep sleep wake up, if display power supply was kept
+    // initial false for re-init after processor deep sleep wake up, if display power supply was kept
     // this can be used to avoid the repeated initial full refresh on displays with fast partial update
     // NOTE: garbage will result on fast partial update displays, if initial full update is omitted after power loss
     // pulldown_rst_mode true for alternate RST handling to avoid feeding 5V through RST pin

+ 47 - 52
src/epd/GxEPD2_260.cpp

@@ -230,10 +230,10 @@ void GxEPD2_260::refresh(int16_t x, int16_t y, int16_t w, int16_t h)
   w1 -= x1 - x;
   h1 -= y1 - y;
   if (!_using_partial_mode) _Init_Part();
-  _writeCommand(0x91); // partial in
+  if (usePartialUpdateWindow) _writeCommand(0x91); // partial in
   _setPartialRamArea(x1, y1, w1, h1);
   _Update_Part();
-  _writeCommand(0x92); // partial out
+  if (usePartialUpdateWindow) _writeCommand(0x92); // partial out
 }
 
 void GxEPD2_260::powerOff(void)
@@ -308,10 +308,14 @@ void GxEPD2_260::_InitDisplay()
   _writeData (HEIGHT / 256);
   _writeData (HEIGHT % 256);
   _writeCommand(0x82); // vcom_DC setting
-  //_writeData (0x08);
-  _writeData (0x00);
+  //_writeData (0x00);   // -0.1
+  //_writeData (0x08);   // -0.1 + 8 * -0.05 = -0.5V from demo
+  //_writeData (0x12);   // -0.1 + 18 * -0.05 = -1.0V from OTP, slightly better
+  _writeData (0x1c);   // -0.1 + 28 * -0.05 = -1.5V test, better
+  //_writeData (0x26);   // -0.1 + 38 * -0.05 = -2.0V test, same
+  //_writeData (0x30);   // -0.1 + 48 * -0.05 = -2.5V test, darker
   _writeCommand(0x50); //VCOM AND DATA INTERVAL SETTING
-  _writeData(0x97);    //WBmode:VBDF 17|D7 VBDW 97 VBDB 57   WBRmode:VBDF F7 VBDW 77 VBDB 37  VBDR B7
+  _writeData(0x17);    //WBmode:VBDF 17|D7 VBDW 97 VBDB 57   WBRmode:VBDF F7 VBDW 77 VBDB 37  VBDR B7
 }
 
 const unsigned char GxEPD2_260::lut_20_vcom0_full[] PROGMEM =
@@ -370,62 +374,53 @@ const unsigned char GxEPD2_260::lut_24_bb_full[] PROGMEM =
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 };
 
-//partial screen update LUT
-//#define Tx19 0x19 // original value is 25 (phase length)
-#define Tx19 0x30   // new value for test is 48 (phase length)
+// partial update waveform
+
+// same waveform as by demo code from Good Display
+//#define T1  0 // color change charge balance pre-phase
+//#define T2  0 // color change or sustain charge balance pre-phase
+//#define T3 25 // color change or sustain phase
+//#define T4  0 // color change phase
+
+// new waveform created by Jean-Marc Zingg for actual panel
+#define T1 60 // color change charge balance pre-phase
+#define T2  2 // color change or sustain charge balance pre-phase
+#define T3  6 // color change or sustain phase
+#define T4 60 // color change phase
+
+// for new waveform without sustain phase: uncomment next 2 lines, good enough for fat fonts
+#define T2  0 // color change or sustain charge balance pre-phase
+#define T3  0 // color change or sustain phase
+
+// "balanced flash once" variant
+//#define T1  0 // color change charge balance pre-phase
+//#define T2 60 // color change or sustain charge balance pre-phase
+//#define T3 60 // color change or sustain phase
+//#define T4  0 // color change phase
+
 const unsigned char GxEPD2_260::lut_20_vcom0_partial[] PROGMEM =
 {
-  0x00, Tx19, 0x01, 0x00, 0x00, 0x01,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, T1, T2, T3, T4, 1, // 00 00 00 00
 };
 
 const unsigned char GxEPD2_260::lut_21_ww_partial[] PROGMEM =
-{
-    0x00, Tx19, 0x01, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+{ // 10 w
+  0x18, T1, T2, T3, T4, 1, // 00 01 10 00
 };
 
 const unsigned char GxEPD2_260::lut_22_bw_partial[] PROGMEM =
-{
-    0x80, Tx19, 0x01, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+{ // 10 w
+  0x5A, T1, T2, T3, T4, 1, // 01 01 10 10
 };
 
 const unsigned char GxEPD2_260::lut_23_wb_partial[] PROGMEM =
-{
-    0x40, Tx19, 0x01, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+{ // 01 b
+  0xA5, T1, T2, T3, T4, 1, // 10 10 01 01
 };
 
 const unsigned char GxEPD2_260::lut_24_bb_partial[] PROGMEM =
-{
-    0x00, Tx19, 0x01, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+{ // 01 b
+  0x24, T1, T2, T3, T4, 1, // 00 10 01 00
 };
 
 void GxEPD2_260::_Init_Full()
@@ -449,15 +444,15 @@ void GxEPD2_260::_Init_Part()
 {
   _InitDisplay();
   _writeCommand(0x20);
-  _writeDataPGM(lut_20_vcom0_partial, sizeof(lut_20_vcom0_partial));
+  _writeDataPGM(lut_20_vcom0_partial, sizeof(lut_20_vcom0_partial), 44 - sizeof(lut_20_vcom0_partial));
   _writeCommand(0x21);
-  _writeDataPGM(lut_21_ww_partial, sizeof(lut_21_ww_partial));
+  _writeDataPGM(lut_21_ww_partial, sizeof(lut_21_ww_partial), 42 - sizeof(lut_21_ww_partial));
   _writeCommand(0x22);
-  _writeDataPGM(lut_22_bw_partial, sizeof(lut_22_bw_partial));
+  _writeDataPGM(lut_22_bw_partial, sizeof(lut_22_bw_partial), 42 - sizeof(lut_22_bw_partial));
   _writeCommand(0x23);
-  _writeDataPGM(lut_23_wb_partial, sizeof(lut_23_wb_partial));
+  _writeDataPGM(lut_23_wb_partial, sizeof(lut_23_wb_partial), 42 - sizeof(lut_23_wb_partial));
   _writeCommand(0x24);
-  _writeDataPGM(lut_24_bb_partial, sizeof(lut_24_bb_partial));
+  _writeDataPGM(lut_24_bb_partial, sizeof(lut_24_bb_partial), 42 - sizeof(lut_24_bb_partial));
   _PowerOn();
   _using_partial_mode = true;
 }

+ 2 - 1
src/epd/GxEPD2_260.h

@@ -25,11 +25,12 @@ class GxEPD2_260 : public GxEPD2_EPD
     static const GxEPD2::Panel panel = GxEPD2::GDEW026T0;
     static const bool hasColor = false;
     static const bool hasPartialUpdate = true;
+    static const bool usePartialUpdateWindow = false; // set false for better image
     static const bool hasFastPartialUpdate = true;
     static const uint16_t power_on_time = 60; // ms, e.g. 58296us
     static const uint16_t power_off_time = 20; // ms, e.g. 19748us
     static const uint16_t full_refresh_time = 1600; // ms, e.g. 1543246us
-    static const uint16_t partial_refresh_time = 600; // ms, e.g. 514116us
+    static const uint16_t partial_refresh_time = 1300; // ms, e.g. 1228991us
     // constructor
     GxEPD2_260(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     // methods (virtual)

+ 75 - 7
src/epd/GxEPD2_371.cpp

@@ -230,10 +230,10 @@ void GxEPD2_371::refresh(int16_t x, int16_t y, int16_t w, int16_t h)
   w1 -= x1 - x;
   h1 -= y1 - y;
   if (!_using_partial_mode) _Init_Part();
-  _writeCommand(0x91); // partial in
+  if (usePartialUpdateWindow) _writeCommand(0x91); // partial in
   _setPartialRamArea(x1, y1, w1, h1);
   _Update_Part();
-  _writeCommand(0x92); // partial out
+  if (usePartialUpdateWindow) _writeCommand(0x92); // partial out
 }
 
 void GxEPD2_371::powerOff(void)
@@ -264,7 +264,7 @@ void GxEPD2_371::_setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t
   _writeData(y % 256);
   _writeData(ye / 256);
   _writeData(ye % 256);
-  _writeData(0x00);
+  _writeData(0x01);
 }
 
 void GxEPD2_371::_PowerOn()
@@ -281,6 +281,8 @@ void GxEPD2_371::_PowerOff()
 {
   if (_power_is_on)
   {
+    _writeCommand(0x03); // power off sequence
+    _writeData(0x30);
     _writeCommand(0x02); // power off
     _waitWhileBusy("_PowerOff", power_off_time);
   }
@@ -291,25 +293,74 @@ void GxEPD2_371::_PowerOff()
 void GxEPD2_371::_InitDisplay()
 {
   if (_hibernating) _reset();
+  _writeCommand(0x01); // power setting
+  _writeData (0x07);
+  _writeData (0x07);   // VGH=20V,VGL=-20V
+  _writeData (0x3f);   // VDH=15V
+  _writeData (0x3f);   // VDL=-15V
   _writeCommand(0x06); // boost soft start
   _writeData (0x17);   // A
   _writeData (0x17);   // B
   _writeData (0x1d);   // C
   _PowerOn();
   _writeCommand(0x00); // panel setting
-  _writeData(0x1f);    // LUT from OTP, 128x296
+  _writeData(0x1f);    // LUT from OTP
   _writeCommand(0x61); // resolution setting
   _writeData (WIDTH);
   _writeData (HEIGHT / 256);
   _writeData (HEIGHT % 256);
-  _writeCommand(0x50);
-  _writeData (0x23);
-  _writeData (0x07);
+  _writeCommand(0x82); // vcom_DC setting
+  _writeData (0x1C);
+  _writeCommand(0x50); // VCOM AND DATA INTERVAL SETTING
+  _writeData(0x29);    // LUTKW, N2OCP: copy new to old
+  _writeData(0x07);
 }
 
+// experimental partial screen update LUTs with balanced charge
+// LUTs are filled with zeroes
+
+#define T1 35 // charge balance pre-phase
+#define T2  0 // optional extension
+#define T3 35 // color change phase (b/w)
+#define T4  0 // optional extension for one color
+
+const unsigned char GxEPD2_371::lut_20_LUTC_partial[] PROGMEM =
+{
+  0x00, T1, T2, T3, T4, 1, // 00 00 00 00
+};
+
+const unsigned char GxEPD2_371::lut_21_LUTWW_partial[] PROGMEM =
+{ // 10 w
+  0x00, T1, T2, T3, T4, 1, // 00 00 00 00
+};
+
+const unsigned char GxEPD2_371::lut_22_LUTKW_partial[] PROGMEM =
+{ // 10 w
+  0x48, T1, T2, T3, T4, 1, // 01 00 10 00
+  //0x5A, T1, T2, T3, T4, 1, // 01 01 10 10 more white
+};
+
+const unsigned char GxEPD2_371::lut_23_LUTWK_partial[] PROGMEM =
+{ // 01 b
+  0x84, T1, T2, T3, T4, 1, // 10 00 01 00
+  //0xA5, T1, T2, T3, T4, 1, // 10 10 01 01 more black
+};
+
+const unsigned char GxEPD2_371::lut_24_LUTKK_partial[] PROGMEM =
+{ // 01 b
+  0x00, T1, T2, T3, T4, 1, // 00 00 00 00
+};
+
+const unsigned char GxEPD2_371::lut_25_LUTBD_partial[] PROGMEM =
+{
+  0x00, T1, T2, T3, T4, 1, // 00 00 00 00
+};
+
 void GxEPD2_371::_Init_Full()
 {
   _InitDisplay();
+  _writeCommand(0x00); // panel setting
+  _writeData(0x1f);    // full update LUT from OTP
   _PowerOn();
   _using_partial_mode = false;
 }
@@ -317,6 +368,23 @@ void GxEPD2_371::_Init_Full()
 void GxEPD2_371::_Init_Part()
 {
   _InitDisplay();
+  _writeCommand(0x00); //panel setting
+  _writeData(hasFastPartialUpdate ? 0x3f : 0x1f); // partial update LUT from registers
+  _writeCommand(0x50); // VCOM AND DATA INTERVAL SETTING
+  _writeData(0x39);    // LUTBD, N2OCP: copy new to old
+  _writeData(0x07);
+  _writeCommand(0x20);
+  _writeDataPGM(lut_20_LUTC_partial, sizeof(lut_20_LUTC_partial), 42 - sizeof(lut_20_LUTC_partial));
+  _writeCommand(0x21);
+  _writeDataPGM(lut_21_LUTWW_partial, sizeof(lut_21_LUTWW_partial), 42 - sizeof(lut_21_LUTWW_partial));
+  _writeCommand(0x22);
+  _writeDataPGM(lut_22_LUTKW_partial, sizeof(lut_22_LUTKW_partial), 42 - sizeof(lut_22_LUTKW_partial));
+  _writeCommand(0x23);
+  _writeDataPGM(lut_23_LUTWK_partial, sizeof(lut_23_LUTWK_partial), 42 - sizeof(lut_23_LUTWK_partial));
+  _writeCommand(0x24);
+  _writeDataPGM(lut_24_LUTKK_partial, sizeof(lut_24_LUTKK_partial), 42 - sizeof(lut_24_LUTKK_partial));
+  _writeCommand(0x25);
+  _writeDataPGM(lut_25_LUTBD_partial, sizeof(lut_25_LUTBD_partial), 42 - sizeof(lut_25_LUTBD_partial));
   _PowerOn();
   _using_partial_mode = true;
 }

+ 15 - 2
src/epd/GxEPD2_371.h

@@ -25,11 +25,12 @@ class GxEPD2_371 : public GxEPD2_EPD
     static const GxEPD2::Panel panel = GxEPD2::GDEW0371W7;
     static const bool hasColor = false;
     static const bool hasPartialUpdate = true;
-    static const bool hasFastPartialUpdate = false;
+    static const bool usePartialUpdateWindow = false; // set false for better image
+    static const bool hasFastPartialUpdate = true; // set this false to force full refresh always
     static const uint16_t power_on_time = 200; // ms, e.g. 126267us
     static const uint16_t power_off_time = 50; // ms, e.g. 39563us
     static const uint16_t full_refresh_time = 3000; // ms, e.g. 2953630us
-    static const uint16_t partial_refresh_time = 3000; // ms, e.g. 2953630us
+    static const uint16_t partial_refresh_time = 1500; // ms, e.g. 1482735us
     // constructor
     GxEPD2_371(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     // methods (virtual)
@@ -43,6 +44,11 @@ class GxEPD2_371 : public GxEPD2_EPD
     void writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
     void writeImagePart(const uint8_t* black, const uint8_t* color, int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
                         int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
+    // for differential update: set current and previous buffers equal (for fast partial update to work correctly)
+    // done by controller (N2OCP); override with empty code
+    void writeImageAgain(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) {};
+    void writeImagePartAgain(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
+                             int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false) {};
     // write sprite of native data to controller memory, without screen refresh; x and w should be multiple of 8
     void writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);
     // write to controller memory, with screen refresh; x and w should be multiple of 8
@@ -67,6 +73,13 @@ class GxEPD2_371 : public GxEPD2_EPD
     void _Init_Part();
     void _Update_Full();
     void _Update_Part();
+  private:
+    static const unsigned char lut_20_LUTC_partial[];
+    static const unsigned char lut_21_LUTWW_partial[];
+    static const unsigned char lut_22_LUTKW_partial[];
+    static const unsigned char lut_23_LUTWK_partial[];
+    static const unsigned char lut_24_LUTKK_partial[];
+    static const unsigned char lut_25_LUTBD_partial[];
 };
 
 #endif

+ 68 - 64
src/epd/GxEPD2_420.cpp

@@ -229,10 +229,10 @@ void GxEPD2_420::refresh(int16_t x, int16_t y, int16_t w, int16_t h)
   w1 -= x1 - x;
   h1 -= y1 - y;
   if (!_using_partial_mode) _Init_Part();
-  _writeCommand(0x91); // partial in
+  if (usePartialUpdateWindow) _writeCommand(0x91); // partial in
   _setPartialRamArea(x1, y1, w1, h1);
   _Update_Part();
-  _writeCommand(0x92); // partial out
+  if (usePartialUpdateWindow) _writeCommand(0x92); // partial out
 }
 
 void GxEPD2_420::powerOff(void)
@@ -290,13 +290,31 @@ void GxEPD2_420::_PowerOff()
 void GxEPD2_420::_InitDisplay()
 {
   if (_hibernating) _reset();
-  _writeCommand(0x06); // boost
-  _writeData(0x17);
-  _writeData(0x17);
-  _writeData(0x17);
-  _writeCommand(0x00);
-  //_writeData(0x1f); // LUT from OTP Pixel with B/W.
-  _writeData(0x3F); //300x400 B/W mode, LUT set by register
+  _writeCommand(0x01); // POWER SETTING
+  _writeData (0x03);   // VDS_EN, VDG_EN internal
+  _writeData (0x00);   // VCOM_HV, VGHL_LV=16V
+  _writeData (0x2b);   // VDH=11V
+  _writeData (0x2b);   // VDL=11V
+  _writeCommand(0x06); // boost soft start
+  _writeData (0x17);   // A
+  _writeData (0x17);   // B
+  _writeData (0x17);   // C
+  _writeCommand(0x00); // panel setting
+  _writeData(0x3f);    // 300x400 B/W mode, LUT set by register
+  _writeCommand(0x30); // PLL setting
+  _writeData (0x3a);   // 3a 100HZ   29 150Hz 39 200HZ 31 171HZ
+  _writeCommand(0x61); // resolution setting
+  _writeData (WIDTH / 256);
+  _writeData (WIDTH % 256);
+  _writeData (HEIGHT / 256);
+  _writeData (HEIGHT % 256);
+  _writeCommand(0x82); // vcom_DC setting
+  //_writeData (0x08);   // -0.1 + 8 * -0.05 = -0.5V from demo
+  _writeData (0x12);   // -0.1 + 18 * -0.05 = -1.0V from OTP, slightly better
+  //_writeData (0x1c);   // -0.1 + 28 * -0.05 = -1.5V test, worse
+  _writeCommand(0x50); // VCOM AND DATA INTERVAL SETTING
+  //_writeData(0x97);    // WBmode:VBDF 17|D7 VBDW 97 VBDB 57   WBRmode:VBDF F7 VBDW 77 VBDB 37  VBDR B7
+  _writeData(0xd7);    // border floating to avoid flashing
 }
 
 const unsigned char GxEPD2_420::lut_20_vcom0_full[] PROGMEM =
@@ -355,72 +373,58 @@ const unsigned char GxEPD2_420::lut_24_bb_full[] PROGMEM =
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 };
 
-// original wavetable from GxEPD, optimized for the display I have (modified Ben Krasnow version)
-//#define TP0A  2 // sustain phase for bb and ww, change phase for bw and wb
-//#define TP0B 45 // change phase for bw and wb
+// partial update waveform
+
+// same waveform as by demo code from Good Display
+//#define T1  0 // color change charge balance pre-phase
+//#define T2  0 // color change or sustain charge balance pre-phase
+//#define T3  0 // color change or sustain phase
+//#define T4 25 // color change phase
+
+// new waveform created by Jean-Marc Zingg for the actual panel
+#define T1 25 // color change charge balance pre-phase
+#define T2  1 // color change or sustain charge balance pre-phase
+#define T3  2 // color change or sustain phase
+#define T4 25 // color change phase
+
+// for new waveform without sustain phase: uncomment next 2 lines, not good for fat fonts
+//#define T2  0 // color change or sustain charge balance pre-phase // 0 no sustain
+//#define T3  0 // color change or sustain phase // 0 no sustain
 
-// same waveform as demo wavetable from Good Display:
-#define TP0A  0   // sustain phase for bb and ww, change phase for bw and wb
-#define TP0B 0x19 // change phase for bw and wb
+// "balanced flash once" variant
+//#define T1  0 // color change charge balance pre-phase
+//#define T2 25 // color change or sustain charge balance pre-phase
+//#define T3 25 // color change or sustain phase
+//#define T4  0 // color change phase
 
 const unsigned char GxEPD2_420::lut_20_vcom0_partial[] PROGMEM =
 {
-  0x00,
-  TP0A, TP0B, 0x01, 0x00, 0x01,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, T1, T2, T3, T4, 1, // 00 00 00 00
+  0x00,  1,  0,  0,  0, 1, // gnd phase
 };
 
 const unsigned char GxEPD2_420::lut_21_ww_partial[] PROGMEM =
-{
-  0x80, // 10 00 00 00
-  TP0A, TP0B, 0x01, 0x00, 0x01,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+{ // 10 w
+  0x18, T1, T2, T3, T4, 1, // 00 01 10 00
+  0x00,  1,  0,  0,  0, 1, // gnd phase
 };
 
 const unsigned char GxEPD2_420::lut_22_bw_partial[] PROGMEM =
-{
-  0xA0, // 10 10 00 00
-  TP0A, TP0B, 0x01, 0x00, 0x01,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+{ // 10 w
+  0x5A, T1, T2, T3, T4, 1, // 01 01 10 10
+  0x00,  1,  0,  0,  0, 1, // gnd phase
 };
 
 const unsigned char GxEPD2_420::lut_23_wb_partial[] PROGMEM =
-{
-  0x50, // 01 01 00 00
-  TP0A, TP0B, 0x01, 0x00, 0x01,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+{ // 01 b
+  0xA5, T1, T2, T3, T4, 1, // 10 10 01 01
+  0x00,  1,  0,  0,  0, 1, // gnd phase
 };
 
 const unsigned char GxEPD2_420::lut_24_bb_partial[] PROGMEM =
-{
-  0x40, // 01 00 00 00
-  TP0A, TP0B, 0x01, 0x00, 0x01,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+{ // 01 b
+  0x24, T1, T2, T3, T4, 1, // 00 10 01 00
+  0x00,  1,  0,  0,  0, 1, // gnd phase
 };
 
 void GxEPD2_420::_Init_Full()
@@ -444,15 +448,15 @@ void GxEPD2_420::_Init_Part()
 {
   _InitDisplay();
   _writeCommand(0x20);
-  _writeDataPGM(lut_20_vcom0_partial, sizeof(lut_20_vcom0_partial));
+  _writeDataPGM(lut_20_vcom0_partial, sizeof(lut_20_vcom0_partial), 44 - sizeof(lut_20_vcom0_partial));
   _writeCommand(0x21);
-  _writeDataPGM(lut_21_ww_partial, sizeof(lut_21_ww_partial));
+  _writeDataPGM(lut_21_ww_partial, sizeof(lut_21_ww_partial), 42 - sizeof(lut_21_ww_partial));
   _writeCommand(0x22);
-  _writeDataPGM(lut_22_bw_partial, sizeof(lut_22_bw_partial));
+  _writeDataPGM(lut_22_bw_partial, sizeof(lut_22_bw_partial), 42 - sizeof(lut_22_bw_partial));
   _writeCommand(0x23);
-  _writeDataPGM(lut_23_wb_partial, sizeof(lut_23_wb_partial));
+  _writeDataPGM(lut_23_wb_partial, sizeof(lut_23_wb_partial), 42 - sizeof(lut_23_wb_partial));
   _writeCommand(0x24);
-  _writeDataPGM(lut_24_bb_partial, sizeof(lut_24_bb_partial));
+  _writeDataPGM(lut_24_bb_partial, sizeof(lut_24_bb_partial), 42 - sizeof(lut_24_bb_partial));
   _PowerOn();
   _using_partial_mode = true;
 }

+ 4 - 3
src/epd/GxEPD2_420.h

@@ -24,11 +24,12 @@ class GxEPD2_420 : public GxEPD2_EPD
     static const GxEPD2::Panel panel = GxEPD2::GDEW042T2;
     static const bool hasColor = false;
     static const bool hasPartialUpdate = true;
+    static const bool usePartialUpdateWindow = false; // set false for better image
     static const bool hasFastPartialUpdate = true;
     static const uint16_t power_on_time = 40; // ms, e.g. 36996us
-    static const uint16_t power_off_time = 42; // ms, e.g. 40026us
-    static const uint16_t full_refresh_time = 4200; // ms, e.g. 4108541us
-    static const uint16_t partial_refresh_time = 1000; // ms, e.g. 995320us
+    static const uint16_t power_off_time = 20; // ms, e.g. 19285us
+    static const uint16_t full_refresh_time = 1600; // ms, e.g. 1545659us
+    static const uint16_t partial_refresh_time = 600; // ms, e.g. 563754us
     // constructor
     GxEPD2_420(int8_t cs, int8_t dc, int8_t rst, int8_t busy);
     // methods (virtual)