GxEPD2_BW.h 14 KB

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