Paperd.Ink Library 0.0.5
Library for interacting with Paperd.Ink devices.
Loading...
Searching...
No Matches
GxEPD2_213_T5D.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// Controller: UC8151D : https://v4.cecdn.yun300.cn/100001_1909185148/UC8151D.pdf
6//
7// Author: Jean-Marc Zingg
8//
9// Version: see library.properties
10//
11// Library: https://github.com/ZinggJM/GxEPD2
12
13#include "GxEPD2_213_T5D.h"
14
15GxEPD2_213_T5D::GxEPD2_213_T5D(int16_t cs, int16_t dc, int16_t rst, int16_t busy) :
16 GxEPD2_EPD(cs, dc, rst, busy, LOW, 10000000, WIDTH, HEIGHT, panel, hasColor, hasPartialUpdate, hasFastPartialUpdate)
17{
18}
19
20void GxEPD2_213_T5D::clearScreen(uint8_t value)
21{
22 writeScreenBuffer(value);
23 refresh(true);
25}
26
28{
29 if (!_using_partial_mode) _Init_Part();
30 if (_initial_write) _writeScreenBuffer(0x10, value); // set previous
31 _writeScreenBuffer(0x13, value); // set current
32 _initial_write = false; // initial full screen buffer clean done
33}
34
36{
37 if (!_using_partial_mode) _Init_Part();
38 _writeScreenBuffer(0x13, value); // set current
39}
40
41void GxEPD2_213_T5D::_writeScreenBuffer(uint8_t command, uint8_t value)
42{
43 _writeCommand(command);
44 for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
45 {
46 _writeData(value);
47 }
48}
49
50void GxEPD2_213_T5D::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
51{
52 _writeImage(0x13, bitmap, x, y, w, h, invert, mirror_y, pgm);
53}
54
55void GxEPD2_213_T5D::writeImageForFullRefresh(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
56{
57 _writeImage(0x10, bitmap, x, y, w, h, invert, mirror_y, pgm);
58 _writeImage(0x13, bitmap, x, y, w, h, invert, mirror_y, pgm);
59}
60
61void GxEPD2_213_T5D::writeImageAgain(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
62{
63 _writeImage(0x13, bitmap, x, y, w, h, invert, mirror_y, pgm);
64}
65
66void GxEPD2_213_T5D::_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)
67{
68 if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
69 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
70 int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
71 x -= x % 8; // byte boundary
72 w = wb * 8; // byte boundary
73 int16_t x1 = x < 0 ? 0 : x; // limit
74 int16_t y1 = y < 0 ? 0 : y; // limit
75 int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
76 int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
77 int16_t dx = x1 - x;
78 int16_t dy = y1 - y;
79 w1 -= dx;
80 h1 -= dy;
81 if ((w1 <= 0) || (h1 <= 0)) return;
82 if (!_using_partial_mode) _Init_Part();
83 _writeCommand(0x91); // partial in
84 _setPartialRamArea(x1, y1, w1, h1);
85 _writeCommand(command);
86 for (int16_t i = 0; i < h1; i++)
87 {
88 for (int16_t j = 0; j < w1 / 8; j++)
89 {
90 uint8_t data;
91 // use wb, h of bitmap for index!
92 int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
93 if (pgm)
94 {
95#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
96 data = pgm_read_byte(&bitmap[idx]);
97#else
98 data = bitmap[idx];
99#endif
100 }
101 else
102 {
103 data = bitmap[idx];
104 }
105 if (invert) data = ~data;
106 _writeData(data);
107 }
108 }
109 _writeCommand(0x92); // partial out
110 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
111}
112
113void GxEPD2_213_T5D::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_213_T5D::writeImagePartAgain(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 _writeImagePart(0x13, bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
123}
124
125void GxEPD2_213_T5D::_writeImagePart(uint8_t command, const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
126 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
127{
128 if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
129 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
130 if ((w_bitmap < 0) || (h_bitmap < 0) || (w < 0) || (h < 0)) return;
131 if ((x_part < 0) || (x_part >= w_bitmap)) return;
132 if ((y_part < 0) || (y_part >= h_bitmap)) return;
133 int16_t wb_bitmap = (w_bitmap + 7) / 8; // width bytes, bitmaps are padded
134 x_part -= x_part % 8; // byte boundary
135 w = w_bitmap - x_part < w ? w_bitmap - x_part : w; // limit
136 h = h_bitmap - y_part < h ? h_bitmap - y_part : h; // limit
137 x -= x % 8; // byte boundary
138 w = 8 * ((w + 7) / 8); // byte boundary, bitmaps are padded
139 int16_t x1 = x < 0 ? 0 : x; // limit
140 int16_t y1 = y < 0 ? 0 : y; // limit
141 int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
142 int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
143 int16_t dx = x1 - x;
144 int16_t dy = y1 - y;
145 w1 -= dx;
146 h1 -= dy;
147 if ((w1 <= 0) || (h1 <= 0)) return;
148 if (!_using_partial_mode) _Init_Part();
149 _writeCommand(0x91); // partial in
150 _setPartialRamArea(x1, y1, w1, h1);
151 _writeCommand(command);
152 for (int16_t i = 0; i < h1; i++)
153 {
154 for (int16_t j = 0; j < w1 / 8; j++)
155 {
156 uint8_t data;
157 // use wb_bitmap, h_bitmap of bitmap for index!
158 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;
159 if (pgm)
160 {
161#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
162 data = pgm_read_byte(&bitmap[idx]);
163#else
164 data = bitmap[idx];
165#endif
166 }
167 else
168 {
169 data = bitmap[idx];
170 }
171 if (invert) data = ~data;
172 _writeData(data);
173 }
174 }
175 _writeCommand(0x92); // partial out
176 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
177}
178
179void GxEPD2_213_T5D::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)
180{
181 if (black)
182 {
183 writeImage(black, x, y, w, h, invert, mirror_y, pgm);
184 }
185}
186
187void GxEPD2_213_T5D::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,
188 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
189{
190 if (black)
191 {
192 writeImagePart(black, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
193 }
194}
195
196void GxEPD2_213_T5D::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)
197{
198 if (data1)
199 {
200 writeImage(data1, x, y, w, h, invert, mirror_y, pgm);
201 }
202}
203
204void GxEPD2_213_T5D::drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
205{
206 writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
207 refresh(x, y, w, h);
208 writeImageAgain(bitmap, x, y, w, h, invert, mirror_y, pgm);
209}
210
211void GxEPD2_213_T5D::drawImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
212 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
213{
214 writeImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
215 refresh(x, y, w, h);
216 writeImagePartAgain(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
217}
218
219void GxEPD2_213_T5D::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)
220{
221 if (black)
222 {
223 drawImage(black, x, y, w, h, invert, mirror_y, pgm);
224 }
225}
226
227void GxEPD2_213_T5D::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,
228 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
229{
230 if (black)
231 {
232 drawImagePart(black, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
233 }
234}
235
236void GxEPD2_213_T5D::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)
237{
238 if (data1)
239 {
240 drawImage(data1, x, y, w, h, invert, mirror_y, pgm);
241 }
242}
243
244void GxEPD2_213_T5D::refresh(bool partial_update_mode)
245{
246 if (partial_update_mode) refresh(0, 0, WIDTH, HEIGHT);
247 else
248 {
249 if (_using_partial_mode) _Init_Full();
250 _Update_Full();
251 _initial_refresh = false; // initial full update done
252 }
253}
254
255void GxEPD2_213_T5D::refresh(int16_t x, int16_t y, int16_t w, int16_t h)
256{
257 if (_initial_refresh) return refresh(false); // initial update needs be full update
258 // intersection with screen
259 int16_t w1 = x < 0 ? w + x : w; // reduce
260 int16_t h1 = y < 0 ? h + y : h; // reduce
261 int16_t x1 = x < 0 ? 0 : x; // limit
262 int16_t y1 = y < 0 ? 0 : y; // limit
263 w1 = x1 + w1 < int16_t(WIDTH) ? w1 : int16_t(WIDTH) - x1; // limit
264 h1 = y1 + h1 < int16_t(HEIGHT) ? h1 : int16_t(HEIGHT) - y1; // limit
265 if ((w1 <= 0) || (h1 <= 0)) return;
266 // make x1, w1 multiple of 8
267 w1 += x1 % 8;
268 if (w1 % 8 > 0) w1 += 8 - w1 % 8;
269 x1 -= x1 % 8;
270 if (!_using_partial_mode) _Init_Part();
271 _writeCommand(0x91); // partial in
272 _setPartialRamArea(x1, y1, w1, h1);
273 _Update_Part();
274 _writeCommand(0x92); // partial out
275}
276
278{
279 _PowerOff();
280}
281
283{
284 _PowerOff();
285 if (_rst >= 0)
286 {
287 _writeCommand(0x07); // deep sleep
288 _writeData(0xA5); // check code
289 _hibernating = true;
290 }
291}
292
293void GxEPD2_213_T5D::_setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
294{
295 uint16_t xe = (x + w - 1) | 0x0007; // byte boundary inclusive (last byte)
296 uint16_t ye = y + h - 1;
297 x &= 0xFFF8; // byte boundary
298 _writeCommand(0x90); // partial window
299 //_writeData(x / 256);
300 _writeData(x % 256);
301 //_writeData(xe / 256);
302 _writeData(xe % 256);
303 _writeData(y / 256);
304 _writeData(y % 256);
305 _writeData(ye / 256);
306 _writeData(ye % 256);
307 _writeData(0x01); // don't see any difference
308 //_writeData(0x00); // don't see any difference
309}
310
311void GxEPD2_213_T5D::_PowerOn()
312{
313 if (!_power_is_on)
314 {
315 _writeCommand(0x04);
316 _waitWhileBusy("_PowerOn", power_on_time);
317 }
318 _power_is_on = true;
319}
320
321void GxEPD2_213_T5D::_PowerOff()
322{
323 _writeCommand(0x02); // power off
324 _waitWhileBusy("_PowerOff", power_off_time);
325 _power_is_on = false;
326 _using_partial_mode = false;
327}
328
329void GxEPD2_213_T5D::_InitDisplay()
330{
331 if (_hibernating) _reset();
332 _writeCommand(0x00); // panel setting
333 _writeData(0x1f); // LUT from OTP, 128x296
334 _writeCommand(0x61); //resolution setting
336 _writeData (HEIGHT >> 8);
337 _writeData (HEIGHT & 0xFF);
338 _writeCommand(0x50); // VCOM AND DATA INTERVAL SETTING
339 _writeData(0x97); // WBmode:VBDF 17|D7 VBDW 97 VBDB 57 WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7
340}
341
342//partial screen update LUT
343//#define Tx19 0x19 // original value is 25 (phase length)
344#define Tx19 0x20 // new value for test is 32 (phase length)
345const unsigned char GxEPD2_213_T5D::lut_20_vcomDC_partial[] PROGMEM =
346{
347 0x00, Tx19, 0x01, 0x00, 0x00, 0x01,
348 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
349 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
350 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
351 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
352 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
353 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
354 0x00, 0x00,
355};
356
357const unsigned char GxEPD2_213_T5D::lut_21_ww_partial[] PROGMEM =
358{
359 0x00, Tx19, 0x01, 0x00, 0x00, 0x01,
360 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
361 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
362 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
363 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
364 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
365 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
366};
367
368const unsigned char GxEPD2_213_T5D::lut_22_bw_partial[] PROGMEM =
369{
370 0x80, Tx19, 0x01, 0x00, 0x00, 0x01,
371 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
372 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
373 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
374 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
375 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
376 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
377};
378
379const unsigned char GxEPD2_213_T5D::lut_23_wb_partial[] PROGMEM =
380{
381 0x40, Tx19, 0x01, 0x00, 0x00, 0x01,
382 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
383 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
384 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
385 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
386 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
387 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
388};
389
390const unsigned char GxEPD2_213_T5D::lut_24_bb_partial[] PROGMEM =
391{
392 0x00, Tx19, 0x01, 0x00, 0x00, 0x01,
393 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
394 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
395 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
396 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
397 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
398 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
399};
400
401void GxEPD2_213_T5D::_Init_Full()
402{
403 _InitDisplay();
404 _PowerOn();
405 _using_partial_mode = false;
406}
407
408void GxEPD2_213_T5D::_Init_Part()
409{
410 _InitDisplay();
411 _writeCommand(0x00); //panel setting
412 _writeData(hasFastPartialUpdate ? 0xbf : 0x1f); // for test with OTP LUT
413 _writeCommand(0x82); //vcom_DC setting
414 _writeData (0x08);
415 _writeCommand(0x50);
416 _writeData(0x17); //WBmode:VBDF 17|D7 VBDW 97 VBDB 57 WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7
417 _writeCommand(0x20);
418 _writeDataPGM(lut_20_vcomDC_partial, sizeof(lut_20_vcomDC_partial));
419 _writeCommand(0x21);
420 _writeDataPGM(lut_21_ww_partial, sizeof(lut_21_ww_partial));
421 _writeCommand(0x22);
422 _writeDataPGM(lut_22_bw_partial, sizeof(lut_22_bw_partial));
423 _writeCommand(0x23);
424 _writeDataPGM(lut_23_wb_partial, sizeof(lut_23_wb_partial));
425 _writeCommand(0x24);
426 _writeDataPGM(lut_24_bb_partial, sizeof(lut_24_bb_partial));
427 _PowerOn();
428 _using_partial_mode = true;
429}
430
431void GxEPD2_213_T5D::_Update_Full()
432{
433 _writeCommand(0x12); //display refresh
434 _waitWhileBusy("_Update_Full", full_refresh_time);
435}
436
437void GxEPD2_213_T5D::_Update_Part()
438{
439 _writeCommand(0x12); //display refresh
440 _waitWhileBusy("_Update_Part", partial_refresh_time);
441}
const unsigned char GxEPD2_213_T5D::lut_20_vcomDC_partial[] PROGMEM
#define Tx19
static const uint16_t HEIGHT
void writeScreenBufferAgain(uint8_t value=0xFF)
static const bool hasFastPartialUpdate
static const uint16_t full_refresh_time
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 clearScreen(uint8_t value=0xFF)
GxEPD2_213_T5D(int16_t cs, int16_t dc, int16_t rst, int16_t busy)
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 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 writeScreenBuffer(uint8_t value=0xFF)
static const uint16_t power_off_time
static const uint16_t WIDTH
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 uint16_t power_on_time
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)
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 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 refresh(bool partial_update_mode=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 partial_refresh_time
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 _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 _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)