GxEPD2_270c.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  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. // Controller: IL91874 : http://www.e-paper-display.com/download_detail/downloadsId=539.html
  6. //
  7. // Author: Jean-Marc Zingg
  8. //
  9. // Version: see library.properties
  10. //
  11. // Library: https://github.com/ZinggJM/GxEPD2
  12. #include "GxEPD2_270c.h"
  13. GxEPD2_270c::GxEPD2_270c(int8_t cs, int8_t dc, int8_t rst, int8_t busy) :
  14. GxEPD2_EPD(cs, dc, rst, busy, LOW, 20000000, WIDTH, HEIGHT, panel, hasColor, hasPartialUpdate, hasFastPartialUpdate)
  15. {
  16. }
  17. void GxEPD2_270c::clearScreen(uint8_t value)
  18. {
  19. clearScreen(value, 0xFF);
  20. }
  21. void GxEPD2_270c::clearScreen(uint8_t black_value, uint8_t red_value)
  22. {
  23. _initial_write = false; // initial full screen buffer clean done
  24. _Init_Part();
  25. _setPartialRamArea_270c(0x14, 0, 0, WIDTH, HEIGHT);
  26. for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
  27. {
  28. _writeData(~black_value);
  29. }
  30. _setPartialRamArea_270c(0x15, 0, 0, WIDTH, HEIGHT);
  31. for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
  32. {
  33. _writeData(~red_value);
  34. }
  35. refresh(0, 0, WIDTH, HEIGHT);
  36. }
  37. void GxEPD2_270c::writeScreenBuffer(uint8_t value)
  38. {
  39. writeScreenBuffer(value, 0xFF);
  40. }
  41. void GxEPD2_270c::writeScreenBuffer(uint8_t black_value, uint8_t color_value)
  42. {
  43. _initial_write = false; // initial full screen buffer clean done
  44. _Init_Part();
  45. _setPartialRamArea_270c(0x14, 0, 0, WIDTH, HEIGHT);
  46. for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
  47. {
  48. _writeData(~black_value);
  49. }
  50. _setPartialRamArea_270c(0x15, 0, 0, WIDTH, HEIGHT);
  51. for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
  52. {
  53. _writeData(~color_value);
  54. }
  55. }
  56. void GxEPD2_270c::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  57. {
  58. writeImage(bitmap, NULL, x, y, w, h, invert, mirror_y, pgm);
  59. }
  60. void GxEPD2_270c::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)
  61. {
  62. if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
  63. delay(1); // yield() to avoid WDT on ESP8266 and ESP32
  64. int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
  65. x -= x % 8; // byte boundary
  66. w = wb * 8; // byte boundary
  67. int16_t x1 = x < 0 ? 0 : x; // limit
  68. int16_t y1 = y < 0 ? 0 : y; // limit
  69. int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
  70. int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
  71. int16_t dx = x1 - x;
  72. int16_t dy = y1 - y;
  73. w1 -= dx;
  74. h1 -= dy;
  75. if ((w1 <= 0) || (h1 <= 0)) return;
  76. _Init_Part();
  77. _setPartialRamArea_270c(0x14, x1, y1, w1, h1);
  78. for (int16_t i = 0; i < h1; i++)
  79. {
  80. for (int16_t j = 0; j < w1 / 8; j++)
  81. {
  82. uint8_t data = 0xFF;
  83. if (black)
  84. {
  85. // use wb, h of bitmap for index!
  86. int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
  87. if (pgm)
  88. {
  89. #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
  90. data = pgm_read_byte(&black[idx]);
  91. #else
  92. data = black[idx];
  93. #endif
  94. }
  95. else
  96. {
  97. data = black[idx];
  98. }
  99. if (invert) data = ~data;
  100. }
  101. _writeData(~data);
  102. }
  103. }
  104. _setPartialRamArea_270c(0x15, x1, y1, w1, h1);
  105. for (int16_t i = 0; i < h1; i++)
  106. {
  107. for (int16_t j = 0; j < w1 / 8; j++)
  108. {
  109. uint8_t data = 0xFF;
  110. if (color)
  111. {
  112. // use wb, h of bitmap for index!
  113. int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
  114. if (pgm)
  115. {
  116. #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
  117. data = pgm_read_byte(&color[idx]);
  118. #else
  119. data = color[idx];
  120. #endif
  121. }
  122. else
  123. {
  124. data = color[idx];
  125. }
  126. if (invert) data = ~data;
  127. }
  128. _writeData(~data);
  129. }
  130. }
  131. delay(1); // yield() to avoid WDT on ESP8266 and ESP32
  132. }
  133. void GxEPD2_270c::writeImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
  134. int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  135. {
  136. writeImagePart(bitmap, NULL, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
  137. }
  138. void GxEPD2_270c::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,
  139. int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  140. {
  141. if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
  142. delay(1); // yield() to avoid WDT on ESP8266 and ESP32
  143. if ((w_bitmap < 0) || (h_bitmap < 0) || (w < 0) || (h < 0)) return;
  144. if ((x_part < 0) || (x_part >= w_bitmap)) return;
  145. if ((y_part < 0) || (y_part >= h_bitmap)) return;
  146. int16_t wb_bitmap = (w_bitmap + 7) / 8; // width bytes, bitmaps are padded
  147. x_part -= x_part % 8; // byte boundary
  148. w = w_bitmap - x_part < w ? w_bitmap - x_part : w; // limit
  149. h = h_bitmap - y_part < h ? h_bitmap - y_part : h; // limit
  150. x -= x % 8; // byte boundary
  151. w = 8 * ((w + 7) / 8); // byte boundary, bitmaps are padded
  152. int16_t x1 = x < 0 ? 0 : x; // limit
  153. int16_t y1 = y < 0 ? 0 : y; // limit
  154. int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
  155. int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
  156. int16_t dx = x1 - x;
  157. int16_t dy = y1 - y;
  158. w1 -= dx;
  159. h1 -= dy;
  160. if ((w1 <= 0) || (h1 <= 0)) return;
  161. if (!_using_partial_mode) _Init_Part();
  162. _setPartialRamArea_270c(0x14, x1, y1, w1, h1);
  163. for (int16_t i = 0; i < h1; i++)
  164. {
  165. for (int16_t j = 0; j < w1 / 8; j++)
  166. {
  167. uint8_t data;
  168. // use wb_bitmap, h_bitmap of bitmap for index!
  169. int16_t idx = mirror_y ? x_part / 8 + j + dx / 8 + ((h_bitmap - 1 - (y_part + i + dy))) * wb_bitmap : x_part / 8 + j + dx / 8 + (y_part + i + dy) * wb_bitmap;
  170. if (pgm)
  171. {
  172. #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
  173. data = pgm_read_byte(&black[idx]);
  174. #else
  175. data = black[idx];
  176. #endif
  177. }
  178. else
  179. {
  180. data = black[idx];
  181. }
  182. if (invert) data = ~data;
  183. _writeData(~data);
  184. }
  185. }
  186. _setPartialRamArea_270c(0x15, x1, y1, w1, h1);
  187. for (int16_t i = 0; i < h1; i++)
  188. {
  189. for (int16_t j = 0; j < w1 / 8; j++)
  190. {
  191. uint8_t data = 0xFF;
  192. if (color)
  193. {
  194. // use wb_bitmap, h_bitmap of bitmap for index!
  195. int16_t idx = mirror_y ? x_part / 8 + j + dx / 8 + ((h_bitmap - 1 - (y_part + i + dy))) * wb_bitmap : x_part / 8 + j + dx / 8 + (y_part + i + dy) * wb_bitmap;
  196. if (pgm)
  197. {
  198. #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
  199. data = pgm_read_byte(&color[idx]);
  200. #else
  201. data = color[idx];
  202. #endif
  203. }
  204. else
  205. {
  206. data = color[idx];
  207. }
  208. if (invert) data = ~data;
  209. }
  210. _writeData(~data);
  211. }
  212. }
  213. delay(1); // yield() to avoid WDT on ESP8266 and ESP32
  214. }
  215. void GxEPD2_270c::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)
  216. {
  217. if (data1)
  218. {
  219. writeImage(data1, x, y, w, h, invert, mirror_y, pgm);
  220. }
  221. }
  222. void GxEPD2_270c::drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  223. {
  224. writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
  225. refresh(x, y, w, h);
  226. }
  227. void GxEPD2_270c::drawImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
  228. int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  229. {
  230. writeImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
  231. refresh(x, y, w, h);
  232. }
  233. void GxEPD2_270c::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)
  234. {
  235. writeImage(black, color, x, y, w, h, invert, mirror_y, pgm);
  236. refresh(x, y, w, h);
  237. }
  238. void GxEPD2_270c::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,
  239. int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  240. {
  241. writeImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
  242. refresh(x, y, w, h);
  243. }
  244. void GxEPD2_270c::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)
  245. {
  246. writeNative(data1, data2, x, y, w, h, invert, mirror_y, pgm);
  247. refresh(x, y, w, h);
  248. }
  249. void GxEPD2_270c::refresh(bool partial_update_mode)
  250. {
  251. if (partial_update_mode) refresh(0, 0, WIDTH, HEIGHT);
  252. else _Update_Full();
  253. }
  254. void GxEPD2_270c::refresh(int16_t x, int16_t y, int16_t w, int16_t h)
  255. {
  256. x -= x % 8; // byte boundary
  257. w -= x % 8; // byte boundary
  258. int16_t x1 = x < 0 ? 0 : x; // limit
  259. int16_t y1 = y < 0 ? 0 : y; // limit
  260. int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
  261. int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
  262. w1 -= x1 - x;
  263. h1 -= y1 - y;
  264. //_refreshWindow(x1, y1, w1, h1);
  265. w1 = (w1 + 7 + (x1 % 8)) & 0xfff8; // byte boundary exclusive (round up)
  266. h1 = gx_uint16_min(h1, 256); // strange controller error
  267. _writeCommand(0x16);
  268. _writeData(x1 >> 8);
  269. _writeData(x1 & 0xf8);
  270. _writeData(y1 >> 8);
  271. _writeData(y1 & 0xff);
  272. _writeData(w1 >> 8);
  273. _writeData(w1 & 0xf8);
  274. _writeData(h1 >> 8);
  275. _writeData(h1 & 0xff);
  276. _waitWhileBusy("refresh", partial_refresh_time);
  277. }
  278. void GxEPD2_270c::powerOff()
  279. {
  280. _PowerOff();
  281. }
  282. void GxEPD2_270c::hibernate()
  283. {
  284. _PowerOff();
  285. if (_rst >= 0)
  286. {
  287. _writeCommand(0x07); // deep sleep
  288. _writeData(0xA5); // check code
  289. _hibernating = true;
  290. }
  291. }
  292. void GxEPD2_270c::_setPartialRamArea_270c(uint8_t cmd, uint16_t x, uint16_t y, uint16_t w, uint16_t h)
  293. {
  294. w = (w + 7 + (x % 8)) & 0xfff8; // byte boundary exclusive (round up)
  295. _writeCommand(cmd);
  296. _writeData(x >> 8);
  297. _writeData(x & 0xf8);
  298. _writeData(y >> 8);
  299. _writeData(y & 0xff);
  300. _writeData(w >> 8);
  301. _writeData(w & 0xf8);
  302. _writeData(h >> 8);
  303. _writeData(h & 0xff);
  304. }
  305. void GxEPD2_270c::_PowerOn()
  306. {
  307. if (!_power_is_on)
  308. {
  309. _writeCommand(0x04);
  310. _waitWhileBusy("_PowerOn", power_on_time);
  311. }
  312. _power_is_on = true;
  313. }
  314. void GxEPD2_270c::_PowerOff()
  315. {
  316. _writeCommand(0x02); // power off
  317. _waitWhileBusy("_PowerOff", power_off_time);
  318. _power_is_on = false;
  319. }
  320. void GxEPD2_270c::_InitDisplay()
  321. {
  322. if (_hibernating) _reset();
  323. _writeCommand(0x01);
  324. _writeData (0x03);
  325. _writeData (0x00);
  326. _writeData (0x2b);
  327. _writeData (0x2b);
  328. _writeData (0x09);
  329. _writeCommand(0x06);
  330. _writeData (0x07);
  331. _writeData (0x07);
  332. _writeData (0x17);
  333. _writeCommand(0xF8);
  334. _writeData (0x60);
  335. _writeData (0xA5);
  336. _writeCommand(0xF8);
  337. _writeData (0x89);
  338. _writeData (0xA5);
  339. _writeCommand(0xF8);
  340. _writeData (0x90);
  341. _writeData (0x00);
  342. _writeCommand(0xF8);
  343. _writeData (0x93);
  344. _writeData (0x2A);
  345. _writeCommand(0xF8);
  346. _writeData (0x73);
  347. _writeData (0x41);
  348. _writeCommand(0x16);
  349. _writeData(0x00);
  350. //_writeCommand(0x04);
  351. //_waitWhileBusy("_wakeUp Power On");
  352. _writeCommand(0x00);
  353. _writeData(0xaf); // by register LUT
  354. _writeCommand(0x30);
  355. _writeData (0x3a);
  356. _writeCommand(0x61);
  357. _writeData (0x00);
  358. _writeData (0xb0); //176
  359. _writeData (0x01);
  360. _writeData (0x08); //264
  361. _writeCommand(0x82);
  362. _writeData (0x12);
  363. _writeCommand(0X50);
  364. _writeData(0x87);
  365. }
  366. const uint8_t GxEPD2_270c::lut_20_vcomDC[] PROGMEM =
  367. {
  368. 0x00 , 0x00,
  369. 0x00 , 0x1A , 0x1A , 0x00 , 0x00 , 0x01,
  370. 0x00 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  371. 0x00 , 0x0E , 0x01 , 0x0E , 0x01 , 0x10,
  372. 0x00 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  373. 0x00 , 0x04 , 0x10 , 0x00 , 0x00 , 0x05,
  374. 0x00 , 0x03 , 0x0E , 0x00 , 0x00 , 0x0A,
  375. 0x00 , 0x23 , 0x00 , 0x00 , 0x00 , 0x01
  376. };
  377. //R21H
  378. const uint8_t GxEPD2_270c::lut_21[] PROGMEM = {
  379. 0x90 , 0x1A , 0x1A , 0x00 , 0x00 , 0x01,
  380. 0x40 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  381. 0x84 , 0x0E , 0x01 , 0x0E , 0x01 , 0x10,
  382. 0x80 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  383. 0x00 , 0x04 , 0x10 , 0x00 , 0x00 , 0x05,
  384. 0x00 , 0x03 , 0x0E , 0x00 , 0x00 , 0x0A,
  385. 0x00 , 0x23 , 0x00 , 0x00 , 0x00 , 0x01
  386. };
  387. //R22H r
  388. const uint8_t GxEPD2_270c::lut_22_red[] PROGMEM = {
  389. 0xA0 , 0x1A , 0x1A , 0x00 , 0x00 , 0x01,
  390. 0x00 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  391. 0x84 , 0x0E , 0x01 , 0x0E , 0x01 , 0x10,
  392. 0x90 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  393. 0xB0 , 0x04 , 0x10 , 0x00 , 0x00 , 0x05,
  394. 0xB0 , 0x03 , 0x0E , 0x00 , 0x00 , 0x0A,
  395. 0xC0 , 0x23 , 0x00 , 0x00 , 0x00 , 0x01
  396. };
  397. //R23H w
  398. const uint8_t GxEPD2_270c::lut_23_white[] PROGMEM = {
  399. 0x90 , 0x1A , 0x1A , 0x00 , 0x00 , 0x01,
  400. 0x40 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  401. 0x84 , 0x0E , 0x01 , 0x0E , 0x01 , 0x10,
  402. 0x80 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  403. 0x00 , 0x04 , 0x10 , 0x00 , 0x00 , 0x05,
  404. 0x00 , 0x03 , 0x0E , 0x00 , 0x00 , 0x0A,
  405. 0x00 , 0x23 , 0x00 , 0x00 , 0x00 , 0x01
  406. };
  407. //R24H b
  408. const uint8_t GxEPD2_270c::lut_24_black[] PROGMEM = {
  409. 0x90 , 0x1A , 0x1A , 0x00 , 0x00 , 0x01,
  410. 0x20 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  411. 0x84 , 0x0E , 0x01 , 0x0E , 0x01 , 0x10,
  412. 0x10 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  413. 0x00 , 0x04 , 0x10 , 0x00 , 0x00 , 0x05,
  414. 0x00 , 0x03 , 0x0E , 0x00 , 0x00 , 0x0A,
  415. 0x00 , 0x23 , 0x00 , 0x00 , 0x00 , 0x01
  416. };
  417. void GxEPD2_270c::_Init_Full()
  418. {
  419. _InitDisplay();
  420. _writeCommand(0x20); //vcom
  421. _writeDataPGM_sCS(lut_20_vcomDC, sizeof(lut_20_vcomDC));
  422. _writeCommand(0x21); //ww --
  423. _writeDataPGM_sCS(lut_21, sizeof(lut_21));
  424. _writeCommand(0x22); //bw r
  425. _writeDataPGM_sCS(lut_22_red, sizeof(lut_22_red));
  426. _writeCommand(0x23); //wb w
  427. _writeDataPGM_sCS(lut_23_white, sizeof(lut_23_white));
  428. _writeCommand(0x24); //bb b
  429. _writeDataPGM_sCS(lut_24_black, sizeof(lut_24_black));
  430. _PowerOn();
  431. }
  432. void GxEPD2_270c::_Init_Part()
  433. {
  434. _InitDisplay();
  435. _writeCommand(0x20); //vcom
  436. _writeDataPGM_sCS(lut_20_vcomDC, sizeof(lut_20_vcomDC));
  437. _writeCommand(0x21); //ww --
  438. _writeDataPGM_sCS(lut_21, sizeof(lut_21));
  439. _writeCommand(0x22); //bw r
  440. _writeDataPGM_sCS(lut_22_red, sizeof(lut_22_red));
  441. _writeCommand(0x23); //wb w
  442. _writeDataPGM_sCS(lut_23_white, sizeof(lut_23_white));
  443. _writeCommand(0x24); //bb b
  444. _writeDataPGM_sCS(lut_24_black, sizeof(lut_24_black));
  445. _PowerOn();
  446. }
  447. void GxEPD2_270c::_Update_Full()
  448. {
  449. _writeCommand(0x12); //display refresh
  450. _waitWhileBusy("_Update_Full", full_refresh_time);
  451. }
  452. void GxEPD2_270c::_Update_Part()
  453. {
  454. _writeCommand(0x12); //display refresh
  455. _waitWhileBusy("_Update_Part", partial_refresh_time);
  456. }