GxEPD2_3C.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  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. //
  6. // Author: Jean-Marc Zingg
  7. //
  8. // Version: see library.properties
  9. //
  10. // Library: https://github.com/ZinggJM/GxEPD2
  11. #ifndef _GxEPD2_3C_H_
  12. #define _GxEPD2_3C_H_
  13. #include <Adafruit_GFX.h>
  14. #include "GxEPD2_EPD.h"
  15. #include "epd3c/GxEPD2_154c.h"
  16. #include "epd3c/GxEPD2_213c.h"
  17. #include "epd3c/GxEPD2_290c.h"
  18. #include "epd3c/GxEPD2_270c.h"
  19. #include "epd3c/GxEPD2_420c.h"
  20. #include "epd3c/GxEPD2_583c.h"
  21. #include "epd3c/GxEPD2_750c.h"
  22. #ifndef ENABLE_GxEPD2_GFX
  23. // default is off
  24. #define ENABLE_GxEPD2_GFX 0
  25. #endif
  26. #if ENABLE_GxEPD2_GFX
  27. #include "GxEPD2_GFX.h"
  28. #endif
  29. template<typename GxEPD2_Type, const uint16_t page_height>
  30. #if ENABLE_GxEPD2_GFX
  31. class GxEPD2_3C : public GxEPD2_GFX
  32. #else
  33. class GxEPD2_3C : public Adafruit_GFX
  34. #endif
  35. {
  36. public:
  37. GxEPD2_Type epd2;
  38. #if ENABLE_GxEPD2_GFX
  39. GxEPD2_3C(GxEPD2_Type epd2_instance) : epd2(epd2_instance), GxEPD2_GFX(epd2, GxEPD2_Type::WIDTH, GxEPD2_Type::HEIGHT)
  40. #else
  41. GxEPD2_3C(GxEPD2_Type epd2_instance) : Adafruit_GFX(GxEPD2_Type::WIDTH, GxEPD2_Type::HEIGHT), epd2(epd2_instance)
  42. #endif
  43. {
  44. _page_height = page_height;
  45. _pages = (HEIGHT / _page_height) + ((HEIGHT % _page_height) > 0);
  46. _using_partial_mode = false;
  47. _current_page = 0;
  48. setFullWindow();
  49. }
  50. uint16_t pages()
  51. {
  52. return _pages;
  53. }
  54. uint16_t pageHeight()
  55. {
  56. return _page_height;
  57. }
  58. bool mirror(bool m)
  59. {
  60. swap (_mirror, m);
  61. return m;
  62. }
  63. void drawPixel(int16_t x, int16_t y, uint16_t color)
  64. {
  65. if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) return;
  66. if (_mirror) x = width() - x - 1;
  67. // check rotation, move pixel around if necessary
  68. switch (getRotation())
  69. {
  70. case 1:
  71. swap(x, y);
  72. x = WIDTH - x - 1;
  73. break;
  74. case 2:
  75. x = WIDTH - x - 1;
  76. y = HEIGHT - y - 1;
  77. break;
  78. case 3:
  79. swap(x, y);
  80. y = HEIGHT - y - 1;
  81. break;
  82. }
  83. // transpose partial window to 0,0
  84. x -= _pw_x;
  85. y -= _pw_y;
  86. // adjust for current page
  87. y -= _current_page * _page_height;
  88. // check if in current page
  89. if ((y < 0) || (y >= _page_height)) return;
  90. uint16_t i = x / 8 + y * (_pw_w / 8);
  91. _black_buffer[i] = (_black_buffer[i] | (1 << (7 - x % 8))); // white
  92. _color_buffer[i] = _color_buffer[i] = (_color_buffer[i] | (1 << (7 - x % 8)));
  93. if (color == GxEPD_WHITE) return;
  94. else if (color == GxEPD_BLACK) _black_buffer[i] = (_black_buffer[i] & (0xFF ^ (1 << (7 - x % 8))));
  95. else if (color == GxEPD_RED) _color_buffer[i] = (_color_buffer[i] & (0xFF ^ (1 << (7 - x % 8))));
  96. }
  97. void init(uint32_t serial_diag_bitrate = 0) // = 0 : disabled
  98. {
  99. epd2.init(serial_diag_bitrate);
  100. _using_partial_mode = false;
  101. _current_page = 0;
  102. setFullWindow();
  103. }
  104. void fillScreen(uint16_t color) // 0x0 black, >0x0 white, to buffer
  105. {
  106. uint8_t black = 0xFF;
  107. uint8_t red = 0xFF;
  108. if (color == GxEPD_WHITE);
  109. else if (color == GxEPD_BLACK) black = 0x00;
  110. else if (color == GxEPD_RED) red = 0x00;
  111. for (uint16_t x = 0; x < sizeof(_black_buffer); x++)
  112. {
  113. _black_buffer[x] = black;
  114. _color_buffer[x] = red;
  115. }
  116. }
  117. // display buffer content to screen, useful for full screen buffer
  118. void display(bool partial_update_mode = false)
  119. {
  120. epd2.writeImage(_black_buffer, _color_buffer, 0, 0, WIDTH, HEIGHT);
  121. epd2.refresh(partial_update_mode);
  122. }
  123. void setFullWindow()
  124. {
  125. _using_partial_mode = false;
  126. _pw_x = 0;
  127. _pw_y = 0;
  128. _pw_w = WIDTH;
  129. _pw_h = HEIGHT;
  130. }
  131. void setPartialWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
  132. {
  133. if (!epd2.hasPartialUpdate) return;
  134. _rotate(x, y, w, h);
  135. _using_partial_mode = true;
  136. _pw_x = gx_uint16_min(x, WIDTH);
  137. _pw_y = gx_uint16_min(y, HEIGHT);
  138. _pw_w = gx_uint16_min(w, WIDTH - _pw_x);
  139. _pw_h = gx_uint16_min(h, HEIGHT - _pw_y);
  140. // make _pw_x, _pw_w multiple of 8
  141. _pw_w += _pw_x % 8;
  142. if (_pw_w % 8 > 0) _pw_w += 8 - _pw_w % 8;
  143. _pw_x -= _pw_x % 8;
  144. }
  145. void firstPage()
  146. {
  147. fillScreen(GxEPD_WHITE);
  148. _current_page = 0;
  149. _second_phase = false;
  150. epd2.setPaged(); // for GxEPD2_154c paged workaround
  151. }
  152. bool nextPage()
  153. {
  154. uint16_t page_ys = _current_page * _page_height;
  155. if (_using_partial_mode)
  156. {
  157. //Serial.print(" nextPage("); Serial.print(_pw_x); Serial.print(", "); Serial.print(_pw_y); Serial.print(", ");
  158. //Serial.print(_pw_w); Serial.print(", "); Serial.print(_pw_h); Serial.print(") P"); Serial.println(_current_page);
  159. uint16_t page_ye = _current_page < (_pages - 1) ? page_ys + _page_height : HEIGHT;
  160. uint16_t dest_ys = _pw_y + page_ys; // transposed
  161. uint16_t dest_ye = gx_uint16_min(_pw_y + _pw_h, _pw_y + page_ye);
  162. if (dest_ye > dest_ys)
  163. {
  164. //Serial.print("writeImage("); Serial.print(_pw_x); Serial.print(", "); Serial.print(dest_ys); Serial.print(", ");
  165. //Serial.print(_pw_w); Serial.print(", "); Serial.print(dest_ye - dest_ys); Serial.println(")");
  166. epd2.writeImage(_black_buffer, _color_buffer, _pw_x, dest_ys, _pw_w, dest_ye - dest_ys);
  167. }
  168. else
  169. {
  170. //Serial.print("writeImage("); Serial.print(_pw_x); Serial.print(", "); Serial.print(dest_ys); Serial.print(", ");
  171. //Serial.print(_pw_w); Serial.print(", "); Serial.print(dest_ye - dest_ys); Serial.print(") skipped ");
  172. //Serial.print(dest_ys); Serial.print(".."); Serial.println(dest_ye);
  173. }
  174. _current_page++;
  175. if (_current_page == _pages)
  176. {
  177. _current_page = 0;
  178. if (!_second_phase)
  179. {
  180. epd2.refresh(_pw_x, _pw_y, _pw_w, _pw_h);
  181. if (epd2.hasFastPartialUpdate)
  182. {
  183. _second_phase = true;
  184. return true;
  185. }
  186. }
  187. return false;
  188. }
  189. fillScreen(GxEPD_WHITE);
  190. return true;
  191. }
  192. else
  193. {
  194. epd2.writeImage(_black_buffer, _color_buffer, 0, page_ys, WIDTH, gx_uint16_min(_page_height, HEIGHT - page_ys));
  195. _current_page++;
  196. if (_current_page == _pages)
  197. {
  198. _current_page = 0;
  199. if ((epd2.panel == GxEPD2::GDEW0154Z04) && (_pages > 1))
  200. {
  201. if (!_second_phase)
  202. {
  203. epd2.refresh(false);
  204. _second_phase = true;
  205. fillScreen(GxEPD_WHITE);
  206. return true;
  207. }
  208. else epd2.refresh(true);
  209. } else epd2.refresh(false);
  210. epd2.powerOff();
  211. return false;
  212. }
  213. fillScreen(GxEPD_WHITE);
  214. return true;
  215. }
  216. }
  217. // GxEPD style paged drawing; drawCallback() is called as many times as needed
  218. void drawPaged(void (*drawCallback)(const void*), const void* pv)
  219. {
  220. if (_using_partial_mode)
  221. {
  222. for (_current_page = 0; _current_page < _pages; _current_page++)
  223. {
  224. uint16_t page_ys = _current_page * _page_height;
  225. uint16_t page_ye = _current_page < (_pages - 1) ? page_ys + _page_height : HEIGHT;
  226. uint16_t dest_ys = _pw_y + page_ys; // transposed
  227. uint16_t dest_ye = gx_uint16_min(_pw_y + _pw_h, _pw_y + page_ye);
  228. if (dest_ye > dest_ys)
  229. {
  230. fillScreen(GxEPD_WHITE);
  231. drawCallback(pv);
  232. epd2.writeImage(_black_buffer, _color_buffer, _pw_x, dest_ys, _pw_w, dest_ye - dest_ys);
  233. }
  234. }
  235. epd2.refresh(_pw_x, _pw_y, _pw_w, _pw_h);
  236. }
  237. else
  238. {
  239. epd2.setPaged(); // for GxEPD2_154c paged workaround
  240. for (_current_page = 0; _current_page < _pages; _current_page++)
  241. {
  242. uint16_t page_ys = _current_page * _page_height;
  243. fillScreen(GxEPD_WHITE);
  244. drawCallback(pv);
  245. epd2.writeImage(_black_buffer, _color_buffer, 0, page_ys, WIDTH, gx_uint16_min(_page_height, HEIGHT - page_ys));
  246. }
  247. if (epd2.panel == GxEPD2::GDEW0154Z04)
  248. { // GxEPD2_154c paged workaround: write color part
  249. for (_current_page = 0; _current_page < _pages; _current_page++)
  250. {
  251. uint16_t page_ys = _current_page * _page_height;
  252. fillScreen(GxEPD_WHITE);
  253. drawCallback(pv);
  254. epd2.writeImage(_black_buffer, _color_buffer, 0, page_ys, WIDTH, gx_uint16_min(_page_height, HEIGHT - page_ys));
  255. }
  256. }
  257. epd2.refresh();
  258. }
  259. _current_page = 0;
  260. }
  261. void drawInvertedBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color)
  262. {
  263. // taken from Adafruit_GFX.cpp, modified
  264. int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte
  265. uint8_t byte = 0;
  266. for (int16_t j = 0; j < h; j++)
  267. {
  268. for (int16_t i = 0; i < w; i++ )
  269. {
  270. if (i & 7) byte <<= 1;
  271. else
  272. {
  273. #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
  274. byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]);
  275. #else
  276. byte = bitmap[j * byteWidth + i / 8];
  277. #endif
  278. }
  279. if (!(byte & 0x80))
  280. {
  281. drawPixel(x + i, y + j, color);
  282. }
  283. }
  284. }
  285. }
  286. // Support for Bitmaps (Sprites) to Controller Buffer and to Screen
  287. void clearScreen(uint8_t value = 0xFF) // init controller memory and screen (default white)
  288. {
  289. epd2.clearScreen(value);
  290. }
  291. void writeScreenBuffer(uint8_t value = 0xFF) // init controller memory (default white)
  292. {
  293. epd2.writeScreenBuffer(value);
  294. }
  295. // write to controller memory, without screen refresh; x and w should be multiple of 8
  296. 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)
  297. {
  298. epd2.writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
  299. }
  300. void writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false)
  301. {
  302. epd2.writeImage(black, color, x, y, w, h, invert, mirror_y, pgm);
  303. }
  304. // write sprite of native data to controller memory, without screen refresh; x and w should be multiple of 8
  305. 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)
  306. {
  307. epd2.writeNative(data1, data2, x, y, w, h, invert, mirror_y, pgm);
  308. }
  309. // write to controller memory, with screen refresh; x and w should be multiple of 8
  310. 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)
  311. {
  312. epd2.drawImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
  313. }
  314. void drawImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false)
  315. {
  316. epd2.drawImage(black, color, x, y, w, h, invert, mirror_y, pgm);
  317. }
  318. // write sprite of native data to controller memory, with screen refresh; x and w should be multiple of 8
  319. 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)
  320. {
  321. epd2.drawNative(data1, data2, x, y, w, h, invert, mirror_y, pgm);
  322. }
  323. void refresh(bool partial_update_mode = false) // screen refresh from controller memory to full screen
  324. {
  325. epd2.refresh(partial_update_mode);
  326. }
  327. void refresh(int16_t x, int16_t y, int16_t w, int16_t h) // screen refresh from controller memory, partial screen
  328. {
  329. epd2.refresh(x, y, w, h);
  330. }
  331. void powerOff()
  332. {
  333. epd2.powerOff();
  334. }
  335. private:
  336. template <typename T> static inline void
  337. swap(T & a, T & b)
  338. {
  339. T t = a;
  340. a = b;
  341. b = t;
  342. };
  343. static inline uint16_t gx_uint16_min(uint16_t a, uint16_t b)
  344. {
  345. return (a < b ? a : b);
  346. };
  347. static inline uint16_t gx_uint16_max(uint16_t a, uint16_t b)
  348. {
  349. return (a > b ? a : b);
  350. };
  351. void _rotate(uint16_t& x, uint16_t& y, uint16_t& w, uint16_t& h)
  352. {
  353. switch (getRotation())
  354. {
  355. case 1:
  356. swap(x, y);
  357. swap(w, h);
  358. x = WIDTH - x - w;
  359. break;
  360. case 2:
  361. x = WIDTH - x - w;
  362. y = HEIGHT - y - h;
  363. break;
  364. case 3:
  365. swap(x, y);
  366. swap(w, h);
  367. y = HEIGHT - y - h;
  368. break;
  369. }
  370. }
  371. private:
  372. uint8_t _black_buffer[(GxEPD2_Type::WIDTH / 8) * page_height];
  373. uint8_t _color_buffer[(GxEPD2_Type::WIDTH / 8) * page_height];
  374. bool _using_partial_mode, _second_phase, _mirror;
  375. uint16_t _width_bytes, _pixel_bytes;
  376. int16_t _current_page;
  377. uint16_t _pages, _page_height;
  378. uint16_t _pw_x, _pw_y, _pw_w, _pw_h;
  379. };
  380. #endif