GxEPD2_4C.h 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  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. // Display Library based on Demo Example from Good Display: https://www.good-display.com/companyfile/32/
  5. //
  6. // Author: Jean-Marc Zingg
  7. //
  8. // Version: see library.properties
  9. //
  10. // Library: https://github.com/ZinggJM/GxEPD2
  11. #ifndef _GxEPD2_4C_H_
  12. #define _GxEPD2_4C_H_
  13. // uncomment next line to use class GFX of library GFX_Root instead of Adafruit_GFX
  14. //#include <GFX.h>
  15. #ifndef ENABLE_GxEPD2_GFX
  16. // default is off
  17. #define ENABLE_GxEPD2_GFX 0
  18. #endif
  19. #if ENABLE_GxEPD2_GFX
  20. #include "GxEPD2_GFX.h"
  21. #define GxEPD2_GFX_BASE_CLASS GxEPD2_GFX
  22. #elif defined(_GFX_H_)
  23. #define GxEPD2_GFX_BASE_CLASS GFX
  24. #else
  25. #include <Adafruit_GFX.h>
  26. #define GxEPD2_GFX_BASE_CLASS Adafruit_GFX
  27. #endif
  28. #include "GxEPD2_EPD.h"
  29. // for __has_include see https://en.cppreference.com/w/cpp/preprocessor/include
  30. // see also https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html
  31. // #if !defined(__has_include) || __has_include("epd/GxEPD2_102.h") is not portable!
  32. #if defined __has_include
  33. # if __has_include("GxEPD2.h")
  34. # // __has_include can be used
  35. # else
  36. # // __has_include doesn't work for us, include anyway
  37. # undef __has_include
  38. # define __has_include(x) true
  39. # endif
  40. #else
  41. # // no __has_include, include anyway
  42. # define __has_include(x) true
  43. #endif
  44. #if __has_include("epd4c/GxEPD2_154c_GDEM0154F51H.h")
  45. #include "epd4c/GxEPD2_154c_GDEM0154F51H.h"
  46. #endif
  47. #if __has_include("epd4c/GxEPD2_213c_GDEY0213F51.h")
  48. #include "epd4c/GxEPD2_213c_GDEY0213F51.h"
  49. #endif
  50. #if __has_include("epd4c/GxEPD2_213c_GDEY0213F52.h")
  51. #include "epd4c/GxEPD2_213c_GDEY0213F52.h"
  52. #endif
  53. #if __has_include("epd4c/GxEPD2_266c_GDEY0266F51H.h")
  54. #include "epd4c/GxEPD2_266c_GDEY0266F51H.h"
  55. #endif
  56. #if __has_include("epd4c/GxEPD2_290c_GDEY029F51H.h")
  57. #include "epd4c/GxEPD2_290c_GDEY029F51H.h"
  58. #endif
  59. #if __has_include("epd4c/GxEPD2_290c_GDEY029F51.h")
  60. #include "epd4c/GxEPD2_290c_GDEY029F51.h"
  61. #endif
  62. #if __has_include("epd4c/GxEPD2_300c.h")
  63. #include "epd4c/GxEPD2_300c.h"
  64. #endif
  65. #if __has_include("epd4c/GxEPD2_350c_GDEM035F51.h")
  66. #include "epd4c/GxEPD2_350c_GDEM035F51.h"
  67. #endif
  68. #if __has_include("epd4c/GxEPD2_397c_GDEM0397F81.h")
  69. #include "epd4c/GxEPD2_397c_GDEM0397F81.h"
  70. #endif
  71. #if __has_include("epd4c/GxEPD2_420c_GDEY0420F51.h")
  72. #include "epd4c/GxEPD2_420c_GDEY0420F51.h"
  73. #endif
  74. #if __has_include("epd4c/GxEPD2_437c.h")
  75. #include "epd4c/GxEPD2_437c.h"
  76. #endif
  77. #if __has_include("epd4c/GxEPD2_579c_GDEY0579F51.h")
  78. #include "epd4c/GxEPD2_579c_GDEY0579F51.h"
  79. #endif
  80. #if __has_include("epd4c/GxEPD2_750c_GDEM075F52.h")
  81. #include "epd4c/GxEPD2_750c_GDEM075F52.h"
  82. #endif
  83. #if __has_include("epd4c/GxEPD2_1160c_GDEY116F51.h")
  84. #include "epd4c/GxEPD2_1160c_GDEY116F51.h"
  85. #endif
  86. template<typename GxEPD2_Type, const uint16_t page_height>
  87. class GxEPD2_4C : public GxEPD2_GFX_BASE_CLASS
  88. {
  89. public:
  90. GxEPD2_Type epd2;
  91. #if ENABLE_GxEPD2_GFX
  92. GxEPD2_4C(GxEPD2_Type epd2_instance) : GxEPD2_GFX_BASE_CLASS(epd2, GxEPD2_Type::WIDTH_VISIBLE, GxEPD2_Type::HEIGHT), epd2(epd2_instance)
  93. #else
  94. GxEPD2_4C(GxEPD2_Type epd2_instance) : GxEPD2_GFX_BASE_CLASS(GxEPD2_Type::WIDTH_VISIBLE, GxEPD2_Type::HEIGHT), epd2(epd2_instance)
  95. #endif
  96. {
  97. _page_height = page_height;
  98. _pages = (HEIGHT / _page_height) + ((HEIGHT % _page_height) > 0);
  99. _mirror = false;
  100. _using_partial_mode = false;
  101. _current_page = 0;
  102. setFullWindow();
  103. }
  104. uint16_t pages()
  105. {
  106. return _pages;
  107. }
  108. uint16_t pageHeight()
  109. {
  110. return _page_height;
  111. }
  112. bool mirror(bool m)
  113. {
  114. _swap_ (_mirror, m);
  115. return m;
  116. }
  117. void drawPixel(int16_t x, int16_t y, uint16_t color)
  118. {
  119. if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) return;
  120. if (_mirror) x = width() - x - 1;
  121. // check rotation, move pixel around if necessary
  122. switch (getRotation())
  123. {
  124. case 1:
  125. _swap_(x, y);
  126. x = WIDTH - x - 1;
  127. break;
  128. case 2:
  129. x = WIDTH - x - 1;
  130. y = HEIGHT - y - 1;
  131. break;
  132. case 3:
  133. _swap_(x, y);
  134. y = HEIGHT - y - 1;
  135. break;
  136. }
  137. // transpose partial window to 0,0
  138. x -= _pw_x;
  139. y -= _pw_y;
  140. // clip to (partial) window
  141. if ((x < 0) || (x >= int16_t(_pw_w)) || (y < 0) || (y >= int16_t(_pw_h))) return;
  142. // adjust for current page
  143. y -= _current_page * _page_height;
  144. // check if in current page
  145. if ((y < 0) || (y >= int16_t(_page_height))) return;
  146. uint32_t i = x / 4 + uint32_t(y) * (_pw_w / 4);
  147. uint8_t pv = color4(color);
  148. switch(x % 4)
  149. {
  150. case 0: _pixel_buffer[i] = (_pixel_buffer[i] & 0x3F) | (pv << 6); break;
  151. case 1: _pixel_buffer[i] = (_pixel_buffer[i] & 0xCF) | (pv << 4); break;
  152. case 2: _pixel_buffer[i] = (_pixel_buffer[i] & 0xF3) | (pv << 2); break;
  153. case 3: _pixel_buffer[i] = (_pixel_buffer[i] & 0xFC) | pv; break;
  154. }
  155. }
  156. void init(uint32_t serial_diag_bitrate = 0) // = 0 : disabled
  157. {
  158. epd2.init(serial_diag_bitrate);
  159. _using_partial_mode = false;
  160. _current_page = 0;
  161. setFullWindow();
  162. }
  163. // init method with additional parameters:
  164. // initial false for re-init after processor deep sleep wake up, if display power supply was kept
  165. // only relevant for b/w displays with fast partial update
  166. // reset_duration = 20 is default; a value of 2 may help with "clever" reset circuit of newer boards from Waveshare
  167. // pulldown_rst_mode true for alternate RST handling to avoid feeding 5V through RST pin
  168. void init(uint32_t serial_diag_bitrate, bool initial, uint16_t reset_duration = 20, bool pulldown_rst_mode = false)
  169. {
  170. epd2.init(serial_diag_bitrate, initial, reset_duration, pulldown_rst_mode);
  171. _using_partial_mode = false;
  172. _current_page = 0;
  173. setFullWindow();
  174. }
  175. // init method with additional parameters:
  176. // SPIClass& spi: either SPI or alternate HW SPI channel
  177. // SPISettings spi_settings: e.g. for higher SPI speed selection
  178. void init(uint32_t serial_diag_bitrate, bool initial, uint16_t reset_duration, bool pulldown_rst_mode, SPIClass& spi, SPISettings spi_settings)
  179. {
  180. epd2.selectSPI(spi, spi_settings);
  181. epd2.init(serial_diag_bitrate, initial, reset_duration, pulldown_rst_mode);
  182. _using_partial_mode = false;
  183. _current_page = 0;
  184. setFullWindow();
  185. }
  186. // release SPI and control pins
  187. void end()
  188. {
  189. epd2.end();
  190. }
  191. void fillScreen(uint16_t color)
  192. {
  193. uint8_t pv = color4(color) * 0x55; // 0b01010101
  194. for (uint32_t x = 0; x < sizeof(_pixel_buffer); x++)
  195. {
  196. _pixel_buffer[x] = pv;
  197. }
  198. }
  199. // display buffer content to screen, useful for full screen buffer
  200. void display(bool partial_update_mode = false)
  201. {
  202. epd2.writeNative(_pixel_buffer, 0, 0, 0, GxEPD2_Type::WIDTH, _page_height);
  203. epd2.refresh(partial_update_mode);
  204. if (!partial_update_mode) epd2.powerOff();
  205. }
  206. // display part of buffer content to screen, useful for full screen buffer
  207. // displayWindow, use parameters according to actual rotation.
  208. // x and w should be multiple of 8, for rotation 0 or 2,
  209. // y and h should be multiple of 8, for rotation 1 or 3,
  210. // else window is increased as needed,
  211. // this is an addressing limitation of the e-paper controllers
  212. void displayWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
  213. {
  214. x = gx_uint16_min(x, width());
  215. y = gx_uint16_min(y, height());
  216. w = gx_uint16_min(w, width() - x);
  217. h = gx_uint16_min(h, height() - y);
  218. _rotate(x, y, w, h);
  219. epd2.writeNativePart(_pixel_buffer, 0, x, y, GxEPD2_Type::WIDTH, _page_height, x, y, w, h);
  220. epd2.refresh(x, y, w, h);
  221. }
  222. void setFullWindow()
  223. {
  224. _using_partial_mode = false;
  225. _pw_x = 0;
  226. _pw_y = 0;
  227. _pw_w = GxEPD2_Type::WIDTH;
  228. _pw_h = HEIGHT;
  229. }
  230. // setPartialWindow, use parameters according to actual rotation.
  231. // x and w should be multiple of 8, for rotation 0 or 2,
  232. // y and h should be multiple of 8, for rotation 1 or 3,
  233. // else window is increased as needed,
  234. // this is an addressing limitation of the e-paper controllers
  235. void setPartialWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
  236. {
  237. if (!epd2.hasPartialUpdate) return;
  238. _pw_x = gx_uint16_min(x, width());
  239. _pw_y = gx_uint16_min(y, height());
  240. _pw_w = gx_uint16_min(w, width() - _pw_x);
  241. _pw_h = gx_uint16_min(h, height() - _pw_y);
  242. _rotate(_pw_x, _pw_y, _pw_w, _pw_h);
  243. _using_partial_mode = true;
  244. // make _pw_x, _pw_w multiple of 4
  245. _pw_w += _pw_x % 4;
  246. if (_pw_w % 4 > 0) _pw_w += 4 - _pw_w % 4;
  247. _pw_x -= _pw_x % 4;
  248. }
  249. void firstPage()
  250. {
  251. fillScreen(GxEPD_WHITE);
  252. _current_page = 0;
  253. _second_phase = false;
  254. epd2.setPaged(); // for GxEPD2_565c paged workaround
  255. }
  256. bool nextPage()
  257. {
  258. uint16_t page_ys = _current_page * _page_height;
  259. if (_using_partial_mode)
  260. {
  261. //Serial.print(" nextPage("); Serial.print(_pw_x); Serial.print(", "); Serial.print(_pw_y); Serial.print(", ");
  262. //Serial.print(_pw_w); Serial.print(", "); Serial.print(_pw_h); Serial.print(") P"); Serial.println(_current_page);
  263. uint16_t page_ye = _current_page < int16_t(_pages - 1) ? page_ys + _page_height : HEIGHT;
  264. uint16_t dest_ys = _pw_y + page_ys; // transposed
  265. uint16_t dest_ye = gx_uint16_min(_pw_y + _pw_h, _pw_y + page_ye);
  266. if (dest_ye > dest_ys)
  267. {
  268. //Serial.print("writeImage("); Serial.print(_pw_x); Serial.print(", "); Serial.print(dest_ys); Serial.print(", ");
  269. //Serial.print(_pw_w); Serial.print(", "); Serial.print(dest_ye - dest_ys); Serial.println(")");
  270. epd2.writeNative(_pixel_buffer, 0, _pw_x, dest_ys, _pw_w, dest_ye - dest_ys);
  271. }
  272. else
  273. {
  274. //Serial.print("writeImage("); Serial.print(_pw_x); Serial.print(", "); Serial.print(dest_ys); Serial.print(", ");
  275. //Serial.print(_pw_w); Serial.print(", "); Serial.print(dest_ye - dest_ys); Serial.print(") skipped ");
  276. //Serial.print(dest_ys); Serial.print(".."); Serial.println(dest_ye);
  277. }
  278. _current_page++;
  279. if (_current_page == int16_t(_pages))
  280. {
  281. _current_page = 0;
  282. if (!_second_phase)
  283. {
  284. epd2.refresh(_pw_x, _pw_y, _pw_w, _pw_h);
  285. if (epd2.hasFastPartialUpdate)
  286. {
  287. _second_phase = true;
  288. return true;
  289. }
  290. }
  291. return false;
  292. }
  293. fillScreen(GxEPD_WHITE);
  294. return true;
  295. }
  296. else // full update
  297. {
  298. epd2.writeNative(_pixel_buffer, 0, 0, page_ys, GxEPD2_Type::WIDTH, gx_uint16_min(_page_height, HEIGHT - page_ys));
  299. _current_page++;
  300. if (_current_page == int16_t(_pages))
  301. {
  302. _current_page = 0;
  303. if ((epd2.panel == GxEPD2::GDEW0154Z04) && (_pages > 1))
  304. {
  305. if (!_second_phase)
  306. {
  307. epd2.refresh(false); // full update after first phase
  308. _second_phase = true;
  309. fillScreen(GxEPD_WHITE);
  310. return true;
  311. }
  312. else epd2.refresh(true); // partial update after second phase
  313. } else epd2.refresh(false); // full update after only phase
  314. epd2.powerOff();
  315. return false;
  316. }
  317. fillScreen(GxEPD_WHITE);
  318. return true;
  319. }
  320. }
  321. bool nextPageToPrevious() {return false;}; // no-op in this class
  322. // GxEPD style paged drawing; drawCallback() is called as many times as needed
  323. void drawPaged(void (*drawCallback)(const void*), const void* pv)
  324. {
  325. if (_using_partial_mode)
  326. {
  327. for (_current_page = 0; _current_page < _pages; _current_page++)
  328. {
  329. uint16_t page_ys = _current_page * _page_height;
  330. uint16_t page_ye = _current_page < (_pages - 1) ? page_ys + _page_height : HEIGHT;
  331. uint16_t dest_ys = _pw_y + page_ys; // transposed
  332. uint16_t dest_ye = gx_uint16_min(_pw_y + _pw_h, _pw_y + page_ye);
  333. if (dest_ye > dest_ys)
  334. {
  335. fillScreen(GxEPD_WHITE);
  336. drawCallback(pv);
  337. epd2.writeNative(_pixel_buffer, 0, _pw_x, dest_ys, _pw_w, dest_ye - dest_ys);
  338. }
  339. }
  340. epd2.refresh(_pw_x, _pw_y, _pw_w, _pw_h);
  341. }
  342. else // full update
  343. {
  344. epd2.setPaged(); // for GxEPD2_154c paged workaround
  345. for (_current_page = 0; _current_page < _pages; _current_page++)
  346. {
  347. uint16_t page_ys = _current_page * _page_height;
  348. fillScreen(GxEPD_WHITE);
  349. drawCallback(pv);
  350. epd2.writeNative(_pixel_buffer, 0, 0, page_ys, GxEPD2_Type::WIDTH, gx_uint16_min(_page_height, HEIGHT - page_ys));
  351. }
  352. epd2.refresh(false); // full update
  353. epd2.powerOff();
  354. }
  355. _current_page = 0;
  356. }
  357. void drawInvertedBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color)
  358. {
  359. // taken from Adafruit_GFX.cpp, modified
  360. int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte
  361. uint8_t byte = 0;
  362. for (int16_t j = 0; j < h; j++)
  363. {
  364. for (int16_t i = 0; i < w; i++ )
  365. {
  366. if (i & 7) byte <<= 1;
  367. else
  368. {
  369. #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
  370. byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]);
  371. #else
  372. byte = bitmap[j * byteWidth + i / 8];
  373. #endif
  374. }
  375. if (!(byte & 0x80))
  376. {
  377. drawPixel(x + i, y + j, color);
  378. }
  379. }
  380. }
  381. }
  382. // Support for Bitmaps (Sprites) to Controller Buffer and to Screen
  383. void clearScreen(uint8_t value = 0xFF) // init controller memory and screen (default white)
  384. {
  385. epd2.clearScreen(value);
  386. }
  387. void writeScreenBuffer(uint8_t value = 0xFF) // init controller memory (default white)
  388. {
  389. epd2.writeScreenBuffer(value);
  390. }
  391. // write to controller memory, without screen refresh; x and w should be multiple of 8
  392. 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)
  393. {
  394. epd2.writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
  395. }
  396. void writeImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
  397. int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false)
  398. {
  399. epd2.writeImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
  400. }
  401. void 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)
  402. {
  403. epd2.writeImage(black, color, x, y, w, h, invert, mirror_y, pgm);
  404. }
  405. void writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h)
  406. {
  407. epd2.writeImage(black, color, x, y, w, h, false, false, false);
  408. }
  409. void 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,
  410. int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  411. {
  412. epd2.writeImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
  413. }
  414. void 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,
  415. int16_t x, int16_t y, int16_t w, int16_t h)
  416. {
  417. epd2.writeImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, false, false, false);
  418. }
  419. // write sprite of native data to controller memory, without screen refresh; x and w should be multiple of 8
  420. void 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)
  421. {
  422. epd2.writeNative(data1, data2, x, y, w, h, invert, mirror_y, pgm);
  423. }
  424. // write to controller memory, with screen refresh; x and w should be multiple of 8
  425. 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)
  426. {
  427. epd2.drawImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
  428. }
  429. void drawImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
  430. int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false)
  431. {
  432. epd2.drawImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
  433. }
  434. void 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)
  435. {
  436. epd2.drawImage(black, color, x, y, w, h, invert, mirror_y, pgm);
  437. }
  438. void drawImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h)
  439. {
  440. epd2.drawImage(black, color, x, y, w, h, false, false, false);
  441. }
  442. void 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,
  443. int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  444. {
  445. epd2.drawImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
  446. }
  447. void 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,
  448. int16_t x, int16_t y, int16_t w, int16_t h)
  449. {
  450. epd2.drawImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, false, false, false);
  451. }
  452. // write sprite of native data to controller memory, with screen refresh; x and w should be multiple of 8
  453. void 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)
  454. {
  455. epd2.drawNative(data1, data2, x, y, w, h, invert, mirror_y, pgm);
  456. }
  457. void refresh(bool partial_update_mode = false) // screen refresh from controller memory to full screen
  458. {
  459. epd2.refresh(partial_update_mode);
  460. if (!partial_update_mode) epd2.powerOff();
  461. }
  462. void refresh(int16_t x, int16_t y, int16_t w, int16_t h) // screen refresh from controller memory, partial screen
  463. {
  464. epd2.refresh(x, y, w, h);
  465. }
  466. // turns off generation of panel driving voltages, avoids screen fading over time
  467. void powerOff()
  468. {
  469. epd2.powerOff();
  470. }
  471. // turns powerOff() and sets controller to deep sleep for minimum power use, ONLY if wakeable by RST (rst >= 0)
  472. void hibernate()
  473. {
  474. epd2.hibernate();
  475. }
  476. private:
  477. template <typename T> static inline void
  478. _swap_(T & a, T & b)
  479. {
  480. T t = a;
  481. a = b;
  482. b = t;
  483. };
  484. static inline uint16_t gx_uint16_min(uint16_t a, uint16_t b)
  485. {
  486. return (a < b ? a : b);
  487. };
  488. static inline uint16_t gx_uint16_max(uint16_t a, uint16_t b)
  489. {
  490. return (a > b ? a : b);
  491. };
  492. void _rotate(uint16_t& x, uint16_t& y, uint16_t& w, uint16_t& h)
  493. {
  494. switch (getRotation())
  495. {
  496. case 1:
  497. _swap_(x, y);
  498. _swap_(w, h);
  499. x = WIDTH - x - w;
  500. break;
  501. case 2:
  502. x = WIDTH - x - w;
  503. y = HEIGHT - y - h;
  504. break;
  505. case 3:
  506. _swap_(x, y);
  507. _swap_(w, h);
  508. y = HEIGHT - y - h;
  509. break;
  510. }
  511. }
  512. uint8_t color4(uint16_t color)
  513. {
  514. static uint16_t _prev_color = GxEPD_BLACK;
  515. static uint8_t _prev_color4 = 0x00; // black
  516. if (color == _prev_color) return _prev_color4;
  517. uint8_t cv4 = 0x00;
  518. switch (color)
  519. {
  520. case GxEPD_BLACK: cv4 = 0x00; break;
  521. case GxEPD_WHITE: cv4 = 0x01; break;
  522. case GxEPD_GREEN: cv4 = 0x02; break; // use yellow?
  523. case GxEPD_BLUE: cv4 = 0x00; break; // use black
  524. case GxEPD_RED: cv4 = 0x03; break;
  525. case GxEPD_YELLOW: cv4 = 0x02; break;
  526. case GxEPD_ORANGE: cv4 = 0x02; break; // use yellow?
  527. default:
  528. {
  529. uint16_t red = color & 0xF800;
  530. uint16_t green = (color & 0x07E0) << 5;
  531. uint16_t blue = (color & 0x001F) << 11;
  532. if ((red < 0x8000) && (green < 0x8000) && (blue < 0x8000)) cv4 = 0x00; // black
  533. else if ((red >= 0x8000) && (green >= 0x8000) && (blue >= 0x8000)) cv4 = 0x01; // white
  534. else if ((red >= 0x8000) && (blue >= 0x8000)) cv4 = 0x03; // red, blue > red
  535. else if ((green >= 0x8000) && (blue >= 0x8000)) cv4 = 0x01; // green, blue > white
  536. else if ((red >= 0x8000) && (green >= 0xC000)) cv4 = 0x02; // yellow
  537. else if ((red >= 0x8000) && (green >= 0x4000)) cv4 = 0x03; // orange > red
  538. else if (red >= 0x8000) cv4 = 0x03; // red
  539. else if (green >= 0x8000) cv4 = 0x00; // green > black
  540. else cv4 = 0x03; // blue
  541. }
  542. }
  543. _prev_color = color;
  544. _prev_color4 = cv4;
  545. return cv4;
  546. }
  547. private:
  548. uint8_t _pixel_buffer[(GxEPD2_Type::WIDTH / 4) * page_height];
  549. bool _using_partial_mode, _second_phase, _mirror;
  550. uint16_t _width_bytes, _pixel_bytes;
  551. int16_t _current_page;
  552. uint16_t _pages, _page_height;
  553. uint16_t _pw_x, _pw_y, _pw_w, _pw_h;
  554. };
  555. #endif