GxEPD2_270c.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  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. _Init_Part();
  24. _setPartialRamArea_270c(0x14, 0, 0, WIDTH, HEIGHT);
  25. for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
  26. {
  27. _writeData(~black_value);
  28. }
  29. _setPartialRamArea_270c(0x15, 0, 0, WIDTH, HEIGHT);
  30. for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
  31. {
  32. _writeData(~red_value);
  33. }
  34. refresh(0, 0, WIDTH, HEIGHT);
  35. }
  36. void GxEPD2_270c::writeScreenBuffer(uint8_t value)
  37. {
  38. writeScreenBuffer(value, 0xFF);
  39. }
  40. void GxEPD2_270c::writeScreenBuffer(uint8_t black_value, uint8_t color_value)
  41. {
  42. _Init_Part();
  43. _setPartialRamArea_270c(0x14, 0, 0, WIDTH, HEIGHT);
  44. for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
  45. {
  46. _writeData(~black_value);
  47. }
  48. _setPartialRamArea_270c(0x15, 0, 0, WIDTH, HEIGHT);
  49. for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 8; i++)
  50. {
  51. _writeData(~color_value);
  52. }
  53. }
  54. 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)
  55. {
  56. writeImage(bitmap, NULL, x, y, w, h, invert, mirror_y, pgm);
  57. }
  58. 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)
  59. {
  60. delay(1); // yield() to avoid WDT on ESP8266 and ESP32
  61. int16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
  62. x -= x % 8; // byte boundary
  63. w = wb * 8; // byte boundary
  64. int16_t x1 = x < 0 ? 0 : x; // limit
  65. int16_t y1 = y < 0 ? 0 : y; // limit
  66. int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
  67. int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
  68. int16_t dx = x1 - x;
  69. int16_t dy = y1 - y;
  70. w1 -= dx;
  71. h1 -= dy;
  72. if ((w1 <= 0) || (h1 <= 0)) return;
  73. _Init_Part();
  74. _setPartialRamArea_270c(0x14, x1, y1, w1, h1);
  75. for (int16_t i = 0; i < h1; i++)
  76. {
  77. for (int16_t j = 0; j < w1 / 8; j++)
  78. {
  79. uint8_t data = 0xFF;
  80. if (black)
  81. {
  82. // use wb, h of bitmap for index!
  83. int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
  84. if (pgm)
  85. {
  86. #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
  87. data = pgm_read_byte(&black[idx]);
  88. #else
  89. data = black[idx];
  90. #endif
  91. }
  92. else
  93. {
  94. data = black[idx];
  95. }
  96. if (invert) data = ~data;
  97. }
  98. _writeData(~data);
  99. }
  100. }
  101. _setPartialRamArea_270c(0x15, x1, y1, w1, h1);
  102. for (int16_t i = 0; i < h1; i++)
  103. {
  104. for (int16_t j = 0; j < w1 / 8; j++)
  105. {
  106. uint8_t data = 0xFF;
  107. if (color)
  108. {
  109. // use wb, h of bitmap for index!
  110. int16_t idx = mirror_y ? j + dx / 8 + ((h - 1 - (i + dy))) * wb : j + dx / 8 + (i + dy) * wb;
  111. if (pgm)
  112. {
  113. #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
  114. data = pgm_read_byte(&color[idx]);
  115. #else
  116. data = color[idx];
  117. #endif
  118. }
  119. else
  120. {
  121. data = color[idx];
  122. }
  123. if (invert) data = ~data;
  124. }
  125. _writeData(~data);
  126. }
  127. }
  128. delay(1); // yield() to avoid WDT on ESP8266 and ESP32
  129. }
  130. 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)
  131. {
  132. if (data1)
  133. {
  134. writeImage(data1, x, y, w, h, invert, mirror_y, pgm);
  135. }
  136. }
  137. 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)
  138. {
  139. writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
  140. refresh(x, y, w, h);
  141. }
  142. 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)
  143. {
  144. writeImage(black, color, x, y, w, h, invert, mirror_y, pgm);
  145. refresh(x, y, w, h);
  146. }
  147. 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)
  148. {
  149. writeNative(data1, data2, x, y, w, h, invert, mirror_y, pgm);
  150. refresh(x, y, w, h);
  151. }
  152. void GxEPD2_270c::refresh(bool partial_update_mode)
  153. {
  154. if (partial_update_mode) refresh(0, 0, WIDTH, HEIGHT);
  155. else _Update_Full();
  156. }
  157. void GxEPD2_270c::refresh(int16_t x, int16_t y, int16_t w, int16_t h)
  158. {
  159. x -= x % 8; // byte boundary
  160. w -= x % 8; // byte boundary
  161. int16_t x1 = x < 0 ? 0 : x; // limit
  162. int16_t y1 = y < 0 ? 0 : y; // limit
  163. int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
  164. int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
  165. w1 -= x1 - x;
  166. h1 -= y1 - y;
  167. //_refreshWindow(x1, y1, w1, h1);
  168. w1 = (w1 + 7 + (x1 % 8)) & 0xfff8; // byte boundary exclusive (round up)
  169. h1 = gx_uint16_min(h1, 256); // strange controller error
  170. _writeCommand(0x16);
  171. _writeData(x1 >> 8);
  172. _writeData(x1 & 0xf8);
  173. _writeData(y1 >> 8);
  174. _writeData(y1 & 0xff);
  175. _writeData(w1 >> 8);
  176. _writeData(w1 & 0xf8);
  177. _writeData(h1 >> 8);
  178. _writeData(h1 & 0xff);
  179. _waitWhileBusy("refresh", partial_refresh_time);
  180. }
  181. void GxEPD2_270c::powerOff()
  182. {
  183. _PowerOff();
  184. }
  185. void GxEPD2_270c::hibernate()
  186. {
  187. _PowerOff();
  188. if (_rst >= 0)
  189. {
  190. _writeCommand(0x07); // deep sleep
  191. _writeData(0xA5); // check code
  192. _hibernating = true;
  193. }
  194. }
  195. void GxEPD2_270c::_writeData_nCS(const uint8_t* data, uint16_t n)
  196. {
  197. SPI.beginTransaction(_spi_settings);
  198. for (uint8_t i = 0; i < n; i++)
  199. {
  200. if (_cs >= 0) digitalWrite(_cs, LOW);
  201. SPI.transfer(pgm_read_byte(&*data++));
  202. if (_cs >= 0) digitalWrite(_cs, HIGH);
  203. }
  204. SPI.endTransaction();
  205. }
  206. void GxEPD2_270c::_setPartialRamArea_270c(uint8_t cmd, uint16_t x, uint16_t y, uint16_t w, uint16_t h)
  207. {
  208. w = (w + 7 + (x % 8)) & 0xfff8; // byte boundary exclusive (round up)
  209. _writeCommand(cmd);
  210. _writeData(x >> 8);
  211. _writeData(x & 0xf8);
  212. _writeData(y >> 8);
  213. _writeData(y & 0xff);
  214. _writeData(w >> 8);
  215. _writeData(w & 0xf8);
  216. _writeData(h >> 8);
  217. _writeData(h & 0xff);
  218. }
  219. void GxEPD2_270c::_PowerOn()
  220. {
  221. if (!_power_is_on)
  222. {
  223. _writeCommand(0x04);
  224. _waitWhileBusy("_PowerOn", power_on_time);
  225. }
  226. _power_is_on = true;
  227. }
  228. void GxEPD2_270c::_PowerOff()
  229. {
  230. _writeCommand(0x02); // power off
  231. _waitWhileBusy("_PowerOff", power_off_time);
  232. _power_is_on = false;
  233. }
  234. void GxEPD2_270c::_InitDisplay()
  235. {
  236. if (_hibernating) _reset();
  237. _writeCommand(0x01);
  238. _writeData (0x03);
  239. _writeData (0x00);
  240. _writeData (0x2b);
  241. _writeData (0x2b);
  242. _writeData (0x09);
  243. _writeCommand(0x06);
  244. _writeData (0x07);
  245. _writeData (0x07);
  246. _writeData (0x17);
  247. _writeCommand(0xF8);
  248. _writeData (0x60);
  249. _writeData (0xA5);
  250. _writeCommand(0xF8);
  251. _writeData (0x89);
  252. _writeData (0xA5);
  253. _writeCommand(0xF8);
  254. _writeData (0x90);
  255. _writeData (0x00);
  256. _writeCommand(0xF8);
  257. _writeData (0x93);
  258. _writeData (0x2A);
  259. _writeCommand(0xF8);
  260. _writeData (0x73);
  261. _writeData (0x41);
  262. _writeCommand(0x16);
  263. _writeData(0x00);
  264. //_writeCommand(0x04);
  265. //_waitWhileBusy("_wakeUp Power On");
  266. _writeCommand(0x00);
  267. _writeData(0xaf); // by register LUT
  268. _writeCommand(0x30);
  269. _writeData (0x3a);
  270. _writeCommand(0x61);
  271. _writeData (0x00);
  272. _writeData (0xb0); //176
  273. _writeData (0x01);
  274. _writeData (0x08); //264
  275. _writeCommand(0x82);
  276. _writeData (0x12);
  277. _writeCommand(0X50);
  278. _writeData(0x87);
  279. }
  280. const uint8_t GxEPD2_270c::lut_20_vcomDC[] PROGMEM =
  281. {
  282. 0x00 , 0x00,
  283. 0x00 , 0x1A , 0x1A , 0x00 , 0x00 , 0x01,
  284. 0x00 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  285. 0x00 , 0x0E , 0x01 , 0x0E , 0x01 , 0x10,
  286. 0x00 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  287. 0x00 , 0x04 , 0x10 , 0x00 , 0x00 , 0x05,
  288. 0x00 , 0x03 , 0x0E , 0x00 , 0x00 , 0x0A,
  289. 0x00 , 0x23 , 0x00 , 0x00 , 0x00 , 0x01
  290. };
  291. //R21H
  292. const uint8_t GxEPD2_270c::lut_21[] PROGMEM = {
  293. 0x90 , 0x1A , 0x1A , 0x00 , 0x00 , 0x01,
  294. 0x40 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  295. 0x84 , 0x0E , 0x01 , 0x0E , 0x01 , 0x10,
  296. 0x80 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  297. 0x00 , 0x04 , 0x10 , 0x00 , 0x00 , 0x05,
  298. 0x00 , 0x03 , 0x0E , 0x00 , 0x00 , 0x0A,
  299. 0x00 , 0x23 , 0x00 , 0x00 , 0x00 , 0x01
  300. };
  301. //R22H r
  302. const uint8_t GxEPD2_270c::lut_22_red[] PROGMEM = {
  303. 0xA0 , 0x1A , 0x1A , 0x00 , 0x00 , 0x01,
  304. 0x00 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  305. 0x84 , 0x0E , 0x01 , 0x0E , 0x01 , 0x10,
  306. 0x90 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  307. 0xB0 , 0x04 , 0x10 , 0x00 , 0x00 , 0x05,
  308. 0xB0 , 0x03 , 0x0E , 0x00 , 0x00 , 0x0A,
  309. 0xC0 , 0x23 , 0x00 , 0x00 , 0x00 , 0x01
  310. };
  311. //R23H w
  312. const uint8_t GxEPD2_270c::lut_23_white[] PROGMEM = {
  313. 0x90 , 0x1A , 0x1A , 0x00 , 0x00 , 0x01,
  314. 0x40 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  315. 0x84 , 0x0E , 0x01 , 0x0E , 0x01 , 0x10,
  316. 0x80 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  317. 0x00 , 0x04 , 0x10 , 0x00 , 0x00 , 0x05,
  318. 0x00 , 0x03 , 0x0E , 0x00 , 0x00 , 0x0A,
  319. 0x00 , 0x23 , 0x00 , 0x00 , 0x00 , 0x01
  320. };
  321. //R24H b
  322. const uint8_t GxEPD2_270c::lut_24_black[] PROGMEM = {
  323. 0x90 , 0x1A , 0x1A , 0x00 , 0x00 , 0x01,
  324. 0x20 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  325. 0x84 , 0x0E , 0x01 , 0x0E , 0x01 , 0x10,
  326. 0x10 , 0x0A , 0x0A , 0x00 , 0x00 , 0x08,
  327. 0x00 , 0x04 , 0x10 , 0x00 , 0x00 , 0x05,
  328. 0x00 , 0x03 , 0x0E , 0x00 , 0x00 , 0x0A,
  329. 0x00 , 0x23 , 0x00 , 0x00 , 0x00 , 0x01
  330. };
  331. void GxEPD2_270c::_Init_Full()
  332. {
  333. _InitDisplay();
  334. _writeCommand(0x20); //vcom
  335. _writeData_nCS(lut_20_vcomDC, sizeof(lut_20_vcomDC));
  336. _writeCommand(0x21); //ww --
  337. _writeData_nCS(lut_21, sizeof(lut_21));
  338. _writeCommand(0x22); //bw r
  339. _writeData_nCS(lut_22_red, sizeof(lut_22_red));
  340. _writeCommand(0x23); //wb w
  341. _writeData_nCS(lut_23_white, sizeof(lut_23_white));
  342. _writeCommand(0x24); //bb b
  343. _writeData_nCS(lut_24_black, sizeof(lut_24_black));
  344. _PowerOn();
  345. }
  346. void GxEPD2_270c::_Init_Part()
  347. {
  348. _InitDisplay();
  349. _writeCommand(0x20); //vcom
  350. _writeData_nCS(lut_20_vcomDC, sizeof(lut_20_vcomDC));
  351. _writeCommand(0x21); //ww --
  352. _writeData_nCS(lut_21, sizeof(lut_21));
  353. _writeCommand(0x22); //bw r
  354. _writeData_nCS(lut_22_red, sizeof(lut_22_red));
  355. _writeCommand(0x23); //wb w
  356. _writeData_nCS(lut_23_white, sizeof(lut_23_white));
  357. _writeCommand(0x24); //bb b
  358. _writeData_nCS(lut_24_black, sizeof(lut_24_black));
  359. _PowerOn();
  360. }
  361. void GxEPD2_270c::_Update_Full()
  362. {
  363. _writeCommand(0x12); //display refresh
  364. _waitWhileBusy("_Update_Full", full_refresh_time);
  365. }
  366. void GxEPD2_270c::_Update_Part()
  367. {
  368. _writeCommand(0x12); //display refresh
  369. _waitWhileBusy("_Update_Part", partial_refresh_time);
  370. }