// class GxGDEW042Z15 : Display class for GDEW042Z15 e-Paper from Dalian Good Display Co., Ltd.: www.good-display.com // // based on Demo Example from Good Display, available here: http://www.good-display.com/download_detail/downloadsId=515.html // Controller: IL0398 : http://www.good-display.com/download_detail/downloadsId=537.html // // Author : J-M Zingg // // Version : see library.properties // // License: GNU GENERAL PUBLIC LICENSE V3, see LICENSE // // Library: https://github.com/ZinggJM/GxEPD #include "GxGDEW042Z15.h" //#define DISABLE_DIAGNOSTIC_OUTPUT // partial update produces distortion on the right half of the screen in b/w/r mode (on my display) // this workaround updates the whole screen #define USE_PARTIAL_UPDATE_WORKAROUND #define GxGDEW042Z15_BUSY_TIMEOUT 20000000 #if defined(ESP8266) || defined(ESP32) #include #else #include #endif GxGDEW042Z15::GxGDEW042Z15(GxIO& io, int8_t rst, int8_t busy) : GxEPD(GxGDEW042Z15_WIDTH, GxGDEW042Z15_HEIGHT), IO(io), _current_page(-1), _using_partial_mode(false), _diag_enabled(false), _rst(rst), _busy(busy) { } void GxGDEW042Z15::drawPixel(int16_t x, int16_t y, uint16_t color) { if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) return; // check rotation, move pixel around if necessary switch (getRotation()) { case 1: swap(x, y); x = GxGDEW042Z15_WIDTH - x - 1; break; case 2: x = GxGDEW042Z15_WIDTH - x - 1; y = GxGDEW042Z15_HEIGHT - y - 1; break; case 3: swap(x, y); y = GxGDEW042Z15_HEIGHT - y - 1; break; } uint16_t i = x / 8 + y * GxGDEW042Z15_WIDTH / 8; if (_current_page < 0) { if (i >= sizeof(_black_buffer)) return; } else { y -= _current_page * GxGDEW042Z15_PAGE_HEIGHT; if ((y < 0) || (y >= GxGDEW042Z15_PAGE_HEIGHT)) return; i = x / 8 + y * GxGDEW042Z15_WIDTH / 8; } _black_buffer[i] = (_black_buffer[i] & (0xFF ^ (1 << (7 - x % 8)))); // white _red_buffer[i] = (_red_buffer[i] & (0xFF ^ (1 << (7 - x % 8)))); // white if (color == GxEPD_WHITE) return; else if (color == GxEPD_BLACK) _black_buffer[i] = (_black_buffer[i] | (1 << (7 - x % 8))); else if (color == GxEPD_RED) _red_buffer[i] = (_red_buffer[i] | (1 << (7 - x % 8))); else { if ((color & 0xF100) > (0xF100 / 2)) _red_buffer[i] = (_red_buffer[i] | (1 << (7 - x % 8))); else if ((((color & 0xF100) >> 11) + ((color & 0x07E0) >> 5) + (color & 0x001F)) < 3 * 255 / 2) { _black_buffer[i] = (_black_buffer[i] | (1 << (7 - x % 8))); } } } void GxGDEW042Z15::init(uint32_t serial_diag_bitrate) { if (serial_diag_bitrate > 0) { Serial.begin(serial_diag_bitrate); _diag_enabled = true; } IO.init(); IO.setFrequency(4000000); // 4MHz if (_rst >= 0) { digitalWrite(_rst, HIGH); pinMode(_rst, OUTPUT); } pinMode(_busy, INPUT); fillScreen(GxEPD_WHITE); _current_page = -1; _using_partial_mode = false; } void GxGDEW042Z15::fillScreen(uint16_t color) { uint8_t black = 0x00; uint8_t red = 0x00; if (color == GxEPD_WHITE); else if (color == GxEPD_BLACK) black = 0xFF; else if (color == GxEPD_RED) red = 0xFF; else if ((color & 0xF100) > (0xF100 / 2)) red = 0xFF; else if ((((color & 0xF100) >> 11) + ((color & 0x07E0) >> 5) + (color & 0x001F)) < 3 * 255 / 2) black = 0xFF; for (uint16_t x = 0; x < sizeof(_black_buffer); x++) { _black_buffer[x] = black; _red_buffer[x] = red; } } void GxGDEW042Z15::update(void) { if (_current_page != -1) return; _using_partial_mode = false; _wakeUp(); IO.writeCommandTransaction(0x10); // black for (uint32_t i = 0; i < GxGDEW042Z15_BUFFER_SIZE; i++) { uint8_t data = i < sizeof(_black_buffer) ? _black_buffer[i] : 0x00; IO.writeDataTransaction(~data); } IO.writeCommandTransaction(0x13); // red for (uint32_t i = 0; i < GxGDEW042Z15_BUFFER_SIZE; i++) { uint8_t data = i < sizeof(_red_buffer) ? _red_buffer[i] : 0x00; IO.writeDataTransaction(~data); } IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("update"); _sleep(); } void GxGDEW042Z15::drawBitmap(const uint8_t *bitmap, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color, int16_t mode) { if (mode & bm_default) mode |= bm_invert; drawBitmapBM(bitmap, x, y, w, h, color, mode); } void GxGDEW042Z15::drawExamplePicture(const uint8_t* black_bitmap, const uint8_t* red_bitmap, uint32_t black_size, uint32_t red_size) { drawPicture(black_bitmap, red_bitmap, black_size, red_size, bm_normal); } void GxGDEW042Z15::drawPicture(const uint8_t* black_bitmap, const uint8_t* red_bitmap, uint32_t black_size, uint32_t red_size, int16_t mode) { if (_current_page != -1) return; if (mode & bm_partial_update) { _using_partial_mode = true; // remember _wakeUp(); // set full screen IO.writeCommandTransaction(0x91); // partial in _setPartialRamArea(0, 0, GxGDEW042Z15_WIDTH - 1, GxGDEW042Z15_HEIGHT - 1); IO.writeCommandTransaction(0x10); // black for (uint32_t i = 0; i < GxGDEW042Z15_BUFFER_SIZE; i++) { uint8_t data = 0xFF; // 0xFF is white if (i < black_size) { #if defined(__AVR) || defined(ESP8266) || defined(ESP32) data = pgm_read_byte(&black_bitmap[i]); #else data = black_bitmap[i]; #endif if (mode & bm_invert) data = ~data; } IO.writeDataTransaction(data); } IO.writeCommandTransaction(0x13); // red for (uint32_t i = 0; i < GxGDEW042Z15_BUFFER_SIZE; i++) { uint8_t data = 0xFF; // 0xFF is white if (i < red_size) { #if defined(__AVR) || defined(ESP8266) || defined(ESP32) data = pgm_read_byte(&red_bitmap[i]); #else data = red_bitmap[i]; #endif if (mode & bm_invert) data = ~data; } IO.writeDataTransaction(data); } IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("drawPicture"); IO.writeCommandTransaction(0x92); // partial out } else { _using_partial_mode = false; _wakeUp(); IO.writeCommandTransaction(0x10); // black for (uint32_t i = 0; i < GxGDEW042Z15_BUFFER_SIZE; i++) { uint8_t data = 0xFF; // white is 0xFF on device if (i < black_size) { #if defined(__AVR) || defined(ESP8266) || defined(ESP32) data = pgm_read_byte(&black_bitmap[i]); #else data = black_bitmap[i]; #endif if (mode & bm_invert) data = ~data; } IO.writeDataTransaction(data); } IO.writeCommandTransaction(0x13); // red for (uint32_t i = 0; i < GxGDEW042Z15_BUFFER_SIZE; i++) { uint8_t data = 0xFF; // white is 0xFF on device if (i < red_size) { #if defined(__AVR) || defined(ESP8266) || defined(ESP32) data = pgm_read_byte(&red_bitmap[i]); #else data = red_bitmap[i]; #endif if (mode & bm_invert_red) data = ~data; } IO.writeDataTransaction(data); } IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("drawPicture"); _sleep(); } } void GxGDEW042Z15::drawBitmap(const uint8_t *bitmap, uint32_t size, int16_t mode) { if (_current_page != -1) return; if (mode & bm_default) mode |= bm_normal; if (mode & bm_partial_update) { _using_partial_mode = true; // remember _wakeUp(); // set full screen IO.writeCommandTransaction(0x91); // partial in _setPartialRamArea(0, 0, GxGDEW042Z15_WIDTH - 1, GxGDEW042Z15_HEIGHT - 1); IO.writeCommandTransaction(0x10); // black for (uint32_t i = 0; i < GxGDEW042Z15_BUFFER_SIZE; i++) { uint8_t data = 0xFF; // white is 0xFF on device if (i < size) { #if defined(__AVR) || defined(ESP8266) || defined(ESP32) data = pgm_read_byte(&bitmap[i]); #else data = bitmap[i]; #endif if (mode & bm_invert) data = ~data; } IO.writeDataTransaction(data); } IO.writeCommandTransaction(0x13); // red for (uint32_t i = 0; i < GxGDEW042Z15_BUFFER_SIZE; i++) { IO.writeDataTransaction(0xFF); // 0xFF is white } IO.writeCommandTransaction(0x92); // partial out IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("drawBitmap"); IO.writeCommandTransaction(0x92); // partial out } else { _using_partial_mode = false; // remember _wakeUp(); IO.writeCommandTransaction(0x10); // black for (uint32_t i = 0; i < GxGDEW042Z15_BUFFER_SIZE; i++) { uint8_t data = 0xFF; // white is 0xFF on device if (i < size) { #if defined(__AVR) || defined(ESP8266) || defined(ESP32) data = pgm_read_byte(&bitmap[i]); #else data = bitmap[i]; #endif if (mode & bm_invert) data = ~data; } IO.writeDataTransaction(data); } IO.writeCommandTransaction(0x13); // red for (uint32_t i = 0; i < GxGDEW042Z15_BUFFER_SIZE; i++) { IO.writeDataTransaction(0xFF); // 0xFF is white } IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("drawBitmap"); _sleep(); } } void GxGDEW042Z15::eraseDisplay(bool using_partial_update) { if (_current_page != -1) return; if (using_partial_update) { _using_partial_mode = true; // remember _wakeUp(); // set full screen IO.writeCommandTransaction(0x91); // partial in _setPartialRamArea(0, 0, GxGDEW042Z15_WIDTH - 1, GxGDEW042Z15_HEIGHT - 1); IO.writeCommandTransaction(0x10); // black for (uint32_t i = 0; i < GxGDEW042Z15_BUFFER_SIZE; i++) { IO.writeDataTransaction(0xFF); // 0xFF is white } IO.writeCommandTransaction(0x13); for (uint32_t i = 0; i < GxGDEW042Z15_BUFFER_SIZE; i++) { IO.writeDataTransaction(0xFF); // 0xFF is white } IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("eraseDisplay"); IO.writeCommandTransaction(0x92); // partial out } else { _using_partial_mode = false; // remember _wakeUp(); IO.writeCommandTransaction(0x10); // black for (uint32_t i = 0; i < GxGDEW042Z15_BUFFER_SIZE; i++) { IO.writeDataTransaction(0xFF); // 0xFF is white } IO.writeCommandTransaction(0x13); // red for (uint32_t i = 0; i < GxGDEW042Z15_BUFFER_SIZE; i++) { IO.writeDataTransaction(0xFF); // 0xFF is white } IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("eraseDisplay"); _sleep(); } } void GxGDEW042Z15::updateWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h, bool using_rotation) { if (_current_page != -1) return; if (using_rotation) _rotate(x, y, w, h); if (!_using_partial_mode) _wakeUp(); _using_partial_mode = true; IO.writeCommandTransaction(0x91); // partial in _writeToWindow(x, y, x, y, w, h); IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("updateWindow"); IO.writeCommandTransaction(0x92); // partial out } void GxGDEW042Z15::_writeToWindow(uint16_t xs, uint16_t ys, uint16_t xd, uint16_t yd, uint16_t w, uint16_t h) { //Serial.printf("_writeToWindow(%d, %d, %d, %d, %d, %d)\n", xs, ys, xd, yd, w, h); // the screen limits are the hard limits if (xs >= GxGDEW042Z15_WIDTH) return; if (ys >= GxGDEW042Z15_HEIGHT) return; if (xd >= GxGDEW042Z15_WIDTH) return; if (yd >= GxGDEW042Z15_HEIGHT) return; uint16_t xde = gx_uint16_min(GxGDEW042Z15_WIDTH, xd + w) - 1; uint16_t yde = gx_uint16_min(GxGDEW042Z15_HEIGHT, yd + h) - 1; // soft limits, must send as many bytes as set by _setPartialRamArea uint16_t yse = ys + yde - yd; // inclusive uint16_t xss_d8 = xs / 8; uint16_t xse_d8 = xss_d8 + _setPartialRamArea(xd, yd, xde, yde); // exclusive IO.writeCommandTransaction(0x10); // black for (int16_t y1 = ys; y1 <= yse; y1++) { for (int16_t x1 = xss_d8; x1 < xse_d8; x1++) { uint16_t idx = y1 * (GxGDEW042Z15_WIDTH / 8) + x1; uint8_t data = (idx < sizeof(_black_buffer)) ? _black_buffer[idx] : 0x00; IO.writeDataTransaction(~data); } } delay(2); //_setPartialRamArea(xd, yd, xde, yde); IO.writeCommandTransaction(0x13); // red for (int16_t y1 = ys; y1 <= yse; y1++) { for (int16_t x1 = xss_d8; x1 < xse_d8; x1++) { uint16_t idx = y1 * (GxGDEW042Z15_WIDTH / 8) + x1; uint8_t data = (idx < sizeof(_red_buffer)) ? _red_buffer[idx] : 0x00; IO.writeDataTransaction(~data); } } #ifdef USE_PARTIAL_UPDATE_WORKAROUND _setPartialRamArea(0, 0, GxGDEW042Z15_WIDTH - 1, GxGDEW042Z15_HEIGHT - 1); #endif } void GxGDEW042Z15::updateToWindow(uint16_t xs, uint16_t ys, uint16_t xd, uint16_t yd, uint16_t w, uint16_t h, bool using_rotation) { if (using_rotation) { switch (getRotation()) { case 1: swap(xs, ys); swap(xd, yd); swap(w, h); xs = GxGDEW042Z15_WIDTH - xs - w; xd = GxGDEW042Z15_WIDTH - xd - w; break; case 2: xs = GxGDEW042Z15_WIDTH - xs - w; ys = GxGDEW042Z15_HEIGHT - ys - h; xd = GxGDEW042Z15_WIDTH - xd - w; yd = GxGDEW042Z15_HEIGHT - yd - h; break; case 3: swap(xs, ys); swap(xd, yd); swap(w, h); ys = GxGDEW042Z15_HEIGHT - ys - h; yd = GxGDEW042Z15_HEIGHT - yd - h; break; } } _wakeUp(); _using_partial_mode = true; IO.writeCommandTransaction(0x91); // partial in _writeToWindow(xs, ys, xd, yd, w, h); IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("updateToWindow"); IO.writeCommandTransaction(0x92); // partial out delay(1000); // don't stress this display } void GxGDEW042Z15::powerDown() { _sleep(); } uint16_t GxGDEW042Z15::_setPartialRamArea(uint16_t x, uint16_t y, uint16_t xe, uint16_t ye) { x &= 0xFFF8; // byte boundary xe |= 0x0007; // byte boundary IO.writeCommandTransaction(0x90); // partial window IO.writeDataTransaction(x / 256); IO.writeDataTransaction(x % 256); IO.writeDataTransaction(xe / 256); IO.writeDataTransaction(xe % 256); IO.writeDataTransaction(y / 256); IO.writeDataTransaction(y % 256); IO.writeDataTransaction(ye / 256); IO.writeDataTransaction(ye % 256); //IO.writeDataTransaction(0x01); // distortion on full right half IO.writeDataTransaction(0x00); // distortion on right half uint16_t xb = x / 8; // first byte (containing first bit) uint16_t xeb = xe / 8; // last byte (containing last bit) return xeb - xb + 1; // number of bytes to transfer } void GxGDEW042Z15::_waitWhileBusy(const char* comment) { unsigned long start = micros(); while (1) { //=0 BUSY if (digitalRead(_busy) == 1) break; delay(1); if (micros() - start > GxGDEW042Z15_BUSY_TIMEOUT) { if (_diag_enabled) Serial.println("Busy Timeout!"); break; } } if (comment) { #if !defined(DISABLE_DIAGNOSTIC_OUTPUT) if (_diag_enabled) { unsigned long elapsed = micros() - start; Serial.print(comment); Serial.print(" : "); Serial.println(elapsed); } #endif } (void) start; } void GxGDEW042Z15::_wakeUp(void) { if (_rst >= 0) { digitalWrite(_rst, 0); delay(10); digitalWrite(_rst, 1); delay(10); } IO.writeCommandTransaction(0x06); //boost IO.writeDataTransaction (0x17); IO.writeDataTransaction (0x17); IO.writeDataTransaction (0x17); IO.writeCommandTransaction(0x04); _waitWhileBusy("Power On"); IO.writeCommandTransaction(0x00); IO.writeDataTransaction(0x0f); // LUT from OTP Pixel with B/W/R. } void GxGDEW042Z15::_sleep(void) { IO.writeCommandTransaction(0x50); // border floating IO.writeDataTransaction(0x17); IO.writeCommandTransaction(0x02); // power off _waitWhileBusy("Power Off"); if (_rst >= 0) { IO.writeCommandTransaction(0x07); // deep sleep IO.writeDataTransaction(0xA5); } } void GxGDEW042Z15::drawPaged(void (*drawCallback)(void)) { if (_current_page != -1) return; _using_partial_mode = false; _wakeUp(); IO.writeCommandTransaction(0x10); // black for (_current_page = 0; _current_page < GxGDEW042Z15_PAGES; _current_page++) { fillScreen(GxEPD_WHITE); drawCallback(); for (int16_t y1 = 0; y1 < GxGDEW042Z15_PAGE_HEIGHT; y1++) { for (int16_t x1 = 0; x1 < GxGDEW042Z15_WIDTH / 8; x1++) { uint16_t idx = y1 * (GxGDEW042Z15_WIDTH / 8) + x1; uint8_t data = (idx < sizeof(_black_buffer)) ? _black_buffer[idx] : 0x00; IO.writeDataTransaction(~data); } } } IO.writeCommandTransaction(0x13); // red for (_current_page = 0; _current_page < GxGDEW042Z15_PAGES; _current_page++) { fillScreen(GxEPD_WHITE); drawCallback(); for (int16_t y1 = 0; y1 < GxGDEW042Z15_PAGE_HEIGHT; y1++) { for (int16_t x1 = 0; x1 < GxGDEW042Z15_WIDTH / 8; x1++) { uint16_t idx = y1 * (GxGDEW042Z15_WIDTH / 8) + x1; uint8_t data = (idx < sizeof(_red_buffer)) ? _red_buffer[idx] : 0x00; IO.writeDataTransaction(~data); } } } _current_page = -1; IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("drawPaged"); _sleep(); } void GxGDEW042Z15::drawPaged(void (*drawCallback)(uint32_t), uint32_t p) { if (_current_page != -1) return; _using_partial_mode = false; _wakeUp(); IO.writeCommandTransaction(0x10); // black for (_current_page = 0; _current_page < GxGDEW042Z15_PAGES; _current_page++) { fillScreen(GxEPD_WHITE); drawCallback(p); for (int16_t y1 = 0; y1 < GxGDEW042Z15_PAGE_HEIGHT; y1++) { for (int16_t x1 = 0; x1 < GxGDEW042Z15_WIDTH / 8; x1++) { uint16_t idx = y1 * (GxGDEW042Z15_WIDTH / 8) + x1; uint8_t data = (idx < sizeof(_black_buffer)) ? _black_buffer[idx] : 0x00; IO.writeDataTransaction(~data); } } } IO.writeCommandTransaction(0x13); // red for (_current_page = 0; _current_page < GxGDEW042Z15_PAGES; _current_page++) { fillScreen(GxEPD_WHITE); drawCallback(p); for (int16_t y1 = 0; y1 < GxGDEW042Z15_PAGE_HEIGHT; y1++) { for (int16_t x1 = 0; x1 < GxGDEW042Z15_WIDTH / 8; x1++) { uint16_t idx = y1 * (GxGDEW042Z15_WIDTH / 8) + x1; uint8_t data = (idx < sizeof(_red_buffer)) ? _red_buffer[idx] : 0x00; IO.writeDataTransaction(~data); } } } _current_page = -1; IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("drawPaged"); _sleep(); } void GxGDEW042Z15::drawPaged(void (*drawCallback)(const void*), const void* p) { if (_current_page != -1) return; _using_partial_mode = false; _wakeUp(); IO.writeCommandTransaction(0x10); // black for (_current_page = 0; _current_page < GxGDEW042Z15_PAGES; _current_page++) { fillScreen(GxEPD_WHITE); drawCallback(p); for (int16_t y1 = 0; y1 < GxGDEW042Z15_PAGE_HEIGHT; y1++) { for (int16_t x1 = 0; x1 < GxGDEW042Z15_WIDTH / 8; x1++) { uint16_t idx = y1 * (GxGDEW042Z15_WIDTH / 8) + x1; uint8_t data = (idx < sizeof(_black_buffer)) ? _black_buffer[idx] : 0x00; IO.writeDataTransaction(~data); } } } IO.writeCommandTransaction(0x13); // red for (_current_page = 0; _current_page < GxGDEW042Z15_PAGES; _current_page++) { fillScreen(GxEPD_WHITE); drawCallback(p); for (int16_t y1 = 0; y1 < GxGDEW042Z15_PAGE_HEIGHT; y1++) { for (int16_t x1 = 0; x1 < GxGDEW042Z15_WIDTH / 8; x1++) { uint16_t idx = y1 * (GxGDEW042Z15_WIDTH / 8) + x1; uint8_t data = (idx < sizeof(_red_buffer)) ? _red_buffer[idx] : 0x00; IO.writeDataTransaction(~data); } } } _current_page = -1; IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("drawPaged"); _sleep(); } void GxGDEW042Z15::drawPaged(void (*drawCallback)(const void*, const void*), const void* p1, const void* p2) { if (_current_page != -1) return; _using_partial_mode = false; _wakeUp(); IO.writeCommandTransaction(0x10); // black for (_current_page = 0; _current_page < GxGDEW042Z15_PAGES; _current_page++) { fillScreen(GxEPD_WHITE); drawCallback(p1, p2); for (int16_t y1 = 0; y1 < GxGDEW042Z15_PAGE_HEIGHT; y1++) { for (int16_t x1 = 0; x1 < GxGDEW042Z15_WIDTH / 8; x1++) { uint16_t idx = y1 * (GxGDEW042Z15_WIDTH / 8) + x1; uint8_t data = (idx < sizeof(_black_buffer)) ? _black_buffer[idx] : 0x00; IO.writeDataTransaction(~data); } } } IO.writeCommandTransaction(0x13); // red for (_current_page = 0; _current_page < GxGDEW042Z15_PAGES; _current_page++) { fillScreen(GxEPD_WHITE); drawCallback(p1, p2); for (int16_t y1 = 0; y1 < GxGDEW042Z15_PAGE_HEIGHT; y1++) { for (int16_t x1 = 0; x1 < GxGDEW042Z15_WIDTH / 8; x1++) { uint16_t idx = y1 * (GxGDEW042Z15_WIDTH / 8) + x1; uint8_t data = (idx < sizeof(_red_buffer)) ? _red_buffer[idx] : 0x00; IO.writeDataTransaction(~data); } } } _current_page = -1; IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("drawPaged"); _sleep(); } void GxGDEW042Z15::_rotate(uint16_t& x, uint16_t& y, uint16_t& w, uint16_t& h) { switch (getRotation()) { case 1: swap(x, y); swap(w, h); x = GxGDEW042Z15_WIDTH - x - w; break; case 2: x = GxGDEW042Z15_WIDTH - x - w; y = GxGDEW042Z15_HEIGHT - y - h; break; case 3: swap(x, y); swap(w, h); y = GxGDEW042Z15_HEIGHT - y - h; break; } } void GxGDEW042Z15::drawPagedToWindow(void (*drawCallback)(void), uint16_t x, uint16_t y, uint16_t w, uint16_t h) { if (_current_page != -1) return; _rotate(x, y, w, h); if (!_using_partial_mode) { eraseDisplay(false); eraseDisplay(true); } _using_partial_mode = true; IO.writeCommandTransaction(0x91); // partial in for (_current_page = 0; _current_page < GxGDEW042Z15_PAGES; _current_page++) { uint16_t yds = gx_uint16_max(y, _current_page * GxGDEW042Z15_PAGE_HEIGHT); uint16_t yde = gx_uint16_min(y + h, (_current_page + 1) * GxGDEW042Z15_PAGE_HEIGHT); if (yde > yds) { fillScreen(GxEPD_WHITE); drawCallback(); uint16_t ys = yds % GxGDEW042Z15_PAGE_HEIGHT; _writeToWindow(x, ys, x, yds, w, yde - yds); } } IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("updateToWindow"); IO.writeCommandTransaction(0x92); // partial out _current_page = -1; } void GxGDEW042Z15::drawPagedToWindow(void (*drawCallback)(uint32_t), uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint32_t p) { if (_current_page != -1) return; _rotate(x, y, w, h); if (!_using_partial_mode) { eraseDisplay(false); eraseDisplay(true); } _using_partial_mode = true; IO.writeCommandTransaction(0x91); // partial in for (_current_page = 0; _current_page < GxGDEW042Z15_PAGES; _current_page++) { uint16_t yds = gx_uint16_max(y, _current_page * GxGDEW042Z15_PAGE_HEIGHT); uint16_t yde = gx_uint16_min(y + h, (_current_page + 1) * GxGDEW042Z15_PAGE_HEIGHT); if (yde > yds) { fillScreen(GxEPD_WHITE); drawCallback(p); uint16_t ys = yds % GxGDEW042Z15_PAGE_HEIGHT; _writeToWindow(x, ys, x, yds, w, yde - yds); } } IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("updateToWindow"); IO.writeCommandTransaction(0x92); // partial out _current_page = -1; } void GxGDEW042Z15::drawPagedToWindow(void (*drawCallback)(const void*), uint16_t x, uint16_t y, uint16_t w, uint16_t h, const void* p) { if (_current_page != -1) return; _rotate(x, y, w, h); if (!_using_partial_mode) { eraseDisplay(false); eraseDisplay(true); } _using_partial_mode = true; IO.writeCommandTransaction(0x91); // partial in for (_current_page = 0; _current_page < GxGDEW042Z15_PAGES; _current_page++) { uint16_t yds = gx_uint16_max(y, _current_page * GxGDEW042Z15_PAGE_HEIGHT); uint16_t yde = gx_uint16_min(y + h, (_current_page + 1) * GxGDEW042Z15_PAGE_HEIGHT); if (yde > yds) { fillScreen(GxEPD_WHITE); drawCallback(p); uint16_t ys = yds % GxGDEW042Z15_PAGE_HEIGHT; _writeToWindow(x, ys, x, yds, w, yde - yds); } } IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("updateToWindow"); IO.writeCommandTransaction(0x92); // partial out _current_page = -1; } void GxGDEW042Z15::drawPagedToWindow(void (*drawCallback)(const void*, const void*), uint16_t x, uint16_t y, uint16_t w, uint16_t h, const void* p1, const void* p2) { if (_current_page != -1) return; _rotate(x, y, w, h); if (!_using_partial_mode) { eraseDisplay(false); eraseDisplay(true); } _using_partial_mode = true; IO.writeCommandTransaction(0x91); // partial in for (_current_page = 0; _current_page < GxGDEW042Z15_PAGES; _current_page++) { uint16_t yds = gx_uint16_max(y, _current_page * GxGDEW042Z15_PAGE_HEIGHT); uint16_t yde = gx_uint16_min(y + h, (_current_page + 1) * GxGDEW042Z15_PAGE_HEIGHT); if (yde > yds) { fillScreen(GxEPD_WHITE); drawCallback(p1, p2); uint16_t ys = yds % GxGDEW042Z15_PAGE_HEIGHT; _writeToWindow(x, ys, x, yds, w, yde - yds); } } IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("updateToWindow"); IO.writeCommandTransaction(0x92); // partial out _current_page = -1; } void GxGDEW042Z15::drawCornerTest(uint8_t em) { if (_current_page != -1) return; _using_partial_mode = false; _wakeUp(); IO.writeCommandTransaction(0x10); // black for (uint32_t y = 0; y < GxGDEW042Z15_HEIGHT; y++) { for (uint32_t x = 0; x < GxGDEW042Z15_WIDTH / 8; x++) { uint8_t data = 0xFF; if ((x < 1) && (y < 8)) data = 0x00; if ((x > GxGDEW042Z15_WIDTH / 8 - 3) && (y < 16)) data = 0x00; if ((x > GxGDEW042Z15_WIDTH / 8 - 4) && (y > GxGDEW042Z15_HEIGHT - 25)) data = 0x00; if ((x < 4) && (y > GxGDEW042Z15_HEIGHT - 33)) data = 0x00; IO.writeDataTransaction(data); } } IO.writeCommandTransaction(0x13); // red for (uint32_t i = 0; i < GxGDEW042Z15_BUFFER_SIZE; i++) { IO.writeDataTransaction(0xFF); // 0xFF is white } IO.writeCommandTransaction(0x12); //display refresh _waitWhileBusy("drawCornerTest"); _sleep(); }