GxEPD2_583.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  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: IL0371 : http://www.e-paper-display.com/download_detail/downloadsId=536.html
  6. //
  7. // Author: Jean-Marc Zingg
  8. //
  9. // Version: see library.properties
  10. //
  11. // Library: https://github.com/ZinggJM/GxEPD2
  12. #include "GxEPD2_583.h"
  13. GxEPD2_583::GxEPD2_583(int16_t cs, int16_t dc, int16_t rst, int16_t busy) :
  14. GxEPD2_EPD(cs, dc, rst, busy, LOW, 20000000, WIDTH, HEIGHT, panel, hasColor, hasPartialUpdate, hasFastPartialUpdate)
  15. {
  16. }
  17. void GxEPD2_583::clearScreen(uint8_t value)
  18. {
  19. writeScreenBuffer(value);
  20. refresh(true);
  21. }
  22. void GxEPD2_583::writeScreenBuffer(uint8_t value)
  23. {
  24. _initial_write = false; // initial full screen buffer clean done
  25. if (!_using_partial_mode) _Init_Part();
  26. if (value == 0xFF) value = 0x33; // white value for this controller
  27. _writeCommand(0x10);
  28. _startTransfer();
  29. for (uint32_t i = 0; i < uint32_t(WIDTH) * uint32_t(HEIGHT) / 2; i++)
  30. {
  31. _transfer(value);
  32. }
  33. _endTransfer();
  34. }
  35. void GxEPD2_583::writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  36. {
  37. if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
  38. delay(1); // yield() to avoid WDT on ESP8266 and ESP32
  39. uint16_t wb = (w + 7) / 8; // width bytes, bitmaps are padded
  40. x -= x % 8; // byte boundary
  41. w = wb * 8; // byte boundary
  42. int16_t x1 = x < 0 ? 0 : x; // limit
  43. int16_t y1 = y < 0 ? 0 : y; // limit
  44. int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
  45. int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
  46. int16_t dx = x1 - x;
  47. int16_t dy = y1 - y;
  48. w1 -= dx;
  49. h1 -= dy;
  50. if ((w1 <= 0) || (h1 <= 0)) return;
  51. if (!_using_partial_mode) _Init_Part();
  52. _writeCommand(0x91); // partial in
  53. _setPartialRamArea(x1, y1, w1, h1);
  54. _writeCommand(0x10);
  55. _startTransfer();
  56. for (int16_t i = 0; i < h1; i++)
  57. {
  58. for (int16_t j = 0; j < w1 / 8; j++)
  59. {
  60. uint8_t data;
  61. // use wb, h of bitmap for index!
  62. uint16_t idx = mirror_y ? j + dx / 8 + uint16_t((h - 1 - (i + dy))) * wb : j + dx / 8 + uint16_t(i + dy) * wb;
  63. if (pgm)
  64. {
  65. #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
  66. data = pgm_read_byte(&bitmap[idx]);
  67. #else
  68. data = bitmap[idx];
  69. #endif
  70. }
  71. else
  72. {
  73. data = bitmap[idx];
  74. }
  75. if (invert) data = ~data;
  76. _send8pixel(~data);
  77. }
  78. }
  79. _endTransfer();
  80. _writeCommand(0x92); // partial out
  81. delay(1); // yield() to avoid WDT on ESP8266 and ESP32
  82. }
  83. void GxEPD2_583::writeImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
  84. int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  85. {
  86. if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
  87. delay(1); // yield() to avoid WDT on ESP8266 and ESP32
  88. if ((w_bitmap < 0) || (h_bitmap < 0) || (w < 0) || (h < 0)) return;
  89. if ((x_part < 0) || (x_part >= w_bitmap)) return;
  90. if ((y_part < 0) || (y_part >= h_bitmap)) return;
  91. uint16_t wb_bitmap = (w_bitmap + 7) / 8; // width bytes, bitmaps are padded
  92. x_part -= x_part % 8; // byte boundary
  93. w = w_bitmap - x_part < w ? w_bitmap - x_part : w; // limit
  94. h = h_bitmap - y_part < h ? h_bitmap - y_part : h; // limit
  95. x -= x % 8; // byte boundary
  96. w = 8 * ((w + 7) / 8); // byte boundary, bitmaps are padded
  97. int16_t x1 = x < 0 ? 0 : x; // limit
  98. int16_t y1 = y < 0 ? 0 : y; // limit
  99. int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
  100. int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
  101. int16_t dx = x1 - x;
  102. int16_t dy = y1 - y;
  103. w1 -= dx;
  104. h1 -= dy;
  105. if ((w1 <= 0) || (h1 <= 0)) return;
  106. if (!_using_partial_mode) _Init_Part();
  107. _writeCommand(0x91); // partial in
  108. _setPartialRamArea(x1, y1, w1, h1);
  109. _writeCommand(0x10);
  110. _startTransfer();
  111. for (int16_t i = 0; i < h1; i++)
  112. {
  113. for (int16_t j = 0; j < w1 / 8; j++)
  114. {
  115. uint8_t data;
  116. // use wb_bitmap, h_bitmap of bitmap for index!
  117. uint16_t idx = mirror_y ? x_part / 8 + j + dx / 8 + uint16_t((h_bitmap - 1 - (y_part + i + dy))) * wb_bitmap : x_part / 8 + j + dx / 8 + uint16_t(y_part + i + dy) * wb_bitmap;
  118. if (pgm)
  119. {
  120. #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
  121. data = pgm_read_byte(&bitmap[idx]);
  122. #else
  123. data = bitmap[idx];
  124. #endif
  125. }
  126. else
  127. {
  128. data = bitmap[idx];
  129. }
  130. if (invert) data = ~data;
  131. _send8pixel(~data);
  132. }
  133. }
  134. _endTransfer();
  135. _writeCommand(0x92); // partial out
  136. delay(1); // yield() to avoid WDT on ESP8266 and ESP32
  137. }
  138. void GxEPD2_583::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)
  139. {
  140. if (black)
  141. {
  142. writeImage(black, x, y, w, h, invert, mirror_y, pgm);
  143. }
  144. }
  145. void GxEPD2_583::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,
  146. int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  147. {
  148. if (black)
  149. {
  150. writeImagePart(black, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
  151. }
  152. }
  153. void GxEPD2_583::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)
  154. {
  155. if (data1)
  156. {
  157. if (_initial_write) writeScreenBuffer(); // initial full screen buffer clean
  158. delay(1); // yield() to avoid WDT on ESP8266 and ESP32
  159. uint16_t wb = (w + 1) / 2; // width bytes, bitmaps are padded
  160. x -= x % 2; // byte boundary
  161. w = wb * 2; // byte boundary
  162. int16_t x1 = x < 0 ? 0 : x; // limit
  163. int16_t y1 = y < 0 ? 0 : y; // limit
  164. int16_t w1 = x + w < int16_t(WIDTH) ? w : int16_t(WIDTH) - x; // limit
  165. int16_t h1 = y + h < int16_t(HEIGHT) ? h : int16_t(HEIGHT) - y; // limit
  166. int16_t dx = x1 - x;
  167. int16_t dy = y1 - y;
  168. w1 -= dx;
  169. h1 -= dy;
  170. if ((w1 <= 0) || (h1 <= 0)) return;
  171. if (!_using_partial_mode) _Init_Part();
  172. _writeCommand(0x91); // partial in
  173. _setPartialRamArea(x1, y1, w1, h1);
  174. _writeCommand(0x10);
  175. _startTransfer();
  176. for (int16_t i = 0; i < h1; i++)
  177. {
  178. for (int16_t j = 0; j < w1 / 2; j++)
  179. {
  180. uint8_t data;
  181. // use wb, h of bitmap for index!
  182. uint16_t idx = mirror_y ? j + dx / 2 + uint16_t((h - 1 - (i + dy))) * wb : j + dx / 2 + uint16_t(i + dy) * wb;
  183. if (pgm)
  184. {
  185. #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
  186. data = pgm_read_byte(&data1[idx]);
  187. #else
  188. data = data1[idx];
  189. #endif
  190. }
  191. else
  192. {
  193. data = data1[idx];
  194. }
  195. if (invert) data = ~data;
  196. _transfer(data);
  197. }
  198. }
  199. _endTransfer();
  200. _writeCommand(0x92); // partial out
  201. delay(1); // yield() to avoid WDT on ESP8266 and ESP32
  202. }
  203. }
  204. void GxEPD2_583::drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  205. {
  206. writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
  207. refresh(x, y, w, h);
  208. }
  209. void GxEPD2_583::drawImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
  210. int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  211. {
  212. writeImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
  213. refresh(x, y, w, h);
  214. }
  215. void GxEPD2_583::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)
  216. {
  217. writeImage(black, color, x, y, w, h, invert, mirror_y, pgm);
  218. refresh(x, y, w, h);
  219. }
  220. void GxEPD2_583::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,
  221. int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  222. {
  223. writeImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
  224. refresh(x, y, w, h);
  225. }
  226. void GxEPD2_583::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)
  227. {
  228. writeNative(data1, data2, x, y, w, h, invert, mirror_y, pgm);
  229. refresh(x, y, w, h);
  230. }
  231. void GxEPD2_583::refresh(bool partial_update_mode)
  232. {
  233. if (partial_update_mode) refresh(0, 0, WIDTH, HEIGHT);
  234. else
  235. {
  236. if (_using_partial_mode) _Init_Full();
  237. _Update_Full();
  238. }
  239. }
  240. void GxEPD2_583::refresh(int16_t x, int16_t y, int16_t w, int16_t h)
  241. {
  242. // intersection with screen
  243. int16_t w1 = x < 0 ? w + x : w; // reduce
  244. int16_t h1 = y < 0 ? h + y : h; // reduce
  245. int16_t x1 = x < 0 ? 0 : x; // limit
  246. int16_t y1 = y < 0 ? 0 : y; // limit
  247. w1 = x1 + w1 < int16_t(WIDTH) ? w1 : int16_t(WIDTH) - x1; // limit
  248. h1 = y1 + h1 < int16_t(HEIGHT) ? h1 : int16_t(HEIGHT) - y1; // limit
  249. if ((w1 <= 0) || (h1 <= 0)) return;
  250. // make x1, w1 multiple of 8
  251. w1 += x1 % 8;
  252. if (w1 % 8 > 0) w1 += 8 - w1 % 8;
  253. x1 -= x1 % 8;
  254. if (!_using_partial_mode) _Init_Part();
  255. _writeCommand(0x91); // partial in
  256. _setPartialRamArea(x1, y1, w1, h1);
  257. _Update_Part();
  258. _writeCommand(0x92); // partial out
  259. }
  260. void GxEPD2_583::powerOff(void)
  261. {
  262. _PowerOff();
  263. }
  264. void GxEPD2_583::hibernate()
  265. {
  266. _PowerOff();
  267. if (_rst >= 0)
  268. {
  269. // check if it supports this command!
  270. _writeCommand(0x07); // deep sleep
  271. _writeData(0xA5); // check code
  272. _hibernating = true;
  273. }
  274. }
  275. void GxEPD2_583::_send8pixel(uint8_t data)
  276. {
  277. for (uint8_t j = 0; j < 8; j++)
  278. {
  279. uint8_t t = data & 0x80 ? 0x00 : 0x03;
  280. t <<= 4;
  281. data <<= 1;
  282. j++;
  283. t |= data & 0x80 ? 0x00 : 0x03;
  284. data <<= 1;
  285. _transfer(t);
  286. }
  287. }
  288. void GxEPD2_583::_setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
  289. {
  290. uint16_t xe = (x + w - 1) | 0x0007; // byte boundary inclusive (last byte)
  291. uint16_t ye = y + h - 1;
  292. x &= 0xFFF8; // byte boundary
  293. _writeCommand(0x90); // partial window
  294. _writeData(x / 256);
  295. _writeData(x % 256);
  296. _writeData(xe / 256);
  297. _writeData(xe % 256);
  298. _writeData(y / 256);
  299. _writeData(y % 256);
  300. _writeData(ye / 256);
  301. _writeData(ye % 256);
  302. _writeData(0x01); // don't see any difference
  303. //_writeData(0x00); // don't see any difference
  304. }
  305. void GxEPD2_583::_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_583::_PowerOff()
  315. {
  316. _writeCommand(0x02); // power off
  317. _waitWhileBusy("_PowerOff", power_off_time);
  318. _power_is_on = false;
  319. _using_partial_mode = false;
  320. }
  321. void GxEPD2_583::_InitDisplay()
  322. {
  323. if (_hibernating) _reset();
  324. _writeCommand(0x01);
  325. _writeData (0x37); // POWER SETTING
  326. _writeData (0x00);
  327. _writeCommand(0X00); // PANNEL SETTING
  328. _writeData(0xCF);
  329. _writeData(0x08);
  330. _writeCommand(0x06); // boost
  331. _writeData (0xc7);
  332. _writeData (0xcc);
  333. _writeData (0x28);
  334. _writeCommand(0x30); // PLL setting
  335. //_writeData (0x39); // PLL: 7s refresh
  336. _writeData (0x3a); // PLL: 15s refresh
  337. //_writeData (0x3c); // PLL: 30s refresh
  338. _writeCommand(0X41); // TEMPERATURE SETTING
  339. _writeData(0x00);
  340. _writeCommand(0X50); // VCOM AND DATA INTERVAL SETTING
  341. _writeData(0x77);
  342. _writeCommand(0X60); // TCON SETTING
  343. _writeData(0x22);
  344. _writeCommand(0x61); // 600*448
  345. _writeData (0x02); // source 600
  346. _writeData (0x58);
  347. _writeData (0x01); // gate 448
  348. _writeData (0xc0);
  349. _writeCommand(0X82); // VCOM VOLTAGE SETTING
  350. _writeData(0x28); // all temperature range
  351. _writeCommand(0xe5); // FLASH MODE
  352. _writeData(0x03);
  353. }
  354. void GxEPD2_583::_Init_Full()
  355. {
  356. _InitDisplay();
  357. _PowerOn();
  358. _using_partial_mode = false;
  359. }
  360. void GxEPD2_583::_Init_Part()
  361. {
  362. _InitDisplay();
  363. _PowerOn();
  364. _using_partial_mode = true;
  365. }
  366. void GxEPD2_583::_Update_Full()
  367. {
  368. _writeCommand(0x12); //display refresh
  369. _waitWhileBusy("_Update_Full", full_refresh_time);
  370. }
  371. void GxEPD2_583::_Update_Part()
  372. {
  373. _writeCommand(0x12); //display refresh
  374. _waitWhileBusy("_Update_Part", partial_refresh_time);
  375. }