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