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