Paperd.Ink Library 0.0.5
Library for interacting with Paperd.Ink devices.
Loading...
Searching...
No Matches
GxEPD2_290_C90c.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: SSD1680 : https://v4.cecdn.yun300.cn/100001_1909185148/SSD1680.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_C90c.h"
14
15GxEPD2_290_C90c::GxEPD2_290_C90c(int16_t cs, int16_t dc, int16_t rst, int16_t busy) :
16 GxEPD2_EPD(cs, dc, rst, busy, HIGH, 30000000, WIDTH, HEIGHT, panel, hasColor, hasPartialUpdate, hasFastPartialUpdate)
17{
18}
19
21{
22 clearScreen(value, 0xFF);
23}
24
25void GxEPD2_290_C90c::clearScreen(uint8_t black_value, uint8_t color_value)
26{
27 _initial_write = false; // initial full screen buffer clean done
28 _Init_Part();
29 _setPartialRamArea(0, 0, WIDTH, HEIGHT);
30 _writeCommand(0x24);
31 for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
32 {
33 _writeData(black_value);
34 }
35 _writeCommand(0x26);
36 for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
37 {
38 _writeData(~color_value);
39 }
40 _Update_Part();
41}
42
44{
45 writeScreenBuffer(value, 0xFF);
46}
47
48void GxEPD2_290_C90c::writeScreenBuffer(uint8_t black_value, uint8_t color_value)
49{
50 _initial_write = false; // initial full screen buffer clean done
51 _Init_Part();
52 _setPartialRamArea(0, 0, WIDTH, HEIGHT);
53 _writeCommand(0x24);
54 for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
55 {
56 _writeData(black_value);
57 }
58 _writeCommand(0x26);
59 for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
60 {
61 _writeData(~color_value);
62 }
63}
64
65void GxEPD2_290_C90c::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
66{
67 writeImage(bitmap, NULL, x, y, w, h, invert, mirror_y, pgm);
68}
69
70void GxEPD2_290_C90c::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)
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 _Init_Part();
87 _setPartialRamArea(x1, y1, w1, h1);
88 _writeCommand(0x24);
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 = 0xFF;
94 if (black)
95 {
96 // use wb, h of bitmap for index!
97 int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
98 if (pgm)
99 {
100#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
101 data = pgm_read_byte(&black[idx]);
102#else
103 data = black[idx];
104#endif
105 }
106 else
107 {
108 data = black[idx];
109 }
110 if (invert) data = ~data;
111 }
112 _writeData(data);
113 }
114 }
115 _writeCommand(0x26);
116 for (int16_t i = 0; i < h1; i++)
117 {
118 for (int16_t j = 0; j < w1 / 8; j++)
119 {
120 uint8_t data = 0xFF;
121 if (color)
122 {
123 // use wb, h of bitmap for index!
124 int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
125 if (pgm)
126 {
127#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
128 data = pgm_read_byte(&color[idx]);
129#else
130 data = color[idx];
131#endif
132 }
133 else
134 {
135 data = color[idx];
136 }
137 if (invert) data = ~data;
138 }
139 _writeData(~data);
140 }
141 }
142 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
143}
144
145void GxEPD2_290_C90c::writeImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
146 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
147{
148 writeImagePart(bitmap, NULL, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
149}
150
151void GxEPD2_290_C90c::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,
152 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
153{
154 if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
155 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
156 if ((w_bitmap < 0) || (h_bitmap < 0) || (w < 0) || (h < 0)) return;
157 if ((x_part < 0) || (x_part >= w_bitmap)) return;
158 if ((y_part < 0) || (y_part >= h_bitmap)) return;
159 int16_t wb_bitmap = (w_bitmap + 7) / 8; // width bytes, bitmaps are padded
160 x_part -= x_part % 8; // byte boundary
161 w = w_bitmap - x_part < w ? w_bitmap - x_part : w; // limit
162 h = h_bitmap - y_part < h ? h_bitmap - y_part : h; // limit
163 x -= x % 8; // byte boundary
164 w = 8 * ((w + 7) / 8); // byte boundary, bitmaps are padded
165 int16_t x1 = x < 0 ? 0 : x; // limit
166 int16_t y1 = y < 0 ? 0 : y; // limit
167 int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
168 int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
169 int16_t dx = x1 - x;
170 int16_t dy = y1 - y;
171 w1 -= dx;
172 h1 -= dy;
173 if ((w1 <= 0) || (h1 <= 0)) return;
174 if (!_using_partial_mode) _Init_Part();
175 _setPartialRamArea(x1, y1, w1, h1);
176 _writeCommand(0x24);
177 for (int16_t i = 0; i < h1; i++)
178 {
179 for (int16_t j = 0; j < w1 / 8; j++)
180 {
181 uint8_t data;
182 // use wb_bitmap, h_bitmap of bitmap for index!
183 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;
184 if (pgm)
185 {
186#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
187 data = pgm_read_byte(&black[idx]);
188#else
189 data = black[idx];
190#endif
191 }
192 else
193 {
194 data = black[idx];
195 }
196 if (invert) data = ~data;
197 _writeData(data);
198 }
199 }
200 _writeCommand(0x26);
201 for (int16_t i = 0; i < h1; i++)
202 {
203 for (int16_t j = 0; j < w1 / 8; j++)
204 {
205 uint8_t data = 0xFF;
206 if (color)
207 {
208 // use wb_bitmap, h_bitmap of bitmap for index!
209 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;
210 if (pgm)
211 {
212#if defined(__AVR) || defined(ESP8266) || defined(ESP32)
213 data = pgm_read_byte(&color[idx]);
214#else
215 data = color[idx];
216#endif
217 }
218 else
219 {
220 data = color[idx];
221 }
222 if (invert) data = ~data;
223 }
224 _writeData(~data);
225 }
226 }
227 delay(1); // yield() to avoid WDT on ESP8266 and ESP32
228}
229
230void GxEPD2_290_C90c::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)
231{
232 if (data1)
233 {
234 writeImage(data1, x, y, w, h, invert, mirror_y, pgm);
235 }
236}
237
238void GxEPD2_290_C90c::drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
239{
240 writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
241 refresh(x, y, w, h);
242}
243
244void GxEPD2_290_C90c::drawImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
245 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
246{
247 writeImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
248 refresh(x, y, w, h);
249}
250
251void GxEPD2_290_C90c::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)
252{
253 writeImage(black, color, x, y, w, h, invert, mirror_y, pgm);
254 refresh(x, y, w, h);
255}
256
257void GxEPD2_290_C90c::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,
258 int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
259{
260 writeImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
261 refresh(x, y, w, h);
262}
263
264void GxEPD2_290_C90c::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)
265{
266 writeNative(data1, data2, x, y, w, h, invert, mirror_y, pgm);
267 refresh(x, y, w, h);
268}
269
270void GxEPD2_290_C90c::refresh(bool partial_update_mode)
271{
272 if (partial_update_mode) refresh(0, 0, WIDTH, HEIGHT);
273 else _Update_Full();
274}
275
276void GxEPD2_290_C90c::refresh(int16_t x, int16_t y, int16_t w, int16_t h)
277{
278 // intersection with screen
279 int16_t w1 = x < 0 ? w + x : w; // reduce
280 int16_t h1 = y < 0 ? h + y : h; // reduce
281 int16_t x1 = x < 0 ? 0 : x; // limit
282 int16_t y1 = y < 0 ? 0 : y; // limit
283 w1 = x1 + w1 < int16_t(WIDTH) ? w1 : int16_t(WIDTH) - x1; // limit
284 h1 = y1 + h1 < int16_t(HEIGHT) ? h1 : int16_t(HEIGHT) - y1; // limit
285 if ((w1 <= 0) || (h1 <= 0)) return;
286 // make x1, w1 multiple of 8
287 w1 += x1 % 8;
288 if (w1 % 8 > 0) w1 += 8 - w1 % 8;
289 x1 -= x1 % 8;
290 _Init_Part();
291 _setPartialRamArea(x1, y1, w1, h1);
292 _Update_Part();
293}
294
296{
297 _PowerOff();
298}
299
301{
302 _PowerOff();
303 if (_rst >= 0)
304 {
305 _writeCommand(0x10); // deep sleep mode
306 _writeData(0x1); // enter deep sleep
307 _hibernating = true;
308 }
309}
310
311void GxEPD2_290_C90c::_setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
312{
313 _writeCommand(0x44);
314 _writeData(x / 8);
315 _writeData((x + w - 1) / 8);
316 _writeCommand(0x45);
317 _writeData(y % 256);
318 _writeData(y / 256);
319 _writeData((y + h - 1) % 256);
320 _writeData((y + h - 1) / 256);
321 _writeCommand(0x4e);
322 _writeData(x / 8);
323 _writeCommand(0x4f);
324 _writeData(y % 256);
325 _writeData(y / 256);
326}
327
328void GxEPD2_290_C90c::_PowerOn()
329{
330 if (!_power_is_on)
331 {
332 _writeCommand(0x22);
333 _writeData(0xf8);
334 _writeCommand(0x20);
335 _waitWhileBusy("_PowerOn", power_on_time);
336 }
337 _power_is_on = true;
338}
339
340void GxEPD2_290_C90c::_PowerOff()
341{
342 if (_power_is_on)
343 {
344 _writeCommand(0x22);
345 _writeData(0x83);
346 _writeCommand(0x20);
347 _waitWhileBusy("_PowerOff", power_off_time);
348 }
349 _power_is_on = false;
350}
351
352void GxEPD2_290_C90c::_InitDisplay()
353{
354 if (_hibernating) _reset();
355 delay(10); // 10ms according to specs
356 _writeCommand(0x12); //SWRESET
357 delay(10); // 10ms according to specs
358 _writeCommand(0x01); //Driver output control
359 _writeData(0x27);
360 _writeData(0x01);
361 _writeData(0x00);
362 _writeCommand(0x11); //data entry mode
363 _writeData(0x03);
364 _writeCommand(0x3C); //BorderWavefrom
365 _writeData(0x05);
366 _writeCommand(0x18); //Read built-in temperature sensor
367 _writeData(0x80);
368 _writeCommand(0x21); // Display update control
369 _writeData(0x00);
370 _writeData(0x80);
371 _setPartialRamArea(0, 0, WIDTH, HEIGHT);
372}
373
374void GxEPD2_290_C90c::_Init_Full()
375{
376 _InitDisplay();
377 _PowerOn();
378}
379
380void GxEPD2_290_C90c::_Init_Part()
381{
382 _InitDisplay();
383 _PowerOn();
384}
385
386void GxEPD2_290_C90c::_Update_Full()
387{
388 _writeCommand(0x22);
389 _writeData(0xf7);
390 _writeCommand(0x20);
391 _waitWhileBusy("_Update_Full", full_refresh_time);
392}
393
394void GxEPD2_290_C90c::_Update_Part()
395{
396 _writeCommand(0x22);
397 _writeData(0xf7);
398 _writeCommand(0x20);
399 _waitWhileBusy("_Update_Part", partial_refresh_time);
400}
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 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 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 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)
static const uint16_t partial_refresh_time
static const uint16_t power_on_time
void clearScreen(uint8_t value=0xFF)
static const uint16_t WIDTH
void writeScreenBuffer(uint8_t value=0xFF)
void refresh(bool partial_update_mode=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_C90c(int16_t cs, int16_t dc, int16_t rst, int16_t busy)
static const uint16_t HEIGHT
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 power_off_time
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
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