GxEPD2_BW.h 12 KB

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