Paperd.Ink Library 0.0.5
Library for interacting with Paperd.Ink devices.
Loading...
Searching...
No Matches
GxEPD2_154_M10.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: GDEW0154M10 : http://www.e-paper-display.com/products_detail/productId=544.html
6// Controller : UC8151D : http://www.e-paper-display.com/download_detail/downloadsId=1091.html
7//
8// Author: Jean-Marc Zingg
9//
10// Version: see library.properties
11//
12// Library: https://github.com/ZinggJM/GxEPD2
13
14#include "GxEPD2_154_M10.h"
15
16GxEPD2_154_M10::GxEPD2_154_M10(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
21void GxEPD2_154_M10::clearScreen(uint8_t value)
22{
23 writeScreenBuffer(value);
24 refresh(true);
26}
27
29{
30 _initial_write = false; // initial full screen buffer clean done
31 if (!_using_partial_mode) _Init_Part();
32 _writeScreenBuffer(0x13, value); // set current
33 if (_initial_refresh) _writeScreenBuffer(0x10, value); // set previous
34}
35
37{
38 if (!_using_partial_mode) _Init_Part();
39 _writeScreenBuffer(0x10, value); // set previous
40}
41
42void GxEPD2_154_M10::_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_154_M10::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_154_M10::_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)
57{
58 if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
59 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
60 int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
61 x -= x % 8; // byte boundary
62 w = wb * 8; // byte boundary
63 int16_t x1 = x < 0 ? 0 : x; // limit
64 int16_t y1 = y < 0 ? 0 : y; // limit
65 int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
66 int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
67 int16_t dx = x1 - x;
68 int16_t dy = y1 - y;
69 w1 -= dx;
70 h1 -= dy;
71 if ((w1 <= 0) || (h1 <= 0)) return;
72 if (!_using_partial_mode) _Init_Part();
73 _writeCommand(0x91); // partial in
74 _setPartialRamArea(x1, y1, w1, h1);
75 _writeCommand(command);
76 for (int16_t i = 0; i < h1; i++)
77 {
78 for (int16_t j = 0; j < w1 / 8; j++)
79 {
80 uint8_t data;
81 // use wb, h of bitmap for index!
82 int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
83 if (pgm)
84 {
85#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
86 data = pgm_read_byte(&bitmap[idx]);
87#else
88 data = bitmap[idx];
89#endif
90 }
91 else
92 {
93 data = bitmap[idx];
94 }
95 if (invert) data = ~data;
96 _writeData(data);
97 }
98 }
99 _writeCommand(0x92); // partial out
100 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
101}
102
103void GxEPD2_154_M10::writeImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
104 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
105{
106 _writeImagePart(0x13, bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
107}
108
109void GxEPD2_154_M10::_writeImagePart(uint8_t command, const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
110 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
111{
112 if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
113 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
114 if ((w_bitmap < 0) || (h_bitmap < 0) || (w < 0) || (h < 0)) return;
115 if ((x_part < 0) || (x_part >= w_bitmap)) return;
116 if ((y_part < 0) || (y_part >= h_bitmap)) return;
117 int16_t wb_bitmap = (w_bitmap + 7) / 8; // width bytes, bitmaps are padded
118 x_part -= x_part % 8; // byte boundary
119 w = w_bitmap - x_part < w ? w_bitmap - x_part : w; // limit
120 h = h_bitmap - y_part < h ? h_bitmap - y_part : h; // limit
121 x -= x % 8; // byte boundary
122 w = 8 * ((w + 7) / 8); // byte boundary, bitmaps are padded
123 int16_t x1 = x < 0 ? 0 : x; // limit
124 int16_t y1 = y < 0 ? 0 : y; // limit
125 int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
126 int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
127 int16_t dx = x1 - x;
128 int16_t dy = y1 - y;
129 w1 -= dx;
130 h1 -= dy;
131 if ((w1 <= 0) || (h1 <= 0)) return;
132 if (!_using_partial_mode) _Init_Part();
133 _writeCommand(0x91); // partial in
134 _setPartialRamArea(x1, y1, w1, h1);
135 _writeCommand(command);
136 for (int16_t i = 0; i < h1; i++)
137 {
138 for (int16_t j = 0; j < w1 / 8; j++)
139 {
140 uint8_t data;
141 // use wb_bitmap, h_bitmap of bitmap for index!
142 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;
143 if (pgm)
144 {
145#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
146 data = pgm_read_byte(&bitmap[idx]);
147#else
148 data = bitmap[idx];
149#endif
150 }
151 else
152 {
153 data = bitmap[idx];
154 }
155 if (invert) data = ~data;
156 _writeData(data);
157 }
158 }
159 _writeCommand(0x92); // partial out
160 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
161}
162
163void GxEPD2_154_M10::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)
164{
165 if (black)
166 {
167 writeImage(black, x, y, w, h, invert, mirror_y, pgm);
168 }
169}
170
171void GxEPD2_154_M10::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,
172 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
173{
174 if (black)
175 {
176 writeImagePart(black, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
177 }
178}
179
180void GxEPD2_154_M10::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)
181{
182 if (data1)
183 {
184 writeImage(data1, x, y, w, h, invert, mirror_y, pgm);
185 }
186}
187
188void GxEPD2_154_M10::drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
189{
190 writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
191 refresh(x, y, w, h);
192 writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
193}
194
195void GxEPD2_154_M10::drawImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
196 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
197{
198 writeImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
199 refresh(x, y, w, h);
200 writeImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
201}
202
203void GxEPD2_154_M10::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)
204{
205 if (black)
206 {
207 drawImage(black, x, y, w, h, invert, mirror_y, pgm);
208 }
209}
210
211void GxEPD2_154_M10::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,
212 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
213{
214 if (black)
215 {
216 drawImagePart(black, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
217 }
218}
219
220void GxEPD2_154_M10::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)
221{
222 if (data1)
223 {
224 drawImage(data1, x, y, w, h, invert, mirror_y, pgm);
225 }
226}
227
228void GxEPD2_154_M10::refresh(bool partial_update_mode)
229{
230 if (partial_update_mode) refresh(0, 0, WIDTH, HEIGHT);
231 else
232 {
233 if (_using_partial_mode) _Init_Full();
234 _Update_Full();
235 _initial_refresh = false; // initial full update done
236 }
237}
238
239void GxEPD2_154_M10::refresh(int16_t x, int16_t y, int16_t w, int16_t h)
240{
241 if (_initial_refresh) return refresh(false); // initial update needs be full update
242 // intersection with screen
243 int16_t w1 = x < 0 ? w + x : w; // reduce
244 int16_t h1 = y < 0 ? h + y : h; // reduce
245 int16_t x1 = x < 0 ? 0 : x; // limit
246 int16_t y1 = y < 0 ? 0 : y; // limit
247 w1 = x1 + w1 < int16_t(WIDTH) ? w1 : int16_t(WIDTH) - x1; // limit
248 h1 = y1 + h1 < int16_t(HEIGHT) ? h1 : int16_t(HEIGHT) - y1; // limit
249 if ((w1 <= 0) || (h1 <= 0)) return;
250 // make x1, w1 multiple of 8
251 w1 += x1 % 8;
252 if (w1 % 8 > 0) w1 += 8 - w1 % 8;
253 x1 -= x1 % 8;
254 if (!_using_partial_mode) _Init_Part();
255 _writeCommand(0x91); // partial in
256 _setPartialRamArea(x1, y1, w1, h1);
257 _Update_Part();
258 _writeCommand(0x92); // partial out
259}
260
262{
263 _PowerOff();
264}
265
267{
268 _PowerOff();
269 if (_rst >= 0)
270 {
271 _writeCommand(0x07); // deep sleep mode
272 _writeData(0xA5); // enter deep sleep
273 _hibernating = true;
274 }
275}
276
277void GxEPD2_154_M10::_setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
278{
279 uint16_t xe = (x + w - 1) | 0x0007; // byte boundary inclusive (last byte)
280 uint16_t ye = y + h - 1;
281 x &= 0xFFF8; // byte boundary
282 _writeCommand(0x90); // partial window
283 //_writeData(x / 256);
284 _writeData(x % 256);
285 //_writeData(xe / 256);
286 _writeData(xe % 256);
287 _writeData(y / 256);
288 _writeData(y % 256);
289 _writeData(ye / 256);
290 _writeData(ye % 256);
291 _writeData(0x01); // don't see any difference
292 //_writeData(0x00); // don't see any difference
293}
294
295void GxEPD2_154_M10::_PowerOn()
296{
297 if (!_power_is_on)
298 {
299 _writeCommand(0x04);
300 _waitWhileBusy("_PowerOn", power_on_time);
301 }
302 _power_is_on = true;
303}
304
305void GxEPD2_154_M10::_PowerOff()
306{
307 if (_power_is_on)
308 {
309 _writeCommand(0x02);
310 _waitWhileBusy("_PowerOff", power_off_time);
311 }
312 _power_is_on = false;
313 _using_partial_mode = false;
314}
315
316void GxEPD2_154_M10::_InitDisplay()
317{
318 if (_hibernating) _reset();
319 delay(10); // 10ms according to specs
320 _writeCommand(0x00); // panel setting
321 _writeData(0x1f); // LUT from OTP KW-BF KWR-AF BWROTP 0f BWOTP 1f
322 _writeCommand(0x50); // VCOM AND DATA INTERVAL SETTING
323 _writeData(0x97); // WBmode:VBDF 17|D7 VBDW 97 VBDB 57 WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7
324 _writeCommand(0x61); // resolution setting
326 _writeData (HEIGHT >> 8);
327 _writeData (HEIGHT & 0xFF);
328}
329
330// experimental partial screen update LUTs with partially balanced charge
331// LUTs are filled with zeroes
332
333#define T1 10 // charge balance pre-phase
334#define T2 0 // optional extension
335#define T3 60 // color change phase (b/w)
336#define T4 10 // optional extension for one color
337
338const unsigned char GxEPD2_154_M10::lut_20_vcomDC_partial[] PROGMEM =
339{
340 0x00, T1, T2, T3, T4, 1, // 00 00 00 00
341};
342
343const unsigned char GxEPD2_154_M10::lut_21_ww_partial[] PROGMEM =
344{ // 10 w
345 0x00, T1, T2, T3, T4, 1, // 00 00 00 00
346};
347
348const unsigned char GxEPD2_154_M10::lut_22_bw_partial[] PROGMEM =
349{ // 10 w
350 //0x48, T1, T2, T3, T4, 1, // 01 00 10 00
351 0x5A, T1, T2, T3, T4, 1, // 01 01 10 10 more white
352};
353
354const unsigned char GxEPD2_154_M10::lut_23_wb_partial[] PROGMEM =
355{ // 01 b
356 0x84, T1, T2, T3, T4, 1, // 10 00 01 00
357 //0xA5, T1, T2, T3, T4, 1, // 10 10 01 01 more black
358};
359
360const unsigned char GxEPD2_154_M10::lut_24_bb_partial[] PROGMEM =
361{ // 01 b
362 0x00, T1, T2, T3, T4, 1, // 00 00 00 00
363};
364
365void GxEPD2_154_M10::_Init_Full()
366{
367 _InitDisplay();
368 _PowerOn();
369 _using_partial_mode = false;
370}
371
372void GxEPD2_154_M10::_Init_Part()
373{
374 _InitDisplay();
376 {
377 _writeCommand(0x00); // panel setting
378 _writeData(0x3f); // LUT from REG
379 _writeCommand(0x01); // POWER SETTING
380 _writeData (0x03); // VDS_EN, VDG_EN
381 _writeData (0x00); // VCOM_HV, VGL_LV default
382 _writeData (0x21); // VDH as from OTP, TR5
383 _writeData (0x21); // VDL as from OTP, TR5
384 _writeData (0x03); // VDHR default
385 _writeCommand(0x82); // vcom_DC setting
386 _writeData (0x12); // as from OTP, TR5
387 _writeCommand(0x50);
388 _writeData(0x17); //WBmode:VBDF 17|D7 VBDW 97 VBDB 57 WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7
389 _writeCommand(0x20);
390 _writeDataPGM(lut_20_vcomDC_partial, sizeof(lut_20_vcomDC_partial), 36 - sizeof(lut_20_vcomDC_partial));
391 _writeCommand(0x21);
392 _writeDataPGM(lut_21_ww_partial, sizeof(lut_21_ww_partial), 36 - sizeof(lut_21_ww_partial));
393 _writeCommand(0x22);
394 _writeDataPGM(lut_22_bw_partial, sizeof(lut_22_bw_partial), 36 - sizeof(lut_22_bw_partial));
395 _writeCommand(0x23);
396 _writeDataPGM(lut_23_wb_partial, sizeof(lut_23_wb_partial), 36 - sizeof(lut_23_wb_partial));
397 _writeCommand(0x24);
398 _writeDataPGM(lut_24_bb_partial, sizeof(lut_24_bb_partial), 36 - sizeof(lut_24_bb_partial));
399 }
400 _PowerOn();
401 _using_partial_mode = true;
402}
403
404void GxEPD2_154_M10::_Update_Full()
405{
406 _writeCommand(0x12);
407 _waitWhileBusy("_Update_Full", full_refresh_time);
408}
409
410void GxEPD2_154_M10::_Update_Part()
411{
412 _writeCommand(0x12);
413 _waitWhileBusy("_Update_Part", partial_refresh_time);
414}
#define T2
#define T3
#define T4
const unsigned char GxEPD2_154_M10::lut_20_vcomDC_partial[] PROGMEM
#define T1
static const uint16_t power_off_time
static const bool hasFastPartialUpdate
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)
static const uint16_t HEIGHT
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 WIDTH
void writeScreenBuffer(uint8_t value=0xFF)
static const uint16_t power_on_time
static const uint16_t full_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)
void clearScreen(uint8_t value=0xFF)
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 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)
static const uint16_t partial_refresh_time
GxEPD2_154_M10(int16_t cs, int16_t dc, int16_t rst, int16_t busy)
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 _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)