Paperd.Ink Library 0.0.5
Library for interacting with Paperd.Ink devices.
Loading...
Searching...
No Matches
GxEPD2_150_BN.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: DEPG0150BN : https://www.dke.top/products/dke-154-inch-small-display-modules-epd152x152-electronic-e-ink-paper-displays
6// Controller : SSD1681 : http://www.e-paper-display.com/download_detail/downloadsId=825.html
7// Display: LILYGO® TTGO T5 V2.4.1 1.50 inch : https://www.aliexpress.com/item/4000384679772.html
8//
9// Author: Jean-Marc Zingg
10//
11// Version: see library.properties
12//
13// Library: https://github.com/ZinggJM/GxEPD2
14
15#include "GxEPD2_150_BN.h"
16
17GxEPD2_150_BN::GxEPD2_150_BN(int16_t cs, int16_t dc, int16_t rst, int16_t busy) :
18 GxEPD2_EPD(cs, dc, rst, busy, HIGH, 10000000, WIDTH, HEIGHT, panel, hasColor, hasPartialUpdate, hasFastPartialUpdate)
19{
20}
21
22void GxEPD2_150_BN::clearScreen(uint8_t value)
23{
24 writeScreenBuffer(value);
25 refresh(true);
27}
28
30{
31 if (!_using_partial_mode) _Init_Part();
32 if (_initial_write) _writeScreenBuffer(0x26, value); // set previous
33 _writeScreenBuffer(0x24, value); // set current
34 _initial_write = false; // initial full screen buffer clean done
35}
36
38{
39 if (!_using_partial_mode) _Init_Part();
40 _writeScreenBuffer(0x26, value); // set previous
41 _writeScreenBuffer(0x24, value); // set current
42}
43
44void GxEPD2_150_BN::_writeScreenBuffer(uint8_t command, uint8_t value)
45{
46 _writeCommand(command);
47 for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
48 {
49 _writeData(value);
50 }
51}
52
53void GxEPD2_150_BN::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
54{
55 _writeImage(0x24, bitmap, x, y, w, h, invert, mirror_y, pgm);
56}
57
58void GxEPD2_150_BN::writeImageForFullRefresh(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
59{
60 _writeImage(0x26, bitmap, x, y, w, h, invert, mirror_y, pgm);
61 _writeImage(0x24, bitmap, x, y, w, h, invert, mirror_y, pgm);
62}
63
64void GxEPD2_150_BN::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(0x26, bitmap, x, y, w, h, invert, mirror_y, pgm); // set previous
67 _writeImage(0x24, bitmap, x, y, w, h, invert, mirror_y, pgm); // set current
68}
69
70void GxEPD2_150_BN::_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 int16_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 int16_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_150_BN::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_150_BN::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(0x26, bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm); // set previous
125 _writeImagePart(0x24, bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm); // set current
126}
127
128void GxEPD2_150_BN::_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 int16_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 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;
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_150_BN::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_150_BN::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_150_BN::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_150_BN::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_150_BN::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_150_BN::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_150_BN::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_150_BN::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_150_BN::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_150_BN::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(0x1); // enter deep sleep
288 _hibernating = true;
289 }
290}
291
292void GxEPD2_150_BN::_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 / 8);
298 _writeData((x + w - 1) / 8);
299 _writeCommand(0x45);
300 _writeData(y % 256);
301 _writeData(y / 256);
302 _writeData((y + h - 1) % 256);
303 _writeData((y + h - 1) / 256);
304 _writeCommand(0x4e);
305 _writeData(x / 8);
306 _writeCommand(0x4f);
307 _writeData(y % 256);
308 _writeData(y / 256);
309}
310
311void GxEPD2_150_BN::_PowerOn()
312{
313 if (!_power_is_on)
314 {
315 _writeCommand(0x22);
316 _writeData(0xf8);
317 _writeCommand(0x20);
318 _waitWhileBusy("_PowerOn", power_on_time);
319 }
320 _power_is_on = true;
321}
322
323void GxEPD2_150_BN::_PowerOff()
324{
325 if (_power_is_on)
326 {
327 _writeCommand(0x22);
328 _writeData(0x83);
329 _writeCommand(0x20);
330 _waitWhileBusy("_PowerOff", power_off_time);
331 }
332 _power_is_on = false;
333 _using_partial_mode = false;
334}
335
336void GxEPD2_150_BN::_InitDisplay()
337{
338 if (_hibernating) _reset();
339 delay(10); // 10ms according to specs
340 _writeCommand(0x12); // soft reset
341 delay(10); // 10ms according to specs
342 _writeCommand(0x01); // Driver output control
343 _writeData(0xC7);
344 _writeData(0x00);
345 _writeData(0x00);
346 _writeCommand(0x3C); // BorderWavefrom
347 _writeData(0x05);
348 _writeCommand(0x18); // Read built-in temperature sensor
349 _writeData(0x80);
350 _setPartialRamArea(0, 0, WIDTH, HEIGHT);
351}
352
353const unsigned char GxEPD2_150_BN::lut_partial[] PROGMEM =
354{
355 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
356 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
357 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
358 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
359 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
360 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
361 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
362 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
363 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
364 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
365 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
366 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
367 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
368 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
369 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
370 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
371 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
372 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00,
373};
374
375void GxEPD2_150_BN::_Init_Full()
376{
377 _InitDisplay();
378 _PowerOn();
379 _using_partial_mode = false;
380}
381
382void GxEPD2_150_BN::_Init_Part()
383{
384 _InitDisplay();
385 _writeCommand(0x32);
386 _writeDataPGM(lut_partial, sizeof(lut_partial));
387 _PowerOn();
388 _using_partial_mode = true;
389}
390
391void GxEPD2_150_BN::_Update_Full()
392{
393 _writeCommand(0x22);
394 _writeData(0xf4);
395 _writeCommand(0x20);
396 _waitWhileBusy("_Update_Full", full_refresh_time);
397}
398
399void GxEPD2_150_BN::_Update_Part()
400{
401 _writeCommand(0x22);
402 _writeData(0xfc);
403 _writeCommand(0x20);
404 _waitWhileBusy("_Update_Part", partial_refresh_time);
405}
const unsigned char GxEPD2_150_BN::lut_partial[] PROGMEM
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 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 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 HEIGHT
static const uint16_t full_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)
GxEPD2_150_BN(int16_t cs, int16_t dc, int16_t rst, int16_t busy)
void writeScreenBufferAgain(uint8_t value=0xFF)
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)
void clearScreen(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)
static const uint16_t power_off_time
static const uint16_t partial_refresh_time
static const uint16_t WIDTH
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 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 refresh(bool partial_update_mode=false)
static const uint16_t power_on_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)
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)