Paperd.Ink Library 0.0.5
Library for interacting with Paperd.Ink devices.
Loading...
Searching...
No Matches
GxEPD2_290_T94_V2.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, available here: http://www.e-paper-display.com/download_detail/downloadsId=806.html
5// Panel: GDEM029T94 : https://www.good-display.com/product/360.html, Waveshare variant without partial wft in OTP
6// Controller : SSD1680 : https://www.good-display.com/companyfile/101.html
7// Display: Waveshare 2.9" b/w V2 : https://www.waveshare.com/product/displays/e-paper/2.9inch-e-paper-module.htm
8//
9// Author: Jean-Marc Zingg
10//
11// Version: see library.properties
12//
13// Library: https://github.com/ZinggJM/GxEPD2
14//
15// note: this is a driver class variant for the Waveshare V2 board with controller without partial update wft in OTP
16
17#include "GxEPD2_290_T94_V2.h"
18
19GxEPD2_290_T94_V2::GxEPD2_290_T94_V2(int16_t cs, int16_t dc, int16_t rst, int16_t busy) :
20 GxEPD2_EPD(cs, dc, rst, busy, HIGH, 10000000, WIDTH, HEIGHT, panel, hasColor, hasPartialUpdate, hasFastPartialUpdate)
21{
22}
23
25{
26 writeScreenBuffer(value);
27 refresh(true);
29}
30
32{
33 if (!_using_partial_mode) _Init_Part();
34 if (_initial_write) _writeScreenBuffer(0x26, value); // set previous
35 _writeScreenBuffer(0x24, value); // set current
36 _initial_write = false; // initial full screen buffer clean done
37}
38
40{
41 if (!_using_partial_mode) _Init_Part();
42 _writeScreenBuffer(0x26, value); // set previous
43 _writeScreenBuffer(0x24, value); // set current
44}
45
46void GxEPD2_290_T94_V2::_writeScreenBuffer(uint8_t command, uint8_t value)
47{
48 _writeCommand(command);
49 for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
50 {
51 _writeData(value);
52 }
53}
54
55void GxEPD2_290_T94_V2::writeImage(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(0x24, bitmap, x, y, w, h, invert, mirror_y, pgm);
58}
59
60void GxEPD2_290_T94_V2::writeImageForFullRefresh(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
61{
62 _writeImage(0x26, bitmap, x, y, w, h, invert, mirror_y, pgm); // set previous
63 _writeImage(0x24, bitmap, x, y, w, h, invert, mirror_y, pgm);
64}
65
66
67void GxEPD2_290_T94_V2::writeImageAgain(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 _writeImage(0x26, bitmap, x, y, w, h, invert, mirror_y, pgm); // set previous
70 _writeImage(0x24, bitmap, x, y, w, h, invert, mirror_y, pgm); // set current
71}
72
73void GxEPD2_290_T94_V2::_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)
74{
75 if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
76 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
77 int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
78 x -= x % 8; // byte boundary
79 w = wb * 8; // byte boundary
80 int16_t x1 = x < 0 ? 0 : x; // limit
81 int16_t y1 = y < 0 ? 0 : y; // limit
82 int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
83 int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
84 int16_t dx = x1 - x;
85 int16_t dy = y1 - y;
86 w1 -= dx;
87 h1 -= dy;
88 if ((w1 <= 0) || (h1 <= 0)) return;
89 if (!_using_partial_mode) _Init_Part();
90 _setPartialRamArea(x1, y1, w1, h1);
91 _writeCommand(command);
92 for (int16_t i = 0; i < h1; i++)
93 {
94 for (int16_t j = 0; j < w1 / 8; j++)
95 {
96 uint8_t data;
97 // use wb, h of bitmap for index!
98 int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
99 if (pgm)
100 {
101#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
102 data = pgm_read_byte(&bitmap[idx]);
103#else
104 data = bitmap[idx];
105#endif
106 }
107 else
108 {
109 data = bitmap[idx];
110 }
111 if (invert) data = ~data;
112 _writeData(data);
113 }
114 }
115 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
116}
117
118void GxEPD2_290_T94_V2::writeImagePart(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 _writeImagePart(0x24, bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
122}
123
124void GxEPD2_290_T94_V2::writeImagePartAgain(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
125 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
126{
127 _writeImagePart(0x26, bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm); // set previous
128 _writeImagePart(0x24, bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm); // set current
129}
130
131void GxEPD2_290_T94_V2::_writeImagePart(uint8_t command, const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
132 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
133{
134 if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
135 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
136 if ((w_bitmap < 0) || (h_bitmap < 0) || (w < 0) || (h < 0)) return;
137 if ((x_part < 0) || (x_part >= w_bitmap)) return;
138 if ((y_part < 0) || (y_part >= h_bitmap)) return;
139 int16_t wb_bitmap = (w_bitmap + 7) / 8; // width bytes, bitmaps are padded
140 x_part -= x_part % 8; // byte boundary
141 w = w_bitmap - x_part < w ? w_bitmap - x_part : w; // limit
142 h = h_bitmap - y_part < h ? h_bitmap - y_part : h; // limit
143 x -= x % 8; // byte boundary
144 w = 8 * ((w + 7) / 8); // byte boundary, bitmaps are padded
145 int16_t x1 = x < 0 ? 0 : x; // limit
146 int16_t y1 = y < 0 ? 0 : y; // limit
147 int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
148 int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
149 int16_t dx = x1 - x;
150 int16_t dy = y1 - y;
151 w1 -= dx;
152 h1 -= dy;
153 if ((w1 <= 0) || (h1 <= 0)) return;
154 if (!_using_partial_mode) _Init_Part();
155 _setPartialRamArea(x1, y1, w1, h1);
156 _writeCommand(command);
157 for (int16_t i = 0; i < h1; i++)
158 {
159 for (int16_t j = 0; j < w1 / 8; j++)
160 {
161 uint8_t data;
162 // use wb_bitmap, h_bitmap of bitmap for index!
163 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;
164 if (pgm)
165 {
166#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
167 data = pgm_read_byte(&bitmap[idx]);
168#else
169 data = bitmap[idx];
170#endif
171 }
172 else
173 {
174 data = bitmap[idx];
175 }
176 if (invert) data = ~data;
177 _writeData(data);
178 }
179 }
180 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
181}
182
183void GxEPD2_290_T94_V2::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)
184{
185 if (black)
186 {
187 writeImage(black, x, y, w, h, invert, mirror_y, pgm);
188 }
189}
190
191void GxEPD2_290_T94_V2::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,
192 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
193{
194 if (black)
195 {
196 writeImagePart(black, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
197 }
198}
199
200void GxEPD2_290_T94_V2::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)
201{
202 if (data1)
203 {
204 writeImage(data1, x, y, w, h, invert, mirror_y, pgm);
205 }
206}
207
208void GxEPD2_290_T94_V2::drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
209{
210 writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
211 refresh(x, y, w, h);
212 writeImageAgain(bitmap, x, y, w, h, invert, mirror_y, pgm);
213}
214
215void GxEPD2_290_T94_V2::drawImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
216 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
217{
218 writeImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
219 refresh(x, y, w, h);
220 writeImagePartAgain(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
221}
222
223void GxEPD2_290_T94_V2::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)
224{
225 if (black)
226 {
227 drawImage(black, x, y, w, h, invert, mirror_y, pgm);
228 }
229}
230
231void GxEPD2_290_T94_V2::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,
232 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
233{
234 if (black)
235 {
236 drawImagePart(black, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
237 }
238}
239
240void GxEPD2_290_T94_V2::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)
241{
242 if (data1)
243 {
244 drawImage(data1, x, y, w, h, invert, mirror_y, pgm);
245 }
246}
247
248void GxEPD2_290_T94_V2::refresh(bool partial_update_mode)
249{
250 if (partial_update_mode) refresh(0, 0, WIDTH, HEIGHT);
251 else
252 {
253 if (_using_partial_mode) _Init_Full();
254 _Update_Full();
255 _initial_refresh = false; // initial full update done
256 }
257}
258
259void GxEPD2_290_T94_V2::refresh(int16_t x, int16_t y, int16_t w, int16_t h)
260{
261 if (_initial_refresh) return refresh(false); // initial update needs be full update
262 // intersection with screen
263 int16_t w1 = x < 0 ? w + x : w; // reduce
264 int16_t h1 = y < 0 ? h + y : h; // reduce
265 int16_t x1 = x < 0 ? 0 : x; // limit
266 int16_t y1 = y < 0 ? 0 : y; // limit
267 w1 = x1 + w1 < int16_t(WIDTH) ? w1 : int16_t(WIDTH) - x1; // limit
268 h1 = y1 + h1 < int16_t(HEIGHT) ? h1 : int16_t(HEIGHT) - y1; // limit
269 if ((w1 <= 0) || (h1 <= 0)) return;
270 // make x1, w1 multiple of 8
271 w1 += x1 % 8;
272 if (w1 % 8 > 0) w1 += 8 - w1 % 8;
273 x1 -= x1 % 8;
274 if (!_using_partial_mode) _Init_Part();
275 _setPartialRamArea(x1, y1, w1, h1);
276 _Update_Part();
277}
278
280{
281 _PowerOff();
282}
283
285{
286 _PowerOff();
287 if (_rst >= 0)
288 {
289 _writeCommand(0x10); // deep sleep mode
290 _writeData(0x1); // enter deep sleep
291 _hibernating = true;
292 }
293}
294
295void GxEPD2_290_T94_V2::_setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
296{
297 _writeCommand(0x11); // set ram entry mode
298 _writeData(0x03); // x increase, y increase : normal mode
299 _writeCommand(0x44);
300 _writeData(x / 8);
301 _writeData((x + w - 1) / 8);
302 _writeCommand(0x45);
303 _writeData(y % 256);
304 _writeData(y / 256);
305 _writeData((y + h - 1) % 256);
306 _writeData((y + h - 1) / 256);
307 _writeCommand(0x4e);
308 _writeData(x / 8);
309 _writeCommand(0x4f);
310 _writeData(y % 256);
311 _writeData(y / 256);
312}
313
314void GxEPD2_290_T94_V2::_PowerOn()
315{
316 if (!_power_is_on)
317 {
318 _writeCommand(0x22);
319 _writeData(0xf8);
320 _writeCommand(0x20);
321 _waitWhileBusy("_PowerOn", power_on_time);
322 }
323 _power_is_on = true;
324}
325
326void GxEPD2_290_T94_V2::_PowerOff()
327{
328 if (_power_is_on)
329 {
330 _writeCommand(0x22);
331 _writeData(0x83);
332 _writeCommand(0x20);
333 _waitWhileBusy("_PowerOff", power_off_time);
334 }
335 _power_is_on = false;
336 _using_partial_mode = false;
337}
338
339void GxEPD2_290_T94_V2::_InitDisplay()
340{
341 if (_hibernating) _reset();
342 delay(10); // 10ms according to specs
343 _writeCommand(0x12); //SWRESET
344 delay(10); // 10ms according to specs
345 _writeCommand(0x01); //Driver output control
346 _writeData(0x27);
347 _writeData(0x01);
348 _writeData(0x00);
349 _writeCommand(0x11); //data entry mode
350 _writeData(0x03);
351 _writeCommand(0x3C); //BorderWavefrom
352 _writeData(0x05);
353 _writeCommand(0x21); // Display update control
354 _writeData(0x00);
355 _writeData(0x80);
356 _writeCommand(0x18); //Read built-in temperature sensor
357 _writeData(0x80);
358 _setPartialRamArea(0, 0, WIDTH, HEIGHT);
359}
360
361const unsigned char GxEPD2_290_T94_V2::lut_partial[] PROGMEM =
362{
363 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
364 0x80, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
365 0x40, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
366 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
367 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
368 0x0A, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
369 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
370 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
371 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
372 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
373 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
374 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
375 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
376 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
377 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
378 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
379 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
380 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0,
381};
382
383void GxEPD2_290_T94_V2::_Init_Full()
384{
385 _InitDisplay();
386 _PowerOn();
387 _using_partial_mode = false;
388}
389
390void GxEPD2_290_T94_V2::_Init_Part()
391{
392 _InitDisplay();
393 _writeCommand(0x32);
394 _writeDataPGM(lut_partial, sizeof(lut_partial));
395 _PowerOn();
396 _using_partial_mode = true;
397}
398
399void GxEPD2_290_T94_V2::_Update_Full()
400{
401 _writeCommand(0x22);
402 _writeData(0xf4);
403 _writeCommand(0x20);
404 _waitWhileBusy("_Update_Full", full_refresh_time);
405}
406
407void GxEPD2_290_T94_V2::_Update_Part()
408{
409 _writeCommand(0x22);
410 _writeData(0xcc);
411 _writeCommand(0x20);
412 _waitWhileBusy("_Update_Part", partial_refresh_time);
413}
const unsigned char GxEPD2_290_T94_V2::lut_partial[] PROGMEM
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)
void writeScreenBuffer(uint8_t value=0xFF)
static const uint16_t WIDTH
static const uint16_t power_off_time
GxEPD2_290_T94_V2(int16_t cs, int16_t dc, int16_t rst, int16_t busy)
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 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)
static const uint16_t HEIGHT
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 refresh(bool partial_update_mode=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)
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)
static const uint16_t power_on_time
void writeScreenBufferAgain(uint8_t value=0xFF)
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)
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 full_refresh_time
static const uint16_t partial_refresh_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)