Paperd.Ink Library 0.0.5
Library for interacting with Paperd.Ink devices.
Loading...
Searching...
No Matches
GxEPD2_1160_T91.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: https://www.good-display.com/comp/xcompanyFile/downloadNew.do?appId=24&fid=553&id=374
5// Panel: GDEH116T91 : https://www.good-display.com/product/246.html
6// Controller : SSD1677 : https://v4.cecdn.yun300.cn/100001_1909185148/SSD1677.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_1160_T91.h"
15
16GxEPD2_1160_T91::GxEPD2_1160_T91(int16_t cs, int16_t dc, int16_t rst, int16_t busy) :
17 GxEPD2_EPD(cs, dc, rst, busy, HIGH, 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(0x26, value); // set previous
32 _writeScreenBuffer(0x24, 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(0x26, value); // set previous
40 _writeScreenBuffer(0x24, value); // set current
41}
42
43void GxEPD2_1160_T91::_writeScreenBuffer(uint8_t command, uint8_t value)
44{
45 _writeCommand(command);
46 for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
47 {
48 _writeData(value);
49 }
50}
51
52void GxEPD2_1160_T91::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
53{
54 _writeImage(0x24, bitmap, x, y, w, h, invert, mirror_y, pgm);
55}
56
57void GxEPD2_1160_T91::writeImageForFullRefresh(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
58{
59 _writeImage(0x26, bitmap, x, y, w, h, invert, mirror_y, pgm);
60 _writeImage(0x24, bitmap, x, y, w, h, invert, mirror_y, pgm);
61}
62
63
64void GxEPD2_1160_T91::writeImageAgain(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
65{
66 _writeImage(0x24, bitmap, x, y, w, h, invert, mirror_y, pgm);
67 _writeImage(0x26, bitmap, x, y, w, h, invert, mirror_y, pgm);
68}
69
70void GxEPD2_1160_T91::_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)
71{
72 if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
73 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
74 int32_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
75 x -= x % 8; // byte boundary
76 w = wb * 8; // byte boundary
77 int16_t x1 = x < 0 ? 0 : x; // limit
78 int16_t y1 = y < 0 ? 0 : y; // limit
79 int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
80 int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
81 int16_t dx = x1 - x;
82 int16_t dy = y1 - y;
83 w1 -= dx;
84 h1 -= dy;
85 if ((w1 <= 0) || (h1 <= 0)) return;
86 if (!_using_partial_mode) _Init_Part();
87 _setPartialRamArea(x1, y1, w1, h1);
88 _writeCommand(command);
89 for (int16_t i = 0; i < h1; i++)
90 {
91 for (int16_t j = 0; j < w1 / 8; j++)
92 {
93 uint8_t data;
94 // use wb, h of bitmap for index!
95 int32_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
96 if (pgm)
97 {
98#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
99 data = pgm_read_byte(&bitmap[idx]);
100#else
101 data = bitmap[idx];
102#endif
103 }
104 else
105 {
106 data = bitmap[idx];
107 }
108 if (invert) data = ~data;
109 _writeData(data);
110 }
111 }
112 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
113}
114
115void GxEPD2_1160_T91::writeImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
116 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
117{
118 _writeImagePart(0x24, bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
119}
120
121void GxEPD2_1160_T91::writeImagePartAgain(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
122 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
123{
124 _writeImagePart(0x24, bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
125 _writeImagePart(0x26, bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
126}
127
128void GxEPD2_1160_T91::_writeImagePart(uint8_t command, const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
129 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
130{
131 if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
132 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
133 if ((w_bitmap < 0) || (h_bitmap < 0) || (w < 0) || (h < 0)) return;
134 if ((x_part < 0) || (x_part >= w_bitmap)) return;
135 if ((y_part < 0) || (y_part >= h_bitmap)) return;
136 int32_t wb_bitmap = (w_bitmap + 7) / 8; // width bytes, bitmaps are padded
137 x_part -= x_part % 8; // byte boundary
138 w = w_bitmap - x_part < w ? w_bitmap - x_part : w; // limit
139 h = h_bitmap - y_part < h ? h_bitmap - y_part : h; // limit
140 x -= x % 8; // byte boundary
141 w = 8 * ((w + 7) / 8); // byte boundary, bitmaps are padded
142 int16_t x1 = x < 0 ? 0 : x; // limit
143 int16_t y1 = y < 0 ? 0 : y; // limit
144 int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
145 int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
146 int16_t dx = x1 - x;
147 int16_t dy = y1 - y;
148 w1 -= dx;
149 h1 -= dy;
150 if ((w1 <= 0) || (h1 <= 0)) return;
151 if (!_using_partial_mode) _Init_Part();
152 _setPartialRamArea(x1, y1, w1, h1);
153 _writeCommand(command);
154 for (int16_t i = 0; i < h1; i++)
155 {
156 for (int16_t j = 0; j < w1 / 8; j++)
157 {
158 uint8_t data;
159 // use wb_bitmap, h_bitmap of bitmap for index!
160 int32_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;
161 if (pgm)
162 {
163#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
164 data = pgm_read_byte(&bitmap[idx]);
165#else
166 data = bitmap[idx];
167#endif
168 }
169 else
170 {
171 data = bitmap[idx];
172 }
173 if (invert) data = ~data;
174 _writeData(data);
175 }
176 }
177 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
178}
179
180void GxEPD2_1160_T91::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_1160_T91::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_1160_T91::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_1160_T91::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_1160_T91::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_1160_T91::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_1160_T91::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_1160_T91::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_1160_T91::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_1160_T91::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 _setPartialRamArea(x1, y1, w1, h1);
273 _Update_Part();
274}
275
277{
278 _PowerOff();
279}
280
282{
283 _PowerOff();
284 if (_rst >= 0)
285 {
286 _writeCommand(0x10); // deep sleep mode
287 _writeData(0x3); // enter deep sleep
288 _hibernating = true;
289 }
290}
291
292void GxEPD2_1160_T91::_setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
293{
294 _writeCommand(0x11); // set ram entry mode
295 _writeData(0x03); // x increase, y increase : normal mode
296 _writeCommand(0x44);
297 _writeData(x % 256);
298 _writeData(x / 256);
299 _writeData((x + w - 1) % 256);
300 _writeData((x + w - 1) / 256);
301 _writeCommand(0x45);
302 _writeData(y % 256);
303 _writeData(y / 256);
304 _writeData((y + h - 1) % 256);
305 _writeData((y + h - 1) / 256);
306 _writeCommand(0x4e);
307 _writeData(x % 256);
308 _writeData(x / 256);
309 _writeCommand(0x4f);
310 _writeData(y % 256);
311 _writeData(y / 256);
312}
313
314void GxEPD2_1160_T91::_PowerOn()
315{
316 if (!_power_is_on)
317 {
318 _writeCommand(0x22);
319 _writeData(0xc0);
320 _writeCommand(0x20);
321 _waitWhileBusy("_PowerOn", power_on_time);
322 }
323 _power_is_on = true;
324}
325
326void GxEPD2_1160_T91::_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_1160_T91::_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(0x0C); // Soft start setting
346 _writeData(0xAE);
347 _writeData(0xC7);
348 _writeData(0xC3);
349 _writeData(0xC0);
350 _writeData(0x40);
351 _writeCommand(0x01); // Set MUX as 639
352 _writeData(0x7F);
353 _writeData(0x02);
354 _writeData(0x00);
355 _writeCommand(0x3C); // VBD
356 _writeData(0x01); // LUT1, for white
357 _writeCommand(0x18); //Read built-in temperature sensor
358 _writeData(0x80);
359 _writeCommand(0x22);
360 _writeData(0xB1); //Load Temperature and waveform setting.
361 _writeCommand(0x20);
362 _waitWhileBusy("_InitDisplay", power_on_time);
363 _setPartialRamArea(0, 0, WIDTH, HEIGHT);
364}
365
366const uint8_t GxEPD2_1160_T91::lut_partial[] PROGMEM =
367{
368 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //1
369 0x01, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
370 0x0A, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //3
371 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
372 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //5
373 0x00, 0x00, 0x05, 0x05, 0x00, 0x05, 0x03, 0x05, 0x05, 0x00,
374 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //7
375 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
376 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //9
377 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
378 0x22, 0x22, 0x22, 0x22, 0x22
379};
380
381void GxEPD2_1160_T91::_Init_Full()
382{
383 _InitDisplay();
384 _PowerOn();
385 _using_partial_mode = false;
386}
387
388void GxEPD2_1160_T91::_Init_Part()
389{
390 _InitDisplay();
392 {
393 _writeCommand(0x3C); // Border Waveform Control
394 _writeData(0xC0); // HiZ, [POR], floating
395 _writeCommand(0x32);
396 _writeDataPGM(lut_partial, sizeof(lut_partial));
397 }
398 _PowerOn();
399 _using_partial_mode = true;
400}
401
402void GxEPD2_1160_T91::_Update_Full()
403{
404 _writeCommand(0x22);
405 _writeData(0xf4);
406 _writeCommand(0x20);
407 _waitWhileBusy("_Update_Full", full_refresh_time);
408}
409
410void GxEPD2_1160_T91::_Update_Part()
411{
412 _writeCommand(0x22);
413 _writeData(0xcc);
414 _writeCommand(0x20);
416}
const uint8_t GxEPD2_1160_T91::lut_partial[] PROGMEM
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)
static const bool hasFastPartialUpdate
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_1160_T91(int16_t cs, int16_t dc, int16_t rst, int16_t busy)
static const uint16_t power_on_time
static const uint16_t HEIGHT
void writeScreenBufferAgain(uint8_t value=0xFF)
static const uint16_t power_off_time
static const uint16_t WIDTH
static const uint16_t partial_refresh_time
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 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)
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 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 full_refresh_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 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 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 refresh(bool partial_update_mode=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)