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