Paperd.Ink Library 0.0.5
Library for interacting with Paperd.Ink devices.
Loading...
Searching...
No Matches
GxEPD2_102.cpp
Go to the documentation of this file.
1// Display Library for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
2// Requires HW SPI and Adafruit_GFX. Caution: these e-papers require 3.3V supply AND data lines!
3//
4// based on Demo Example from Good Display: http://www.e-paper-display.com/download_list/downloadcategoryid=34&isMode=false.html
5// Panel: GDEW0102T4 : https://www.good-display.com/product/207.html
6// Controller: UC8175 : https://v4.cecdn.yun300.cn/100001_1909185148/UC8175-1.pdf
7//
8// Author: Jean-Marc Zingg
9//
10// Version: see library.properties
11//
12// Library: https://github.com/ZinggJM/GxEPD2
13
14#include "GxEPD2_102.h"
15
16GxEPD2_102::GxEPD2_102(int16_t cs, int16_t dc, int16_t rst, int16_t busy) :
17 GxEPD2_EPD(cs, dc, rst, busy, LOW, 10000000, WIDTH, HEIGHT, panel, hasColor, hasPartialUpdate, hasFastPartialUpdate)
18{
19}
20
21void GxEPD2_102::clearScreen(uint8_t value)
22{
23 writeScreenBuffer(value);
24 refresh(true);
25 writeScreenBuffer(value);
26}
27
29{
30 _initial_write = false; // initial full screen buffer clean done
31 if (!_using_partial_mode) _Init_Part();
33 {
34 _writeCommand(0x10); // init old data
36 for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
37 {
38 _transfer(value);
39 }
41 }
42 _writeCommand(0x13);
44 for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
45 {
46 _transfer(value);
47 }
49}
50
51void GxEPD2_102::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
52{
53 _writeImage(0x13, bitmap, x, y, w, h, invert, mirror_y, pgm);
54}
55
56void GxEPD2_102::writeImageForFullRefresh(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
57{
58 _writeImage(0x10, bitmap, x, y, w, h, invert, mirror_y, pgm);
59 _writeImage(0x13, bitmap, x, y, w, h, invert, mirror_y, pgm);
60}
61
62void GxEPD2_102::_writeImage(uint8_t command, const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
63{
64 if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
65 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
66 int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
67 x -= x % 8; // byte boundary
68 w = wb * 8; // byte boundary
69 int16_t x1 = x < 0 ? 0 : x; // limit
70 int16_t y1 = y < 0 ? 0 : y; // limit
71 int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
72 int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
73 int16_t dx = x1 - x;
74 int16_t dy = y1 - y;
75 w1 -= dx;
76 h1 -= dy;
77 if ((w1 <= 0) || (h1 <= 0)) return;
78 if (!_using_partial_mode) _Init_Part();
79 //uint32_t start = micros();
80 _writeCommand(0x91); // partial in
81 _setPartialRamArea(x1, y1, w1, h1);
82 _writeCommand(command);
84 for (int16_t i = 0; i < h1; i++)
85 {
86 for (int16_t j = 0; j < w1 / 8; j++)
87 {
88 uint8_t data;
89 // use wb, h of bitmap for index!
90 int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
91 if (pgm)
92 {
93#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
94 data = pgm_read_byte(&bitmap[idx]);
95#else
96 data = bitmap[idx];
97#endif
98 }
99 else
100 {
101 data = bitmap[idx];
102 }
103 if (invert) data = ~data;
104 _transfer(data);
105 }
106 }
107 _endTransfer();
108 _writeCommand(0x92); // partial out
109 //Serial.print("GxEPD2_102::writeImage took "); Serial.println(micros() - start);
110 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
111}
112
113void GxEPD2_102::writeImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
114 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
115{
116 _writeImagePart(0x13, bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
117}
118
119void GxEPD2_102::_writeImagePart(uint8_t command, const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
120 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
121{
122 if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
123 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
124 if ((w_bitmap < 0) || (h_bitmap < 0) || (w < 0) || (h < 0)) return;
125 if ((x_part < 0) || (x_part >= w_bitmap)) return;
126 if ((y_part < 0) || (y_part >= h_bitmap)) return;
127 int16_t wb_bitmap = (w_bitmap + 7) / 8; // width bytes, bitmaps are padded
128 x_part -= x_part % 8; // byte boundary
129 w = w_bitmap - x_part < w ? w_bitmap - x_part : w; // limit
130 h = h_bitmap - y_part < h ? h_bitmap - y_part : h; // limit
131 x -= x % 8; // byte boundary
132 w = 8 * ((w + 7) / 8); // byte boundary, bitmaps are padded
133 int16_t x1 = x < 0 ? 0 : x; // limit
134 int16_t y1 = y < 0 ? 0 : y; // limit
135 int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
136 int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
137 int16_t dx = x1 - x;
138 int16_t dy = y1 - y;
139 w1 -= dx;
140 h1 -= dy;
141 if ((w1 <= 0) || (h1 <= 0)) return;
142 if (!_using_partial_mode) _Init_Part();
143 _writeCommand(0x91); // partial in
144 _setPartialRamArea(x1, y1, w1, h1);
145 _writeCommand(command);
147 for (int16_t i = 0; i < h1; i++)
148 {
149 for (int16_t j = 0; j < w1 / 8; j++)
150 {
151 uint8_t data;
152 // use wb_bitmap, h_bitmap of bitmap for index!
153 int16_t idx = mirror_y ? x_part / 8 + j + dx / 8 + ((h_bitmap - 1 - (y_part + i + dy))) * wb_bitmap : x_part / 8 + j + dx / 8 + (y_part + i + dy) * wb_bitmap;
154 if (pgm)
155 {
156#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
157 data = pgm_read_byte(&bitmap[idx]);
158#else
159 data = bitmap[idx];
160#endif
161 }
162 else
163 {
164 data = bitmap[idx];
165 }
166 if (invert) data = ~data;
167 _transfer(data);
168 }
169 }
170 _endTransfer();
171 _writeCommand(0x92); // partial out
172 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
173}
174
175void GxEPD2_102::writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
176{
177 if (black)
178 {
179 writeImage(black, x, y, w, h, invert, mirror_y, pgm);
180 }
181}
182
183void GxEPD2_102::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,
184 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
185{
186 if (black)
187 {
188 writeImagePart(black, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
189 }
190}
191
192void GxEPD2_102::writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
193{
194 if (data1)
195 {
196 writeImage(data1, x, y, w, h, invert, mirror_y, pgm);
197 }
198}
199
200void GxEPD2_102::drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
201{
202 writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
203 refresh(x, y, w, h);
204 writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
205}
206
207void GxEPD2_102::drawImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
208 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
209{
210 writeImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
211 refresh(x, y, w, h);
212 writeImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
213}
214
215void GxEPD2_102::drawImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
216{
217 writeImage(black, color, x, y, w, h, invert, mirror_y, pgm);
218 refresh(x, y, w, h);
219 writeImage(black, color, x, y, w, h, invert, mirror_y, pgm);
220}
221
222void GxEPD2_102::drawImagePart(const uint8_t* black, const uint8_t* color, int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
223 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
224{
225 writeImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
226 refresh(x, y, w, h);
227 writeImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
228}
229
230void GxEPD2_102::drawNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
231{
232 writeNative(data1, data2, x, y, w, h, invert, mirror_y, pgm);
233 refresh(x, y, w, h);
234 writeNative(data1, data2, x, y, w, h, invert, mirror_y, pgm);
235}
236
237void GxEPD2_102::refresh(bool partial_update_mode)
238{
239 if (partial_update_mode) refresh(0, 0, WIDTH, HEIGHT);
240 else
241 {
242 if (_using_partial_mode) _Init_Full();
243 _Update_Full();
244 _initial_refresh = false; // initial full update done
245 }
246}
247
248void GxEPD2_102::refresh(int16_t x, int16_t y, int16_t w, int16_t h)
249{
250 if (_initial_refresh) return refresh(false); // initial update needs be full update
251 // intersection with screen
252 int16_t w1 = x < 0 ? w + x : w; // reduce
253 int16_t h1 = y < 0 ? h + y : h; // reduce
254 int16_t x1 = x < 0 ? 0 : x; // limit
255 int16_t y1 = y < 0 ? 0 : y; // limit
256 w1 = x1 + w1 < int16_t(WIDTH) ? w1 : int16_t(WIDTH) - x1; // limit
257 h1 = y1 + h1 < int16_t(HEIGHT) ? h1 : int16_t(HEIGHT) - y1; // limit
258 if ((w1 <= 0) || (h1 <= 0)) return;
259 // make x1, w1 multiple of 8
260 w1 += x1 % 8;
261 if (w1 % 8 > 0) w1 += 8 - w1 % 8;
262 x1 -= x1 % 8;
263 if (!_using_partial_mode) _Init_Part();
264 if (usePartialUpdateWindow) _writeCommand(0x91); // partial in
265 _setPartialRamArea(x1, y1, w1, h1);
266 _Update_Part();
267 if (usePartialUpdateWindow) _writeCommand(0x92); // partial out
268}
269
271{
272 _PowerOff();
273}
274
276{
277 _PowerOff();
278 if (_rst >= 0)
279 {
280 _writeCommand(0x07); // deep sleep
281 _writeData(0xA5); // check code
282 _hibernating = true;
283 }
284}
285
286void GxEPD2_102::_setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
287{
288 uint16_t xe = (x + w - 1) | 0x0007; // byte boundary inclusive (last byte)
289 uint16_t ye = y + h - 1;
290 x &= 0xFFF8; // byte boundary
291 _writeCommand(0x90); // partial window
292 _writeData(x);
293 _writeData(xe);
294 _writeData(y);
295 _writeData(ye);
296 _writeData(0x00);
297}
298
299void GxEPD2_102::_PowerOn()
300{
301 if (!_power_is_on)
302 {
303 _writeCommand(0x04);
304 _waitWhileBusy("_PowerOn", power_on_time);
305 }
306 _power_is_on = true;
307}
308
309void GxEPD2_102::_PowerOff()
310{
311 _writeCommand(0x02); // power off
312 _waitWhileBusy("_PowerOff", power_off_time);
313 _power_is_on = false;
314 _using_partial_mode = false;
315}
316
317void GxEPD2_102::_InitDisplay()
318{
319 if (_hibernating) _reset();
320 _writeCommand(0xD2); // ??
321 _writeData(0x3F);
322 _writeCommand(0x00); // Panel Setting Register
323 _writeData (useOTPforFullRefresh ? 0x4F : 0x6F); // LUT from OTP or from Registers
324 _writeCommand(0x01); // Power Setting
325 _writeData (0x03); // internal VDH/VDL VGH/VGL
326 _writeData (0x00); // VDG_LVL +15,-15
327 _writeData (0x2b); // VDH_LVL +11
328 _writeData (0x2b); // VDL_LVL -11
329 _writeCommand(0x06); // Charge Pump Setting
330 _writeData(0x3f); // 50ms, Stength 4, 8kHz
331 _writeCommand(0x2A); // LUT Option
332 _writeData(0x00); // no all gate on
333 _writeData(0x00); // 0..5 : 10s, 20..30 : 4.8s
334 _writeCommand(0x30); // PLL
335 _writeData(0x13); // 30 Hz
336 _writeCommand(0x50); // VCOM and Data interval setting
337 _writeData(0x57); // default
338 _writeCommand(0x60); // TCON
339 _writeData(0x22); // 24us
340 _writeCommand(0x61); // Resolution Setting
341 _writeData (0x50); // HRES 80
342 _writeData (0x80); // VRES 128
343 _writeCommand(0x82); // Vcom DC Setting
344 _writeData(0x12); // -1 V
345 _writeCommand(0xe3); // Power Saving
346 _writeData(0x33); //
347}
348
349const unsigned char GxEPD2_102::lut_w_full[] PROGMEM =
350{
351 0x60, 0x5A, 0x5A, 0x00, 0x00, 0x01,
352 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
353 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
354 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
355 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
356 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
357 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
358};
359
360const unsigned char GxEPD2_102::lut_b_full[] PROGMEM =
361{
362 0x90, 0x5A, 0x5A, 0x00, 0x00, 0x01,
363 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
364 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
365 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
366 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
367 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
368 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
369};
370
371const unsigned char GxEPD2_102::lut_w_partial[] PROGMEM =
372{
373 0x60, 0x01, 0x01, 0x00, 0x00, 0x01,
374 0x80, 0x0f, 0x00, 0x00, 0x00, 0x01,
375 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
376 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
377 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
378 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
379 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
380};
381
382const unsigned char GxEPD2_102::lut_b_partial[] PROGMEM =
383{
384 0x90, 0x01, 0x01, 0x00, 0x00, 0x01,
385 0x40, 0x0f, 0x00, 0x00, 0x00, 0x01,
386 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
387 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
388 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
389 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
390 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
391};
392
393void GxEPD2_102::_Init_Full()
394{
395 _InitDisplay();
396 _writeCommand(0x23);
397 _writeDataPGM(lut_w_full, sizeof(lut_w_full));
398 _writeCommand(0x24);
399 _writeDataPGM(lut_b_full, sizeof(lut_b_full));
400 _PowerOn();
401 _using_partial_mode = false;
402}
403
404void GxEPD2_102::_Init_Part()
405{
406 _InitDisplay();
407 _writeCommand(0x00); // Panel Setting Register
408 _writeData (0x6F); // LUT from Registers
409 _writeCommand(0x30); // PLL
410 _writeData(0x05); // 15Hz
411 _writeCommand(0x50); // VCOM and Data interval setting
412 _writeData(0xF2); // DDX 11 : differential, VBD 11 : vcom, CDI 2 : 5 hsync
413 _writeCommand(0x82); // Vcom DC Setting
414 _writeData(0x00); // -0.1 V
415 _writeCommand(0x23);
416 _writeDataPGM(lut_w_partial, sizeof(lut_w_partial));
417 _writeCommand(0x24);
418 _writeDataPGM(lut_b_partial, sizeof(lut_b_partial));
419 _PowerOn();
420 _using_partial_mode = true;
421}
422
423void GxEPD2_102::_Update_Full()
424{
425 _writeCommand(0x12); //display refresh
426 _waitWhileBusy("_Update_Full", full_refresh_time);
427}
428
429void GxEPD2_102::_Update_Part()
430{
431 _writeCommand(0x12); //display refresh
432 _waitWhileBusy("_Update_Part", partial_refresh_time);
433}
const unsigned char GxEPD2_102::lut_w_full[] PROGMEM
void writeImageForFullRefresh(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 refresh(bool partial_update_mode=false)
void hibernate()
void writeImagePart(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)
void clearScreen(uint8_t value=0xFF)
static const uint16_t HEIGHT
Definition GxEPD2_102.h:24
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)
void drawNative(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)
static const bool useOTPforFullRefresh
Definition GxEPD2_102.h:30
void drawImagePart(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)
void drawImage(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)
static const uint16_t full_refresh_time
Definition GxEPD2_102.h:33
void writeScreenBuffer(uint8_t value=0xFF)
GxEPD2_102(int16_t cs, int16_t dc, int16_t rst, int16_t busy)
static const uint16_t power_off_time
Definition GxEPD2_102.h:32
void writeImage(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 powerOff()
static const uint16_t power_on_time
Definition GxEPD2_102.h:31
static const uint16_t partial_refresh_time
Definition GxEPD2_102.h:34
static const uint16_t WIDTH
Definition GxEPD2_102.h:23
static const bool usePartialUpdateWindow
Definition GxEPD2_102.h:28
void _endTransfer()
void _writeCommand(uint8_t c)
void _writeData(uint8_t d)
bool _using_partial_mode
Definition GxEPD2_EPD.h:118
void _waitWhileBusy(const char *comment=0, uint16_t busy_time=5000)
bool _power_is_on
Definition GxEPD2_EPD.h:118
bool _initial_refresh
Definition GxEPD2_EPD.h:117
void _startTransfer()
void _transfer(uint8_t value)
void _reset()
bool _initial_write
Definition GxEPD2_EPD.h:117
int16_t _rst
Definition GxEPD2_EPD.h:112
bool _hibernating
Definition GxEPD2_EPD.h:118
void _writeDataPGM(const uint8_t *data, uint16_t n, int16_t fill_with_zeroes=0)