GxEPD2_7C.h 20 KB

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