Paperd.Ink Library 0.0.5
Library for interacting with Paperd.Ink devices.
Loading...
Searching...
No Matches
GxEPD2_213_B72.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: IL3897 :
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_B72.h"
14
15GxEPD2_213_B72::GxEPD2_213_B72(int16_t cs, int16_t dc, int16_t rst, int16_t busy) :
16 GxEPD2_EPD(cs, dc, rst, busy, HIGH, 10000000, WIDTH, HEIGHT, panel, hasColor, hasPartialUpdate, hasFastPartialUpdate)
17{
18}
19
20void GxEPD2_213_B72::clearScreen(uint8_t value)
21{
22 writeScreenBuffer(value);
23 refresh(true);
25}
26
28{
29 _initial_write = false; // initial full screen buffer clean done
30 if (!_using_partial_mode) _Init_Part();
31 _setPartialRamArea(0, 0, WIDTH, HEIGHT);
32 _writeCommand(0x24);
33 for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
34 {
35 _writeData(value);
36 }
37 if (_initial_refresh) writeScreenBufferAgain(value); // init "old data"
38}
39
41{
42 if (!_using_partial_mode) _Init_Part();
43 _setPartialRamArea(0, 0, WIDTH, HEIGHT);
44 _writeCommand(0x26);
45 for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
46 {
47 _writeData(value);
48 }
49}
50
51void GxEPD2_213_B72::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(0x24, bitmap, x, y, w, h, invert, mirror_y, pgm);
54}
55
56void GxEPD2_213_B72::writeImageAgain(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(0x26, bitmap, x, y, w, h, invert, mirror_y, pgm);
59}
60
61void GxEPD2_213_B72::_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)
62{
63 if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
64 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
65 int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
66 x -= x % 8; // byte boundary
67 w = wb * 8; // byte boundary
68 int16_t x1 = x < 0 ? 0 : x; // limit
69 int16_t y1 = y < 0 ? 0 : y; // limit
70 int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
71 int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
72 int16_t dx = x1 - x;
73 int16_t dy = y1 - y;
74 w1 -= dx;
75 h1 -= dy;
76 if ((w1 <= 0) || (h1 <= 0)) return;
77 if (!_using_partial_mode) _Init_Part();
78 _setPartialRamArea(x1, y1, w1, h1);
79 _writeCommand(command);
80 for (int16_t i = 0; i < h1; i++)
81 {
82 for (int16_t j = 0; j < w1 / 8; j++)
83 {
84 uint8_t data;
85 // use wb, h of bitmap for index!
86 int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
87 if (pgm)
88 {
89#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
90 data = pgm_read_byte(&bitmap[idx]);
91#else
92 data = bitmap[idx];
93#endif
94 }
95 else
96 {
97 data = bitmap[idx];
98 }
99 if (invert) data = ~data;
100 _writeData(data);
101 }
102 }
103 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
104}
105
106void GxEPD2_213_B72::writeImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
107 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
108{
109 _writeImagePart(0x24, bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
110}
111
112void GxEPD2_213_B72::writeImagePartAgain(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
113 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
114{
115 _writeImagePart(0x26, bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
116}
117
118void GxEPD2_213_B72::_writeImagePart(uint8_t command, const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
119 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
120{
121 if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
122 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
123 if ((w_bitmap < 0) || (h_bitmap < 0) || (w < 0) || (h < 0)) return;
124 if ((x_part < 0) || (x_part >= w_bitmap)) return;
125 if ((y_part < 0) || (y_part >= h_bitmap)) return;
126 int16_t wb_bitmap = (w_bitmap + 7) / 8; // width bytes, bitmaps are padded
127 x_part -= x_part % 8; // byte boundary
128 w = w_bitmap - x_part < w ? w_bitmap - x_part : w; // limit
129 h = h_bitmap - y_part < h ? h_bitmap - y_part : h; // limit
130 x -= x % 8; // byte boundary
131 w = 8 * ((w + 7) / 8); // byte boundary, bitmaps are padded
132 int16_t x1 = x < 0 ? 0 : x; // limit
133 int16_t y1 = y < 0 ? 0 : y; // limit
134 int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
135 int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
136 int16_t dx = x1 - x;
137 int16_t dy = y1 - y;
138 w1 -= dx;
139 h1 -= dy;
140 if ((w1 <= 0) || (h1 <= 0)) return;
141 if (!_using_partial_mode) _Init_Part();
142 _setPartialRamArea(x1, y1, w1, h1);
143 _writeCommand(command);
144 for (int16_t i = 0; i < h1; i++)
145 {
146 for (int16_t j = 0; j < w1 / 8; j++)
147 {
148 uint8_t data;
149 // use wb_bitmap, h_bitmap of bitmap for index!
150 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;
151 if (pgm)
152 {
153#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
154 data = pgm_read_byte(&bitmap[idx]);
155#else
156 data = bitmap[idx];
157#endif
158 }
159 else
160 {
161 data = bitmap[idx];
162 }
163 if (invert) data = ~data;
164 _writeData(data);
165 }
166 }
167 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
168}
169
170void GxEPD2_213_B72::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)
171{
172 if (black)
173 {
174 writeImage(black, x, y, w, h, invert, mirror_y, pgm);
175 }
176}
177
178void GxEPD2_213_B72::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,
179 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 writeImagePart(black, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
184 }
185}
186
187void GxEPD2_213_B72::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)
188{
189 if (data1)
190 {
191 writeImage(data1, x, y, w, h, invert, mirror_y, pgm);
192 }
193}
194
195void GxEPD2_213_B72::drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
196{
197 writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
198 refresh(x, y, w, h);
199 writeImageAgain(bitmap, x, y, w, h, invert, mirror_y, pgm);
200}
201
202void GxEPD2_213_B72::drawImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
203 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
204{
205 writeImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
206 refresh(x, y, w, h);
207 writeImagePartAgain(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
208}
209
210void GxEPD2_213_B72::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)
211{
212 if (black)
213 {
214 drawImage(black, x, y, w, h, invert, mirror_y, pgm);
215 }
216}
217
218void GxEPD2_213_B72::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,
219 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 drawImagePart(black, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
224 }
225}
226
227void GxEPD2_213_B72::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)
228{
229 if (data1)
230 {
231 drawImage(data1, x, y, w, h, invert, mirror_y, pgm);
232 }
233}
234
235void GxEPD2_213_B72::refresh(bool partial_update_mode)
236{
237 if (partial_update_mode) refresh(0, 0, WIDTH, HEIGHT);
238 else
239 {
240 if (_using_partial_mode) _Init_Full();
241 _Update_Full();
242 _initial_refresh = false; // initial full update done
243 }
244}
245
246void GxEPD2_213_B72::refresh(int16_t x, int16_t y, int16_t w, int16_t h)
247{
248 if (_initial_refresh) return refresh(false); // initial update needs be full update
249 // intersection with screen
250 int16_t w1 = x < 0 ? w + x : w; // reduce
251 int16_t h1 = y < 0 ? h + y : h; // reduce
252 int16_t x1 = x < 0 ? 0 : x; // limit
253 int16_t y1 = y < 0 ? 0 : y; // limit
254 w1 = x1 + w1 < int16_t(WIDTH) ? w1 : int16_t(WIDTH) - x1; // limit
255 h1 = y1 + h1 < int16_t(HEIGHT) ? h1 : int16_t(HEIGHT) - y1; // limit
256 if ((w1 <= 0) || (h1 <= 0)) return;
257 // make x1, w1 multiple of 8
258 w1 += x1 % 8;
259 if (w1 % 8 > 0) w1 += 8 - w1 % 8;
260 x1 -= x1 % 8;
261 if (!_using_partial_mode) _Init_Part();
262 _setPartialRamArea(x1, y1, w1, h1);
263 _Update_Part();
264}
265
267{
268 _PowerOff();
269}
270
272{
273 _PowerOff();
274 if (_rst >= 0)
275 {
276 _writeCommand(0x10); // deep sleep mode
277 _writeData(0x1); // enter deep sleep
278 _hibernating = true;
279 }
280}
281
282void GxEPD2_213_B72::_setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
283{
284 _writeCommand(0x11); // set ram entry mode
285 _writeData(0x03); // x increase, y increase : normal mode
286 _writeCommand(0x44);
287 _writeData(x / 8);
288 _writeData((x + w - 1) / 8);
289 _writeCommand(0x45);
290 _writeData(y % 256);
291 _writeData(y / 256);
292 _writeData((y + h - 1) % 256);
293 _writeData((y + h - 1) / 256);
294 _writeCommand(0x4e);
295 _writeData(x / 8);
296 _writeCommand(0x4f);
297 _writeData(y % 256);
298 _writeData(y / 256);
299}
300
301void GxEPD2_213_B72::_PowerOn()
302{
303 if (!_power_is_on)
304 {
305 _writeCommand(0x22);
306 _writeData(0xc0);
307 _writeCommand(0x20);
308 _waitWhileBusy("_PowerOn", power_on_time);
309 }
310 _power_is_on = true;
311}
312
313void GxEPD2_213_B72::_PowerOff()
314{
315 _writeCommand(0x22);
316 _writeData(0xc3);
317 _writeCommand(0x20);
318 _waitWhileBusy("_PowerOff", power_off_time);
319 _power_is_on = false;
320 _using_partial_mode = false;
321}
322
323void GxEPD2_213_B72::_InitDisplay()
324{
325 if (_hibernating) _reset();
326 _writeCommand(0x74); //set analog block control
327 _writeData(0x54);
328 _writeCommand(0x7E); //set digital block control
329 _writeData(0x3B);
330 _writeCommand(0x01); //Driver output control
331 _writeData(0xF9); // (HEIGHT - 1) % 256
332 _writeData(0x00); // (HEIGHT - 1) / 256
333 _writeData(0x00);
334 _writeCommand(0x3C); //BorderWavefrom
335 _writeData(0x03);
336 _writeCommand(0x2C); //VCOM Voltage
337 _writeData(0x70); // NA ??
338 _writeCommand(0x03); //Gate Driving voltage Control
339 _writeData(0x15); // 19V
340 _writeCommand(0x04); //Source Driving voltage Control
341 _writeData(0x41); // VSH1 15V
342 _writeData(0xA8); // VSH2 5V
343 _writeData(0x32); // VSL -15V
344 _writeCommand(0x3A); //Dummy Line
345 _writeData(0x30);
346 _writeCommand(0x3B); //Gate time
347 _writeData(0x0A);
348 _setPartialRamArea(0, 0, WIDTH, HEIGHT);
349}
350
351const uint8_t GxEPD2_213_B72::LUT_DATA_full[] PROGMEM =
352{
353 0x80, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, //LUT0: BB: VS 0 ~7
354 0x10, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, //LUT1: BW: VS 0 ~7
355 0x80, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, //LUT2: WB: VS 0 ~7
356 0x10, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, //LUT3: WW: VS 0 ~7
357 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT4: VCOM: VS 0 ~7
358 0x03, 0x03, 0x00, 0x00, 0x02, // TP0 A~D RP0
359 0x09, 0x09, 0x00, 0x00, 0x02, // TP1 A~D RP1
360 0x03, 0x03, 0x00, 0x00, 0x02, // TP2 A~D RP2
361 0x00, 0x00, 0x00, 0x00, 0x00, // TP3 A~D RP3
362 0x00, 0x00, 0x00, 0x00, 0x00, // TP4 A~D RP4
363 0x00, 0x00, 0x00, 0x00, 0x00, // TP5 A~D RP5
364 0x00, 0x00, 0x00, 0x00, 0x00, // TP6 A~D RP6
365};
366
367const uint8_t GxEPD2_213_B72::LUT_DATA_part[] PROGMEM =
368{
369 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT0: BB: VS 0 ~7
370 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT1: BW: VS 0 ~7
371 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT2: WB: VS 0 ~7
372 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT3: WW: VS 0 ~7
373 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT4: VCOM: VS 0 ~7
374 0x0A, 0x00, 0x00, 0x00, 0x00, // TP0 A~D RP0
375 0x00, 0x00, 0x00, 0x00, 0x00, // TP1 A~D RP1
376 0x00, 0x00, 0x00, 0x00, 0x00, // TP2 A~D RP2
377 0x00, 0x00, 0x00, 0x00, 0x00, // TP3 A~D RP3
378 0x00, 0x00, 0x00, 0x00, 0x00, // TP4 A~D RP4
379 0x00, 0x00, 0x00, 0x00, 0x00, // TP5 A~D RP5
380 0x00, 0x00, 0x00, 0x00, 0x00, // TP6 A~D RP6
381};
382
383void GxEPD2_213_B72::_Init_Full()
384{
385 _InitDisplay();
386 _writeCommand(0x32);
387 _writeDataPGM(LUT_DATA_full, sizeof(LUT_DATA_full));
388 _PowerOn();
389 _using_partial_mode = false;
390}
391
392void GxEPD2_213_B72::_Init_Part()
393{
394 _InitDisplay();
395 _writeCommand(0x2C); //VCOM Voltage
396 _writeData(0x26); // NA ??
397 _writeCommand(0x32);
398 _writeDataPGM(LUT_DATA_part, sizeof(LUT_DATA_part));
399 _PowerOn();
400 _using_partial_mode = true;
401}
402
403void GxEPD2_213_B72::_Update_Full()
404{
405 _writeCommand(0x22);
406 _writeData(0xc4);
407 _writeCommand(0x20);
408 _waitWhileBusy("_Update_Full", full_refresh_time);
409}
410
411void GxEPD2_213_B72::_Update_Part()
412{
413 _writeCommand(0x22);
414 _writeData(0x04);
415 _writeCommand(0x20);
416 _waitWhileBusy("_Update_Part", partial_refresh_time);
417}
const uint8_t GxEPD2_213_B72::LUT_DATA_full[] PROGMEM
static const uint16_t WIDTH
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 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)
static const uint16_t power_on_time
static const uint16_t partial_refresh_time
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)
static const uint16_t full_refresh_time
void clearScreen(uint8_t value=0xFF)
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)
GxEPD2_213_B72(int16_t cs, int16_t dc, int16_t rst, int16_t busy)
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)
void refresh(bool partial_update_mode=false)
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)
static const uint16_t HEIGHT
static const uint16_t power_off_time
void writeScreenBuffer(uint8_t value=0xFF)
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 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 writeScreenBufferAgain(uint8_t value=0xFF)
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)