From 4eb4ac213b808c11fc6a7225a53d4f0a87f8b605 Mon Sep 17 00:00:00 2001 From: David Zovko Date: Thu, 16 Jul 2020 08:40:33 +0200 Subject: [PATCH] Update screen refresh sequence Updating core screen refresh sequence to make it compatible with all batches of ED060SC7 screens. --- Inkplate.cpp | 1295 ++++++++++++++++++++++++-------------------------- Inkplate.h | 326 +++++++------ 2 files changed, 804 insertions(+), 817 deletions(-) diff --git a/Inkplate.cpp b/Inkplate.cpp index 16ffe38..5bb7d15 100644 --- a/Inkplate.cpp +++ b/Inkplate.cpp @@ -3,15 +3,32 @@ #include "Adafruit_GFX.h" #include "Inkplate.h" Adafruit_MCP23017 mcp; +SPIClass spi2(HSPI); +SdFat sd(&spi2); +//--------------------------STATIC FUNCTIONS-------------------------------------------- +//For precise 1uS timing, we cannot use delayMicroseconds(), instead we use ASM with nop command. Initial Z value will be difeerent on different CPU speeds! (for 240 MHz CPU Clock z = 25) +void usleep1() { + int z = 25; + while (z--) { + asm("NOP"); + }; +} + +void ckvClock() { + CKV_CLEAR; + usleep1(); + CKV_SET; + usleep1(); +} + +//--------------------------USER FUNCTIONS-------------------------------------------- Inkplate::Inkplate(uint8_t _mode) : Adafruit_GFX(E_INK_WIDTH, E_INK_HEIGHT) { _displayMode = _mode; } -Inkplate::Inkplate() : Adafruit_GFX(E_INK_WIDTH, E_INK_HEIGHT) { -} - void Inkplate::begin(void) { + if(_beginDone == 1) return; Wire.begin(); mcp.begin(0); mcp.pinMode(VCOM, OUTPUT); @@ -28,6 +45,7 @@ void Inkplate::begin(void) { mcp.pinMode(OE, OUTPUT); mcp.pinMode(GMOD, OUTPUT); mcp.pinMode(SPV, OUTPUT); + //pinMode(SPV, OUTPUT); //DATA PINS pinMode(4, OUTPUT); //D0 @@ -47,202 +65,23 @@ void Inkplate::begin(void) { //Battery voltage Switch MOSFET mcp.pinMode(9, OUTPUT); - //1 bit per pixel mode (monochrome mode) - if (_displayMode == 0) { - D_memory_new = (uint8_t*)ps_malloc(600 * 100); - _partial = (uint8_t*)ps_malloc(600*100); - _pBuffer = (uint8_t*) ps_malloc(120000); - if (D_memory_new == NULL || _partial == NULL || _pBuffer == NULL) { - do { - delay(100); - } while (true); - } - memset(D_memory_new, 0, 60000); - memset(_partial, 0, 60000); + D_memory_new = (uint8_t*)ps_malloc(600 * 100); + _partial = (uint8_t*)ps_malloc(600*100); + _pBuffer = (uint8_t*) ps_malloc(120000); + D_memory4Bit = (uint8_t*)ps_malloc(240000); + if (D_memory_new == NULL || _partial == NULL || _pBuffer == NULL || D_memory4Bit == NULL) { + do { + delay(100); + } while (true); } - - //3 bit per pixel mode (8 level grayscale mode) - if (_displayMode == 1) { - D_memory4Bit = (uint8_t*)ps_malloc(240000); - if (D_memory4Bit == NULL ) { - do { - delay(100); - } while (true); - } - memset(D_memory4Bit, 255, 240000); - } -} - -void Inkplate::clearDisplay() { - //Clear 1 bit per pixel display buffer - if (_displayMode == 0) memset(_partial, 0, 60000); - - //Clear 3 bit per pixel display buffer - if (_displayMode == 1) memset(D_memory4Bit, 255, 240000); -} - -void Inkplate::draw_mode_on() { - einkOn(); - SPH_SET; - SPV_SET; - delay(1); - OE_SET; - delay(1); - GMOD_SET; - CKV_SET; - delay(5); -} - -void Inkplate::draw_mode_off() { - CKV_CLOCK; - GMOD_CLEAR; - CKV_CLOCK; - GPIO.out &= ~DATA; - CL_CLEAR; - LE_CLEAR; - OE_CLEAR; - SPV_CLEAR; - CKV_CLEAR; - SPH_CLEAR; - einkOff(); -} - -void Inkplate::advance_line() { - CKV_CLEAR; - usleep1(); - CKV_SET; - usleep1(); -} - -void Inkplate::begin_frame() { - - SPV_SET; - delayMicroseconds(100); //usleep(500); - SPV_CLEAR; - //usleep1(); - CKV_CLEAR; - usleep1(); - //delayMicroseconds(20); //usleep(25); - CKV_SET - usleep1(); - SPV_SET; - usleep1(); - //delayMicroseconds(20); //usleep(25); + memset(D_memory_new, 0, 60000); + memset(_partial, 0, 60000); + memset(_pBuffer, 0, 120000); + memset(D_memory4Bit, 255, 240000); - - //SPV_SET; //STV=1 - //int loop = 2; - //while(loop--) - //{ - // CKV_CLEAR; //CPV=0 - // usleep1(); - // CKV_SET; //CPV=1 - // usleep1(); - //} - //SPV_CLEAR; //STV=0 - //loop = 2; - //while(loop--) - //{ - // CKV_CLEAR; //CPV=0 - // usleep1(); - // CKV_SET; //CPV=1 - // usleep1(); - //} - //SPV_SET; //STV=1 - //loop = 2; - //while(loop--) - //{ - // CKV_CLEAR; //CPV=0 - // usleep1(); - // CKV_SET; //CPV=1 - // usleep1(); - //} - - //Skip three lines to get to the start of the screen - advance_line(); - advance_line(); - advance_line(); -} - -void Inkplate::end_frame() { - -} - -void Inkplate::begin_line() { - SPH_CLEAR; - usleep1(); -// LE_SET; //LE =1 -//CL_CLEAR; //CL=0 -//CL_SET; //CL=1 -//CL_CLEAR; //CL=0 -//CL_SET; //CL=1 -//LE_CLEAR; //LE=0 -//CL_CLEAR; //CL=0 -//CL_SET; //CL=1 -//CL_CLEAR; //CL=0 -//CL_SET; //CL=1 -//EPD_OE_H; //OE =1 -//CL_CLEAR; //CL=0 -//CL_SET; //CL=1 -//CL_CLEAR; //CL=0 -//CL_SET; //CL=1 -//SPH_CLEAR; //SPH=0 -} - -void Inkplate::end_line() { - - SPH_SET; - //usleep1(); - - CKV_CLEAR; - //CL_SET; - usleep1(); - CL_CLEAR; - //usleep1(); - CKV_SET; - usleep1(); - LE_SET; - //usleep1(); - LE_CLEAR; - //usleep1(); - - -// SPH_SET; //STH=1 -//CL_CLEAR; //CL=0 -//CL_SET; //CL=1 -//CL_CLEAR; //CL=0 -//CL_SET; //CL=1 -//CKV_CLEAR; //CPV=0 – zegar taktujacy bufor GATE -//EPD_OE_L; //OE =0 -//CL_CLEAR; //CL=0 -//CL_SET; //CL=1 -//CL_CLEAR; //CL=0 -//CL_SET; //CL=1 -//CKV_SET; //CPV=1 -} - -void Inkplate::end_line_slow() { - SPH_SET; - //usleep1(); - CKV_CLEAR; - CL_SET; - delayMicroseconds(20); - //usleep1(); - CL_CLEAR; - CKV_SET; - delayMicroseconds(4); - LE_SET; - //usleep1(); - LE_CLEAR; - //usleep1(); -} - -//For precise 1uS timing, we cannot use delayMicroseconds(), instead we use ASM with nop command. Initial Z value will be difeerent on different CPU speeds! (for 240 MHz CPU Clock z = 25) -void usleep1() { - int z = 27; - while (z--) { - asm("NOP"); - }; + //precalculateGamma(gammaLUT, INKPLATE_GAMMA); + precalculateGamma(gammaLUT, 1); + _beginDone = 1; } //Draw function, used by Adafruit GFX. @@ -267,8 +106,7 @@ void Inkplate::drawPixel(int16_t x0, int16_t y0, uint16_t color) { if (_displayMode == 0) { int x = x0 / 8; int x_sub = x0 % 8; - uint8_t temp = *(_partial + 100 * y0 + x); //D_memory_new[99 * y0 + x]; //-->> Doesn't work with index - //D_memory_new[100 * y0 + x] = ~pixelMaskLUT[x_sub] & temp | (color ? pixelMaskLUT[x_sub] : 0); //-->> Doesn't work with index + uint8_t temp = *(_partial + 100 * y0 + x); //D_memory_new[99 * y0 + x]; *(_partial + 100 * y0 + x) = ~pixelMaskLUT[x_sub] & temp | (color ? pixelMaskLUT[x_sub] : 0); } else { color &= 7; @@ -280,7 +118,13 @@ void Inkplate::drawPixel(int16_t x0, int16_t y0, uint16_t color) { } } +void Inkplate::clearDisplay() { + //Clear 1 bit per pixel display buffer + if (_displayMode == 0) memset(_partial, 0, 60000); + //Clear 3 bit per pixel display buffer + if (_displayMode == 1) memset(D_memory4Bit, 255, 240000); +} //Function that displays content from RAM to screen void Inkplate::display() { @@ -288,95 +132,15 @@ void Inkplate::display() { if (_displayMode == 1) display3b(); } -//Display content from RAM to display (1 bit per pixel,. monochrome picture). -void Inkplate::display1b() { - for(int i = 0; i<60000; i++) { - *(D_memory_new+i) &= *(_partial+i); - *(D_memory_new+i) |= (*(_partial+i)); - } - uint16_t _pos; - uint32_t _send; - uint8_t data; - draw_mode_on(); - SPV_SET; - delayMicroseconds(500); - - //cleanFast(2); - cleanFast(2); - for(int i = 0; i<8; i++) { - cleanFast(0); - delayMicroseconds(500); - } - for(int i = 0; i<8; i++) { - cleanFast(1); - delayMicroseconds(500); - } - for(int i = 0; i<8; i++) { - cleanFast(0); - delayMicroseconds(500); - } - //for(int i = 0; i<2; i++) { - //cleanFast(2); - //} - //for (int i = 0; i < 6; i++) { - // cleanFast(1); - // delayMicroseconds(500); - //} - - //for (int i = 0; i < 6; i++) { - // cleanFast(0); - // delayMicroseconds(500); - //} - - //for (int i = 0; i < 1; i++) { - // cleanFast(1); - //delayMicroseconds(500); - //} - - for (int k = 0; k < 6; k++) { - begin_frame(); - _pos = 59999; - for (int i = 0; i < 600; i++) { - //data = 10101010; - //_send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); - GPIO.out_w1tc = DATA; - //GPIO.out_w1ts = _send; - //CL_SET; - CL_CLEAR; - begin_line(); - for (int j = 0; j < 100; j++) { - data = LUTB[(*(D_memory_new + _pos) >> 4)]; - _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); - GPIO.out_w1tc = DATA | CL; - GPIO.out_w1ts = (_send) | CL; - data = LUTB[*(D_memory_new + _pos) & 0x0F]; - _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); - GPIO.out_w1tc = DATA | CL; - GPIO.out_w1ts = (_send) | CL; - _pos--; - } - end_line(); - } - end_frame(); - delayMicroseconds(500); - } - cleanFast(2); - cleanFast(2); - delayMicroseconds(500); - draw_mode_off(); -} - void Inkplate::partialUpdate() { if(_displayMode == 1) return; + if(_blockPartial == 1) display1b(); uint16_t _pos = 59999; uint32_t _send; uint8_t data; uint8_t diffw, diffb; uint32_t n = 119999; - - draw_mode_on(); - SPV_SET; - delayMicroseconds(500); + uint8_t dram; for (int i = 0; i < 600; i++) { for (int j = 0; j < 100; j++) { @@ -390,174 +154,68 @@ void Inkplate::partialUpdate() { } } - for (int k = 0; k < 7; k++) { - begin_frame(); - n = 119999; + einkOn(); + for (int k = 0; k < 5; k++) { + vscan_start(); + n = 119999; for (int i = 0; i < 600; i++) { - //data = 10101010; - //_send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); - GPIO.out_w1tc = DATA; - //GPIO.out_w1ts = _send; - //CL_SET; - CL_CLEAR; - begin_line(); - for (int j = 0; j < 200; j++) { - //_pos = i * 200 + j; - ////diffw = ((*(D_memory_new+_pos))^(*(_partial+_pos)))&(~(*(_partial+_pos))); - ////diffb = ((*(D_memory_new+_pos))^(*(_partial+_pos)))&((*(_partial+_pos))); - ////data = LUTW[diffw>>4] & (LUTB[diffb>>4]); - //data = LUT2[(*(D_memory_new + _pos) >> 4)]; data = *(_pBuffer + n); _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); - GPIO.out_w1tc = DATA | CL; + hscan_start(_send); + n--; + for (int j = 0; j < 199; j++) { + data = *(_pBuffer + n); + _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); GPIO.out_w1ts = (_send) | CL; - //data = LUT2[*(D_memory_new + _pos) & 0x0F]; - ////data = LUTW[diffw&0x0F] & (LUTB[diffb&0x0F]); - ////_send = ((_pBuffer & B00000011) << 4) | (((_pBuffer & B00001100) >> 2) << 18) | (((_pBuffer & B00010000) >> 4) << 23) | (((_pBuffer & B11100000) >> 5) << 25); - ////GPIO.out_w1tc = DATA | CL; - ////GPIO.out_w1ts = _send; - ////GPIO.out_w1ts = CL; + GPIO.out_w1tc = DATA | CL; n--; } - end_line(); + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + vscan_end(); } - end_frame(); - delayMicroseconds(500); + delayMicroseconds(230); } + /* + for (int k = 0; k < 1; k++) { + vscan_start(); + _pos = 59999; + for (int i = 0; i < 600; i++) { + data = discharge[(*(D_memory_new + _pos) >> 4)]; + _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); + hscan_start(_send); + data = discharge[*(D_memory_new + _pos) & 0x0F]; + _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + _pos--; + for (int j = 0; j < 99; j++) { + data = discharge[(*(D_memory_new + _pos) >> 4)]; + _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + data = discharge[*(D_memory_new + _pos) & 0x0F]; + _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + _pos--; + } + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + vscan_end(); + } + delayMicroseconds(230); + } + */ + cleanFast(2, 2); + cleanFast(3, 1); + vscan_start(); + einkOff(); + einkOff(); for(int i = 0; i<60000; i++) { *(D_memory_new+i) &= *(_partial+i); *(D_memory_new+i) |= (*(_partial+i)); } - - cleanFast(2); - delayMicroseconds(500); - draw_mode_off(); -} - -//Display content from RAM to display (3 bit per pixel,. 8 level of grayscale, STILL IN PROGRESSS, we need correct wavefrom to get good picture, use it only for pictures not for GFX). -void Inkplate::display3b() { - draw_mode_on(); - SPV_SET; - delayMicroseconds(500); - cleanFast(2); - //cleanFast(2); - //for(int i = 0; i<5; i++) { - // cleanFast(0); - //} - for(int i = 0; i<5; i++) { - cleanFast(0); - //delay(1); - } - delay(5); - for(int i = 0; i<5; i++) { - cleanFast(0); - //delay(1); - } - delay(5); - for(int i = 0; i<7; i++) { - cleanFast(1); - //delay(1); - } - delay(5); - for(int i = 0; i<7; i++) { - cleanFast(0); - //delay(1); - } - delay(5); - //cleanFast(1); - cleanFast(2); - //cleanFast(0); - //for(int i = 0; i<3; i++) { - // cleanFast(2); - //} - - for (int k = 0; k < 12; k++) { - //for (int k = 0; k < sz_contrast_cycles; ++k) { - //for (int contrast_cnt = 0; contrast_cnt < contrast_cycles[k]; ++contrast_cnt) { - begin_frame(); - uint8_t *dp = D_memory4Bit + 239999; - uint32_t _send; - uint8_t pix1; - uint8_t pix2; - uint8_t pix3; - uint8_t pix4; - - for (int i = 0; i < 600; i++) { - //CL_SET; - //GPIO.out_w1tc = DATA; - //CL_CLEAR; - begin_line(); - //portDISABLE_INTERRUPTS(); - for (int j = 0; j < 100; j++) { - - uint8_t pixel = 0B00000000; - uint8_t pixel2 = 0B00000000; - - //4 bit mode (non-reversed bits) - //pix1 = (*(dp) >> k) & 1; //4, 5, 6, 7 - //pix2 = (*(dp--) >> k + 4) & 1; //0, 1, 2, 3 - //pix3 = (*(dp) >> k) & 1; - //pix4 = (*(dp--) >> k + 4) & 1; - - //pix1 = (*(dp)) & 0x07; //4, 5, 6, 7 - //pix2 = (*(dp--) >> 4)& 0x07; //0, 1, 2, 3 - //pix3 = (*(dp)) & 0x07; - //pix4 = (*(dp--) >> 4)& 0x07; - pix1 = *(dp--); - pix2 = *(dp--); - pix3 = *(dp--); - pix4 = *(dp--); - //pixel |= ( pixel_to_epd_cmd[pix1] << 6) | ( pixel_to_epd_cmd[pix2] << 4) | ( pixel_to_epd_cmd[pix3] << 2) | ( pixel_to_epd_cmd[pix4] << 0); - //pixel |= ( waveform3Bit[pix1][k] << 6) | ( waveform3Bit[pix2][k] << 4) | ( waveform3Bit[pix3][k] << 2) | ( waveform3Bit[pix4][k] << 0); - pixel |= ( waveform3Bit[pix1&0x07][k] << 6) | ( waveform3Bit[(pix1>>4)&0x07][k] << 4) | ( waveform3Bit[pix2&0x07][k] << 2) | ( waveform3Bit[(pix2>>4)&0x07][k] << 0); - pixel2 |= ( waveform3Bit[pix3&0x07][k] << 6) | ( waveform3Bit[(pix3>>4)&0x07][k] << 4) | ( waveform3Bit[pix4&0x07][k] << 2) | ( waveform3Bit[(pix4>>4)&0x07][k] << 0); - - _send = ((pixel & B00000011) << 4) | (((pixel & B00001100) >> 2) << 18) | (((pixel & B00010000) >> 4) << 23) | (((pixel & B11100000) >> 5) << 25); - GPIO.out_w1tc = DATA | CL; - GPIO.out_w1ts = (_send) | CL; - //GPIO.out_w1ts = CL; - - //4 bit mode (non-reversed bits) - //pix1 = (*(dp) >> k) & 1; //4, 5, 6, 7 - //pix2 = (*(dp--) >> k + 4) & 1; //0, 1, 2, 3 - //pix3 = (*(dp) >> k) & 1; - //pix4 = (*(dp--) >> k + 4) & 1; - - //pixel2 |= ( pixel_to_epd_cmd[pix1] << 6); - //pixel2 |= ( pixel_to_epd_cmd[pix2] << 4); - //pixel2 |= ( pixel_to_epd_cmd[pix3] << 2); - //pixel2 |= ( pixel_to_epd_cmd[pix4] << 0); - - //pix1 = (*(dp)) & 0x07; //4, 5, 6, 7 - //pix2 = (*(dp--) >> 4)& 0x07; //0, 1, 2, 3 - //pix3 = (*(dp)) & 0x07; - //pix4 = (*(dp--) >> 4)& 0x07; - - //pixel |= ( pixel_to_epd_cmd[pix1] << 6) | ( pixel_to_epd_cmd[pix2] << 4) | ( pixel_to_epd_cmd[pix3] << 2) | ( pixel_to_epd_cmd[pix4] << 0); - //pixel2 |= ( waveform3Bit[pix1][k] << 6) | ( waveform3Bit[pix2][k] << 4) | ( waveform3Bit[pix3][k] << 2) | ( waveform3Bit[pix4][k] << 0); - - _send = ((pixel2 & B00000011) << 4) | (((pixel2 & B00001100) >> 2) << 18) | (((pixel2 & B00010000) >> 4) << 23) | (((pixel2 & B11100000) >> 5) << 25); - GPIO.out_w1tc = DATA | CL; - GPIO.out_w1ts = (_send) | CL; - //GPIO.out_w1ts = CL; - } - //portENABLE_INTERRUPTS(); - end_line(); - } - end_frame(); - //} - } - - cleanFast(2); - delayMicroseconds(500); - draw_mode_off(); -} - -void ckvClock() { - CKV_CLEAR; - usleep1(); - CKV_SET; - usleep1(); } void Inkplate::drawBitmap3Bit(int16_t _x, int16_t _y, const unsigned char* _p, int16_t _w, int16_t _h) { @@ -566,12 +224,6 @@ void Inkplate::drawBitmap3Bit(int16_t _x, int16_t _y, const unsigned char* _p, i int i, j; int xSize = _w / 2 + _rem; - //if (_shiftX == 0) { - // for (int i = _y; i < _y + _h; i++) { - // memcpy(D_memory4Bit + (400 * i) + _x/2, _p + _w/2 * (i-_y), _w / 2); - // } - //} - for (i = 0; i < _h; i++) { for (j = 0; j < xSize - 1; j++) { drawPixel((j * 2) + _x, i + _y, (*(_p + xSize * (i) + j) >> 4)>>1); @@ -582,176 +234,6 @@ void Inkplate::drawBitmap3Bit(int16_t _x, int16_t _y, const unsigned char* _p, i } } -//Clears contenst from display (slower, some epaper panels needs slower cleaning process from others). -void Inkplate::fillScreen(uint8_t c) { - uint8_t data = c == 0 ? B10101010 : B01010101; - uint32_t _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); - draw_mode_on(); - SPV_SET; - delayMicroseconds(500); //usleep(500); - for (int k = 0; k < REF_RATE; k++) { - begin_frame(); - for (int i = 0; i < 600; i++) { - - begin_line(); - for (int j = 0; j < 100; j++) { - GPIO.out &= ~DATA; - GPIO.out |= _send; - CL_SET; - CL_CLEAR; - GPIO.out &= ~DATA; - GPIO.out |= _send; - CL_SET; - CL_CLEAR; - } - end_line(); - } - end_frame(); - } - draw_mode_off(); -} - -//Clear screan before new screen update using waveform -void Inkplate::clean() { - draw_mode_on(); - SPV_SET; //dspv_gpio::set::set(1 << DSPV_BIT); - delayMicroseconds(500); //usleep(500); - int m = 0; - cleanFast(0); //white - m++; - for (int i = 0; i < 8; i++) { - cleanFast((waveform[m] >> 30) & 3); //White to white - m++; - delay(1); - } - //cleanFast(0); - cleanFast((waveform[m] >> 24) & 3); //white to black - m++; - for (int i = 0; i < 8; i++) { - cleanFast((waveform[m]) & 3); //Black to black - m++; - delay(1); - } - cleanFast((waveform[m] >> 6) & 3); //Black to white - m++; - for (int i = 0; i < 8; i++) { - cleanFast((waveform[m] >> 30) & 3); //White to white - m++; - delay(1); - } - //cleanFast(2); - //delay(1); - draw_mode_off(); -} - -//Clears content from epaper diplay as fast as ESP32 can. -void Inkplate::cleanFast(uint8_t c) { - uint8_t data; - if (c == 0) { - data = B10101010; //White - } else if (c == 1) { - data = B01010101; //Black - } else if (c == 2) { - data = B00000000; //Discharge - } - uint32_t _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); - begin_frame(); - for (int i = 0; i < 600; i++) { - - begin_line(); - for (int j = 0; j < 100; j++) { - GPIO.out_w1tc = DATA | CL; - GPIO.out_w1ts = (_send) | CL; - //GPIO.out_w1ts = CL; - GPIO.out_w1tc = DATA | CL; - GPIO.out_w1ts = (_send) | CL; - //GPIO.out_w1ts = CL; - } - end_line(); - } - end_frame(); -} - -//Turn on supply for epaper display (TPS65186) [+15 VDC, -15VDC, +22VDC, -20VDC, +3.3VDC, VCOM] -void Inkplate::einkOn() { - _panelOn = 1; - pinsAsOutputs(); - WAKEUP_SET; - PWRUP_SET; - //Enable all rails - Wire.beginTransmission(0x48); - Wire.write(0x01); - Wire.write(B00111111); - Wire.endTransmission(); - //Set out voltage on LDO outputs - Wire.beginTransmission(0x48); - Wire.write(0x02); - Wire.write(B00100011); - Wire.endTransmission(); - //Set VCOM Voltage - Wire.beginTransmission(0x48); - Wire.write(0x03); - Wire.write(150); - Wire.endTransmission(); - //Set power up times (all on 3mS) - Wire.beginTransmission(0x48); - Wire.write(0x0A); - Wire.write(0); - Wire.endTransmission(); - //Set Power Down Seq. - Wire.beginTransmission(0x48); - Wire.write(0x0B); - Wire.write(B00011011); - Wire.endTransmission(); - //Set Power Down Times (all on 6mS) - Wire.beginTransmission(0x48); - Wire.write(0x0C); - Wire.write(0); - Wire.endTransmission(); - - delay(20); - - VCOM_SET; - - Wire.beginTransmission(0x48); - Wire.write(0x0D); - Wire.write(B10000000); - Wire.endTransmission(); - - delay(2); - - Wire.beginTransmission(0x48); - Wire.write(0x00); - Wire.endTransmission(); - - Wire.requestFrom(0x48, 1); - _temperature = Wire.read(); - - -} - -//Turn off epapewr supply and put all digital IO pins in high Z state -void Inkplate::einkOff() { - _panelOn = 0; - GPIO.out &= ~(DATA | CL | LE); - SPH_CLEAR; - OE_CLEAR; - GMOD_CLEAR; - SPV_CLEAR; - - PWRUP_CLEAR; - //Enable all rails - Wire.beginTransmission(0x48); - Wire.write(0x01); - Wire.write(B00000000); - Wire.endTransmission(); - //delay(250); - WAKEUP_CLEAR; - VCOM_CLEAR; - - pinsZstate(); -} - void Inkplate::setRotation(uint8_t r) { _rotation = r % 4; switch (rotation) { @@ -774,10 +256,286 @@ void Inkplate::setRotation(uint8_t r) { } } +//Turn off epapewr supply and put all digital IO pins in high Z state +void Inkplate::einkOff() { + if(_panelOn == 0) return; + _panelOn = 0; + GMOD_CLEAR; + OE_CLEAR; + GPIO.out &= ~(DATA | CL | LE); + SPH_CLEAR; + SPV_CLEAR; + + PWRUP_CLEAR; + WAKEUP_CLEAR; + VCOM_CLEAR; + + pinsZstate(); +} + +//Turn on supply for epaper display (TPS65186) [+15 VDC, -15VDC, +22VDC, -20VDC, +3.3VDC, VCOM] +void Inkplate::einkOn() { + if(_panelOn == 1) return; + _panelOn = 1; + pinsAsOutputs(); + WAKEUP_SET; + PWRUP_SET; + VCOM_SET; + //Enable all rails + Wire.beginTransmission(0x48); + Wire.write(0x01); + Wire.write(B00111111); + Wire.endTransmission(); + + delay(40); + + Wire.beginTransmission(0x48); + Wire.write(0x0D); + Wire.write(B10000000); + Wire.endTransmission(); + + delay(2); + + Wire.beginTransmission(0x48); + Wire.write(0x00); + Wire.endTransmission(); + + Wire.requestFrom(0x48, 1); + _temperature = Wire.read(); + + LE_CLEAR; //setpin_le(FALSE); + OE_CLEAR; //setpin_oe(FALSE); + CL_CLEAR; //setpin_cl(FALSE); + SPH_SET; //setpin_sph(FALSE); + GMOD_SET; //setpin_gmode(FALSE); + SPV_SET; //setpin_spv(FALSE); + CKV_CLEAR; //setpin_ckv(FALSE); + OE_SET; +} + +void Inkplate::selectDisplayMode(uint8_t _mode) { + if(_mode != _displayMode) { + _displayMode = _mode&1; + memset(D_memory_new, 0, 60000); + memset(_partial, 0, 60000); + memset(_pBuffer, 0, 120000); + memset(D_memory4Bit, 255, 240000); + _blockPartial = 1; + } +} + +uint8_t Inkplate::getDisplayMode() { + return _displayMode; +} + +int Inkplate::drawBitmapFromSD(SdFile* p, int x, int y) { + if(sdCardOk == 0) return 0; + struct bitmapHeader bmpHeader; + readBmpHeader(p, &bmpHeader); + if (bmpHeader.signature != 0x4D42 || bmpHeader.compression != 0 || !(bmpHeader.color == 1 || bmpHeader.color == 24)) return 0; + + if ((bmpHeader.color == 24 || bmpHeader.color == 32) && getDisplayMode() != INKPLATE_3BIT) { + selectDisplayMode(INKPLATE_3BIT); + } + + if (bmpHeader.color == 1 && getDisplayMode() != INKPLATE_1BIT) { + selectDisplayMode(INKPLATE_1BIT); + } + + if (bmpHeader.color == 1) drawMonochromeBitmap(p, bmpHeader, x, y); + if (bmpHeader.color == 24) drawGrayscaleBitmap24(p, bmpHeader, x, y); + + return 1; +} + +int Inkplate::drawBitmapFromSD(char* fileName, int x, int y) { + if(sdCardOk == 0) return 0; + SdFile dat; + if (dat.open(fileName, O_RDONLY)) { + return drawBitmapFromSD(&dat, x, y); + } else { + return 0; + } +} + +int Inkplate::sdCardInit() { + spi2.begin(14, 12, 13, 15); + sdCardOk = sd.begin(15, SD_SCK_MHZ(25)); + return sdCardOk; +} + +SdFat Inkplate::getSdFat() { + return sd; +} + +SPIClass Inkplate::getSPI() { + return spi2; +} + uint8_t Inkplate::getPanelState() { return _panelOn; } +uint8_t Inkplate::readTouchpad(uint8_t _pad) { + return mcp.digitalRead((_pad&3)+10); +} + +int8_t Inkplate::readTemperature() { + return _temperature; +} + +double Inkplate::readBattery() { + mcp.digitalWrite(9, HIGH); + delay(1); + int adc = analogRead(35); + mcp.digitalWrite(9, LOW); + return (double(adc) / 4095 * 3.3 * 2); +} + +//--------------------------LOW LEVEL STUFF-------------------------------------------- +void Inkplate::vscan_start() +{ + CKV_SET; + delayMicroseconds(7); + SPV_CLEAR; + delayMicroseconds(10); + CKV_CLEAR; + delayMicroseconds(0); //usleep1(); + CKV_SET; + delayMicroseconds(8); + SPV_SET; + delayMicroseconds(10); + CKV_CLEAR; + delayMicroseconds(0); //usleep1(); + CKV_SET; + delayMicroseconds(18); + CKV_CLEAR; + delayMicroseconds(0); //usleep1(); + CKV_SET; + delayMicroseconds(18); + CKV_CLEAR; + delayMicroseconds(0); //usleep1(); + CKV_SET; + //delayMicroseconds(18); +} + +void Inkplate::vscan_write() +{ + CKV_CLEAR; + LE_SET; + LE_CLEAR; + delayMicroseconds(0); + SPH_CLEAR; + CL_SET; + CL_CLEAR; + SPH_SET; + CKV_SET; +} + +void Inkplate::hscan_start(uint32_t _d) +{ + SPH_CLEAR; + GPIO.out_w1ts = (_d) | CL; + GPIO.out_w1tc = DATA | CL; + //CL_SET; + //CL_CLEAR; + SPH_SET; +} + +void Inkplate::vscan_end() { + CKV_CLEAR; + LE_SET; + LE_CLEAR; + delayMicroseconds(1); + CKV_SET; +} + +//Clear screan before new screen update using waveform +void Inkplate::clean() { + einkOn(); + int m = 0; + cleanFast(0, 1); //white + m++; + cleanFast((waveform[m] >> 30) & 3, 8); //White to white + m++; + cleanFast((waveform[m] >> 24) & 3, 1); //white to black + m++; + cleanFast((waveform[m]) & 3, 8); //Black to black + m++; + cleanFast((waveform[m] >> 6) & 3, 1); //Black to white + m++; + cleanFast((waveform[m] >> 30) & 3, 10); //White to white +} + +//Clears content from epaper diplay as fast as ESP32 can. +void Inkplate::cleanFast(uint8_t c, uint8_t rep) { + einkOn(); + uint8_t data; + if (c == 0) { + data = B10101010; //White + } else if (c == 1) { + data = B01010101; //Black + } else if (c == 2) { + data = B00000000; //Discharge + } else if (c == 3) { + data = B11111111; //Skip + } + + uint32_t _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); + for (int i = 0; i < rep; i++) { + unsigned int x, y; + vscan_start(); + for (y = 0; y < 600; y++) { + hscan_start(_send); + for (x = 0; x < 100; x++) { + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + } + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + vscan_end(); + } + delayMicroseconds(230); + } +} + +void Inkplate::cleanFast2(uint8_t c, uint8_t n, uint16_t d) { + /* + uint8_t data; + if (c == 0) { + data = B10101010; //White + } else if (c == 1) { + data = B01010101; //Black + } else if (c == 2) { + data = B00000000; //Discharge + } else if (c == 3) { + data = B11111111; //Skip + } + uint32_t _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); + begin_frame(); + for (int k = 0; k < n; k++) { + + for (int i = 0; i < 600; i++) { + begin_line(); + if (k == 0) { + + for (int j = 0; j < 100; j++) { + GPIO.out_w1tc = DATA | CL; + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + GPIO.out_w1ts = (_send) | CL; + } + } + end_line(); + } + end_frame(); + delayMicroseconds(d); + } + */ +} + void Inkplate::pinsZstate() { //CONTROL PINS pinMode(0, INPUT); @@ -820,58 +578,269 @@ void Inkplate::pinsAsOutputs() { pinMode(27, OUTPUT); //D7 } -void Inkplate::selectDisplayMode(uint8_t _mode) { - if (_mode == INKPLATE_1BIT) { - if (D_memory_new != NULL) return; - if (D_memory4Bit != NULL) free(D_memory4Bit); - //1 bit per pixel mode (monochrome mode) - D_memory_new = (uint8_t*)ps_malloc(600 * 100); - _partial = (uint8_t*)ps_malloc(600 * 100); - _pBuffer = (uint8_t*) ps_malloc(120000); - if (D_memory_new == NULL || _partial == NULL || _pBuffer == NULL) { - do { - delay(100); - } while (true); - } - memset(D_memory_new, 0, 60000); - _displayMode = INKPLATE_1BIT; +//--------------------------PRIVATE FUNCTIONS-------------------------------------------- +//Display content from RAM to display (1 bit per pixel,. monochrome picture). +void Inkplate::display1b() { + for(int i = 0; i<60000; i++) { + *(D_memory_new+i) &= *(_partial+i); + *(D_memory_new+i) |= (*(_partial+i)); } - - if (_mode == INKPLATE_3BIT) { - if (D_memory4Bit != NULL) return; - if (D_memory_new != NULL) { - free(D_memory_new); - free(_partial); - free(_pBuffer); - } - //3 bit per pixel mode (8 level grayscale mode) - D_memory4Bit = (uint8_t*)ps_malloc(240000); - if (D_memory4Bit == NULL ) { - do { - delay(100); - } while (true); + uint16_t _pos; + uint32_t _send; + uint8_t data; + uint8_t dram; + einkOn(); + //clean(); + cleanFast(0, 1); + cleanFast(1, 12); + cleanFast(2, 1); + cleanFast(0, 11); + cleanFast(2, 1); + cleanFast(1, 12); + cleanFast(2, 1); + cleanFast(0, 11); + for (int k = 0; k < 5; k++) { + _pos = 59999; + vscan_start(); + for (int i = 0; i < 600; i++) { + dram = *(D_memory_new + _pos); + data = LUTB[(dram >> 4)&0x0F]; + _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); + hscan_start(_send); + data = LUTB[dram & 0x0F]; + _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + _pos--; + for (int j = 0; j < 99; j++) { + dram = *(D_memory_new + _pos); + data = LUTB[(dram >> 4)&0x0F]; + _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + data = LUTB[dram & 0x0F]; + _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + _pos--; + } + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + vscan_end(); } - memset(D_memory4Bit, 255, 240000); - _displayMode = INKPLATE_3BIT; + delayMicroseconds(230); + } + + _pos = 59999; + vscan_start(); + for (int i = 0; i < 600; i++) { + dram = *(D_memory_new + _pos); + data = LUT2[(dram >> 4)&0x0F]; + _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); + hscan_start(_send); + data = LUT2[dram & 0x0F]; + _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + _pos--; + for (int j = 0; j < 99; j++) { + dram = *(D_memory_new + _pos); + data = LUT2[(dram >> 4)&0x0F]; + _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + data = LUT2[dram & 0x0F]; + _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + _pos--; + } + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + vscan_end(); + } + delayMicroseconds(230); + /* + for (int k = 0; k < 1; k++) { + vscan_start(); + hscan_start(); + _pos = 59999; + for (int i = 0; i < 600; i++) { + for (int j = 0; j < 100; j++) { + data = discharge[(*(D_memory_new + _pos) >> 4)]; + _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); + GPIO.out_w1tc = DATA | CL; + GPIO.out_w1ts = (_send) | CL; + data = discharge[*(D_memory_new + _pos) & 0x0F]; + _send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); + GPIO.out_w1tc = DATA | CL; + GPIO.out_w1ts = (_send) | CL; + _pos--; + } + vscan_write(); + } + CKV_CLEAR; + delayMicroseconds(230); + } + */ + cleanFast(2, 2); + cleanFast(3, 1); + vscan_start(); + einkOff(); + _blockPartial = 0; +} + +//Display content from RAM to display (3 bit per pixel,. 8 level of grayscale, STILL IN PROGRESSS, we need correct wavefrom to get good picture, use it only for pictures not for GFX). +void Inkplate::display3b() { + einkOn(); + cleanFast(0, 1); + cleanFast(1, 12); + cleanFast(2, 1); + cleanFast(0, 11); + cleanFast(2, 1); + cleanFast(1, 12); + cleanFast(2, 1); + cleanFast(0, 11); + + for (int k = 0; k < 7; k++) { + uint8_t *dp = D_memory4Bit + 239999; + uint32_t _send; + uint8_t pix1; + uint8_t pix2; + uint8_t pix3; + uint8_t pix4; + uint8_t pixel; + uint8_t pixel2; + + vscan_start(); + for (int i = 0; i < 600; i++) { + pixel = 0B00000000; + pixel2 = 0B00000000; + pix1 = *(dp--); + pix2 = *(dp--); + pix3 = *(dp--); + pix4 = *(dp--); + pixel |= ( waveform3Bit[pix1&0x07][k] << 6) | ( waveform3Bit[(pix1>>4)&0x07][k] << 4) | ( waveform3Bit[pix2&0x07][k] << 2) | ( waveform3Bit[(pix2>>4)&0x07][k] << 0); + pixel2 |= ( waveform3Bit[pix3&0x07][k] << 6) | ( waveform3Bit[(pix3>>4)&0x07][k] << 4) | ( waveform3Bit[pix4&0x07][k] << 2) | ( waveform3Bit[(pix4>>4)&0x07][k] << 0); + + _send = ((pixel & B00000011) << 4) | (((pixel & B00001100) >> 2) << 18) | (((pixel & B00010000) >> 4) << 23) | (((pixel & B11100000) >> 5) << 25); + hscan_start(_send); + _send = ((pixel2 & B00000011) << 4) | (((pixel2 & B00001100) >> 2) << 18) | (((pixel2 & B00010000) >> 4) << 23) | (((pixel2 & B11100000) >> 5) << 25); + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + + for (int j = 0; j < 99; j++) { + + pixel = 0B00000000; + pixel2 = 0B00000000; + pix1 = *(dp--); + pix2 = *(dp--); + pix3 = *(dp--); + pix4 = *(dp--); + pixel |= ( waveform3Bit[pix1&0x07][k] << 6) | ( waveform3Bit[(pix1>>4)&0x07][k] << 4) | ( waveform3Bit[pix2&0x07][k] << 2) | ( waveform3Bit[(pix2>>4)&0x07][k] << 0); + pixel2 |= ( waveform3Bit[pix3&0x07][k] << 6) | ( waveform3Bit[(pix3>>4)&0x07][k] << 4) | ( waveform3Bit[pix4&0x07][k] << 2) | ( waveform3Bit[(pix4>>4)&0x07][k] << 0); + + _send = ((pixel & B00000011) << 4) | (((pixel & B00001100) >> 2) << 18) | (((pixel & B00010000) >> 4) << 23) | (((pixel & B11100000) >> 5) << 25); + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + + _send = ((pixel2 & B00000011) << 4) | (((pixel2 & B00001100) >> 2) << 18) | (((pixel2 & B00010000) >> 4) << 23) | (((pixel2 & B11100000) >> 5) << 25); + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + } + GPIO.out_w1ts = (_send) | CL; + GPIO.out_w1tc = DATA | CL; + vscan_end(); + } + delayMicroseconds(230); + } + cleanFast(2, 1); + cleanFast(3, 1); + vscan_start(); + einkOff(); +} + +uint32_t Inkplate::read32(uint8_t* c) { + return (*(c) | (*(c + 1) << 8) | (*(c + 2) << 16) | (*(c + 3) << 24)); +} + +uint16_t Inkplate::read16(uint8_t* c) { + return (*(c) | (*(c + 1) << 8)); +} + +void Inkplate::readBmpHeader(SdFile *_f, struct bitmapHeader *_h) { + uint8_t header[100]; + _f->rewind(); + _f->read(header, 100); + _h->signature = read16(header + 0); + _h->fileSize = read32(header + 2); + _h->startRAW = read32(header + 10); + _h->dibHeaderSize = read32(header + 14); + _h->width = read32(header + 18); + _h->height = read32(header + 22); + _h->color = read16(header + 28); + _h->compression = read32(header + 30); + return; +} + +int Inkplate::drawMonochromeBitmap(SdFile *f, struct bitmapHeader bmpHeader, int x, int y) { + int w = bmpHeader.width; + int h = bmpHeader.height; + uint8_t paddingBits = w % 32; + w /= 32; + + f->seekSet(bmpHeader.startRAW); + int i, j; + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++) { + uint32_t pixelRow = f->read() << 24 | f->read() << 16 | f->read() << 8 | f->read(); + for (int n = 0; n < 32; n++) { + drawPixel((i * 32) + n + x, h - j + y, !(pixelRow & (1ULL << (31 - n)))); + } + } + if (paddingBits) { + uint32_t pixelRow = f->read() << 24 | f->read() << 16 | f->read() << 8 | f->read(); + for (int n = 0; n < paddingBits; n++) { + drawPixel((i * 32) + n + x, h - j + y, !(pixelRow & (1ULL << (31 - n)))); + } + } + } + f->close(); + return 1; +} + +int Inkplate::drawGrayscaleBitmap24(SdFile *f, struct bitmapHeader bmpHeader, int x, int y) { + int w = bmpHeader.width; + int h = bmpHeader.height; + char padding = w % 4; + f->seekSet(bmpHeader.startRAW); + int i, j; + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++) { + //This is the proper way of converting True Color (24 Bit RGB) bitmap file into grayscale, but it takes waaay too much time (full size picture takes about 17s to decode!) + //float px = (0.2126 * (readByteFromSD(&file) / 255.0)) + (0.7152 * (readByteFromSD(&file) / 255.0)) + (0.0722 * (readByteFromSD(&file) / 255.0)); + //px = pow(px, 1.5); + //display.drawPixel(i + x, h - j + y, (uint8_t)(px*7)); + + //So then, we are convertng it to grayscale using good old average and gamma correction (from LUT). With this metod, it is still slow (full size image takes 4 seconds), but much beter than prev mentioned method. + uint8_t px = (f->read() * 2126 / 10000) + (f->read() * 7152 / 10000) + (f->read() * 722 / 10000); + //drawPixel(i + x, h - j + y, gammaLUT[px]); + drawPixel(i + x, h - j + y, px>>5); + //drawPixel(i + x, h - j + y, px/32); + } + if (padding) { + for (int p = 0; p < padding; p++) { + f->read(); + } + } + } + f->close(); + return 1; +} + +void Inkplate::precalculateGamma(uint8_t* c, float gamma) { + for (int i = 0; i < 256; i++) { + c[i] = int(round((pow(i / 255.0, gamma)) * 15)); } } -uint8_t Inkplate::getDisplayMode() { - return _displayMode; -} - -uint8_t Inkplate::readTouchpad(uint8_t _pad) { - return mcp.digitalRead((_pad&3)+10); -} - -int8_t Inkplate::readTemperature() { - return _temperature; -} - -double Inkplate::readBattery() { - mcp.digitalWrite(9, HIGH); - delay(1); - int adc = analogRead(35); - mcp.digitalWrite(9, LOW); - return (double(adc) / 4095 * 3.3 * 2); -} \ No newline at end of file diff --git a/Inkplate.h b/Inkplate.h index 8934b22..90cc3eb 100644 --- a/Inkplate.h +++ b/Inkplate.h @@ -1,154 +1,172 @@ -#ifndef __INKPLATE_H__ -#define __INKPLATE_H__ -#if ARDUINO >= 100 -#include "Arduino.h" -#else -#include "WProgram.h" -#endif - -#ifndef ARDUINO_ESP32_DEV -#error "Wrong board selected! Select ESP32 Wrover from board menu!" -#endif - -#include -#include -#include "Adafruit_MCP23017.h" -#define E_INK_WIDTH 800 -#define E_INK_HEIGHT 600 -extern Adafruit_MCP23017 mcp; - -#define DATA 0x0E8C0030 //D0-D7 = GPIO4 GPIO5 GPIO18 GPIO19 GPIO23 GPIO25 GPIO26 GPIO27 - -#define CL 0x01 //GPIO0 -#define CL_SET {GPIO.out_w1ts = CL;} -#define CL_CLEAR {GPIO.out_w1tc = CL;} - -#define LE 0x04 //GPIO2 -#define LE_SET {GPIO.out_w1ts = LE;} -#define LE_CLEAR {GPIO.out_w1tc = LE;} - -#define CKV 0x01 //GPIO32 -#define CKV_SET {GPIO.out1_w1ts.val = CKV;} -#define CKV_CLEAR {GPIO.out1_w1tc.val = CKV;} - -#define SPH 0x02 //GPIO33 -#define SPH_SET {GPIO.out1_w1ts.val = SPH;} -#define SPH_CLEAR {GPIO.out1_w1tc.val = SPH;} - -//I/O Expander - A Channel -#define GMOD 1 //GPIOA1 -#define GMOD_SET {mcp.digitalWrite(GMOD, HIGH);} -#define GMOD_CLEAR {mcp.digitalWrite(GMOD, HIGH);} - -#define OE 0 //GPIOA0 -#define OE_SET {mcp.digitalWrite(OE, HIGH);} -#define OE_CLEAR {mcp.digitalWrite(OE, LOW);} - -#define SPV 2 //GPIOA5 -#define SPV_SET {mcp.digitalWrite(SPV, HIGH);} -#define SPV_CLEAR {mcp.digitalWrite(SPV, LOW);} - -#define WAKEUP 3 //GPIOA3 -#define WAKEUP_SET {mcp.digitalWrite(WAKEUP, HIGH);} -#define WAKEUP_CLEAR {mcp.digitalWrite(WAKEUP, LOW);} - -#define PWRUP 4 //GPIOA4 -#define PWRUP_SET {mcp.digitalWrite(PWRUP, HIGH);} -#define PWRUP_CLEAR {mcp.digitalWrite(PWRUP, LOW);} - -#define VCOM 5 //GPIOA6 -#define VCOM_SET {mcp.digitalWrite(VCOM, HIGH);} -#define VCOM_CLEAR {mcp.digitalWrite(VCOM, LOW);} - -#define GDISP_SCREEN_HEIGHT 600 -#define CKV_CLOCK ckvClock(); -#define SCLOCK { CL_SET; CL_CLEAR; } - -#ifndef _swap_int16_t -#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; } -#endif - -#define BLACK 1 -#define WHITE 0 -#define GPIO0_ENABLE 8 -#define REF_RATE 6 -#define INKPLATE_1BIT 0 -#define INKPLATE_3BIT 1 -#define PAD1 0 -#define PAD2 1 -#define PAD3 2 - -static void ckvClock(); -static void usleep1(); - -class Inkplate : public Adafruit_GFX { - public: - Inkplate(uint8_t _mode); - Inkplate(); - uint8_t* D_memory_new; - //uint8_t D_memory_new[600][100]; - uint8_t* _partial; - uint8_t* D_memory4Bit; - uint8_t * _pBuffer; - const uint8_t LUT2[16] = {B10101010, B10101001, B10100110, B10100101, B10011010, B10011001, B10010110, B10010101, B01101010, B01101001, B01100110, B01100101, B01011010, B01011001, B01010110, B01010101}; - const uint8_t LUTW[16] = {B11111111, B11111110, B11111011, B11111010, B11101111, B11101110, B11101011, B11101010, B10111111, B10111110, B10111011, B10111010, B10101111, B10101110, B10101011, B10101010}; - const uint8_t LUTB[16] = {B11111111, B11111101, B11110111, B11110101, B11011111, B11011101, B11010111, B11010101, B01111111, B01111101, B01110111, B01110101, B01011111, B01011101, B01010111, B01010101}; - const uint8_t pixelMaskLUT[8] = {B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000}; - //const uint8_t pixelMaskLUT[8] = {B10000000, B01000000, B00100000, B00010000, B00001000, B00000100, B00000010, B00000001}; - const uint8_t pixelMaskGLUT[2] = {B00001111, B11110000}; - const uint8_t pixel_to_epd_cmd[3] = {B00000001, B00000010, B00000011}; - //BLACK->WHITE - //WORKING! -> const uint8_t waveform3Bit[8][8] = {{0, 0, 1, 1, 1, 1, 1, 0}, {1, 1, 2, 2, 1, 1, 1, 0}, {0, 1, 1, 2, 1, 2, 1, 0}, {3, 3, 2, 2, 2, 1, 1, 0}, {2, 2, 2, 2, 3, 3, 1, 0}, {3, 3, 1, 1, 1, 2, 2, 3}, {0, 1, 1, 1, 2, 2, 2, 0}, {3, 3, 3, 3, 3, 2, 2, 0}}; - //BETTER WF -> const uint8_t waveform3Bit[8][12] = {{0, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 0}, {2, 1, 2, 1, 1, 0, 2, 1, 2, 1, 1}, {1, 2, 2, 2, 1, 1, 1, 3, 3, 3, 0}, {1, 1, 1, 2, 0, 1, 1, 1, 2, 3, 0}, {1, 1, 2, 1, 2, 0, 1, 1, 2, 1, 2}, {0, 1, 1, 1, 2, 2, 1, 1, 1, 2, 2, 0}, {1, 2, 2, 1, 2, 1, 2, 2, 1, 2, 0}, {3, 3, 3, 3, 3, 2, 3, 0, 2, 2, 3, 0}}; - const uint8_t waveform3Bit[8][12] = {{3, 3, 3, 1, 1, 1, 0, 0, 1, 1, 1, 1}, {1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1}, {1, 2, 2, 2, 1, 1, 1, 3, 3, 3, 3}, {1, 1, 2, 1, 2, 0, 1, 1, 2, 1, 2}, {0, 1, 1, 1, 2, 2, 1, 1, 1, 2, 2, 3}, {1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 2, 2}, {1, 2, 1, 2, 2, 0, 1, 2, 1, 2, 2}, {1, 2, 2, 2, 2, 0, 1, 2, 2, 2, 2}}; - //const char partialBlack[] = {1, 1, 1, 1, 1, 3, 3, 3, 3, 3} - //const char partialWhite[] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2}; - - //PVI waveform for cleaning screen, not sure if it is correct, but it cleans screen properly. - const uint32_t waveform[50] = {0x00000008, 0x00000008, 0x00200408, 0x80281888, 0x60a81898, 0x60a8a8a8, 0x60a8a8a8, 0x6068a868, 0x6868a868, 0x6868a868, 0x68686868, 0x6a686868, 0x5a686868, 0x5a686868, 0x5a586a68, 0x5a5a6a68, 0x5a5a6a68, 0x55566a68, 0x55565a64, 0x55555654, 0x55555556, 0x55555556, 0x55555556, 0x55555516, 0x55555596, 0x15555595, 0x95955595, 0x95959595, 0x95949495, 0x94949495, 0x94949495, 0xa4949494, 0x9494a4a4, 0x84a49494, 0x84948484, 0x84848484, 0x84848484, 0x84848484, 0xa5a48484, 0xa9a4a4a8, 0xa9a8a8a8, 0xa5a9a9a4, 0xa5a5a5a4, 0xa1a5a5a1, 0xa9a9a9a9, 0xa9a9a9a9, 0xa9a9a9a9, 0xa9a9a9a9, 0x15151515, 0x11111111}; - - //Settings for contrast. Basicly, each element in array describes how many times each color is written to display (starting form darkest to lightest). - //This is for 3 bit mode, but you can expant to 8 bit mode if you want, by adding more wariables, changing display seqence in display4Bit() and expanding the memory buffer size to double the current size. - const uint8_t contrast_cycles[3] = {1, 4, 3}; - const uint8_t sz_contrast_cycles = sizeof(contrast_cycles) / sizeof(uint8_t); - - void drawPixel(int16_t x0, int16_t y0, uint16_t color); - void begin(void); - void clearDisplay(); - void display(); - void partialUpdate(); - void draw_mode_off(); - void draw_mode_on(); - void advance_line(); - void begin_frame(); - void end_frame(); - void begin_line(); - void end_line(); - void drawBitmap3Bit(int16_t _x, int16_t _y, const unsigned char* _p, int16_t _w, int16_t _h); - void fillScreen(uint8_t c); - void cleanFast(uint8_t c); - void clean(); - void setRotation(uint8_t); - void einkOff(void); - void einkOn(void); - void selectDisplayMode(uint8_t _mode); - uint8_t getDisplayMode(); - void end_line_slow(); - void pinsZstate(); - void pinsAsOutputs(); - uint8_t getPanelState(); - uint8_t readTouchpad(uint8_t); - int8_t readTemperature(); - double readBattery(); - - private: - int8_t _temperature; - void display1b(); - void display3b(); - uint8_t _panelOn=0; - uint8_t _rotation = 0; - uint16_t _tempRotation; - uint8_t _displayMode = 0; //By default, 1 bit mode is used -}; - -#endif +#ifndef __INKPLATE_H__ +#define __INKPLATE_H__ +#if ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +#ifndef ARDUINO_ESP32_DEV +#error "Wrong board selected! Select ESP32 Wrover from board menu!" +#endif + +#include "Adafruit_GFX.h" +#include "Wire.h" +#include "SPI.h" +#include "Adafruit_MCP23017.h" +#include "SdFat.h" + +#define INKPLATE_GAMMA 1.45 +#define E_INK_WIDTH 800 +#define E_INK_HEIGHT 600 +#define BLACK 1 +#define WHITE 0 +#define GPIO0_ENABLE 8 +#define INKPLATE_1BIT 0 +#define INKPLATE_3BIT 1 +#define PAD1 0 +#define PAD2 1 +#define PAD3 2 + +#define DATA 0x0E8C0030 //D0-D7 = GPIO4 GPIO5 GPIO18 GPIO19 GPIO23 GPIO25 GPIO26 GPIO27 + +#define CL 0x01 //GPIO0 +#define CL_SET {GPIO.out_w1ts = CL;} +#define CL_CLEAR {GPIO.out_w1tc = CL;} + +#define LE 0x04 //GPIO2 +#define LE_SET {GPIO.out_w1ts = LE;} +#define LE_CLEAR {GPIO.out_w1tc = LE;} + +#define CKV 0x01 //GPIO32 +#define CKV_SET {GPIO.out1_w1ts.val = CKV;} +#define CKV_CLEAR {GPIO.out1_w1tc.val = CKV;} + +#define SPH 0x02 //GPIO33 +#define SPH_SET {GPIO.out1_w1ts.val = SPH;} +#define SPH_CLEAR {GPIO.out1_w1tc.val = SPH;} + +//#define SPV 15 //GPIO15 +//#define SPV_SET {digitalWrite(SPV, HIGH);} +//#define SPV_CLEAR {digitalWrite(SPV, LOW);} + +//I/O Expander - A Channel +#define GMOD 1 //GPIOA1 +#define GMOD_SET {mcp.digitalWrite(GMOD, HIGH);} +#define GMOD_CLEAR {mcp.digitalWrite(GMOD, LOW);} + +#define OE 0 //GPIOA0 +#define OE_SET {mcp.digitalWrite(OE, HIGH);} +#define OE_CLEAR {mcp.digitalWrite(OE, LOW);} + +#define SPV 2 //GPIOA5 +#define SPV_SET {mcp.digitalWrite(SPV, HIGH);} +#define SPV_CLEAR {mcp.digitalWrite(SPV, LOW);} + +#define WAKEUP 3 //GPIOA3 +#define WAKEUP_SET {mcp.digitalWrite(WAKEUP, HIGH);} +#define WAKEUP_CLEAR {mcp.digitalWrite(WAKEUP, LOW);} + +#define PWRUP 4 //GPIOA4 +#define PWRUP_SET {mcp.digitalWrite(PWRUP, HIGH);} +#define PWRUP_CLEAR {mcp.digitalWrite(PWRUP, LOW);} + +#define VCOM 5 //GPIOA6 +#define VCOM_SET {mcp.digitalWrite(VCOM, HIGH);} +#define VCOM_CLEAR {mcp.digitalWrite(VCOM, LOW);} + +#define CKV_CLOCK ckvClock(); + +#ifndef _swap_int16_t +#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; } +#endif + +extern Adafruit_MCP23017 mcp; +extern SPIClass spi2; +extern SdFat sd; +static void ckvClock(); +static void usleep1(); + +class Inkplate : public Adafruit_GFX { + public: + uint8_t* D_memory_new; + uint8_t* _partial; + uint8_t* D_memory4Bit; + uint8_t * _pBuffer; + const uint8_t LUT2[16] = {B10101010, B10101001, B10100110, B10100101, B10011010, B10011001, B10010110, B10010101, B01101010, B01101001, B01100110, B01100101, B01011010, B01011001, B01010110, B01010101}; + const uint8_t LUTW[16] = {B11111111, B11111110, B11111011, B11111010, B11101111, B11101110, B11101011, B11101010, B10111111, B10111110, B10111011, B10111010, B10101111, B10101110, B10101011, B10101010}; + const uint8_t LUTB[16] = {B11111111, B11111101, B11110111, B11110101, B11011111, B11011101, B11010111, B11010101, B01111111, B01111101, B01110111, B01110101, B01011111, B01011101, B01010111, B01010101}; + const uint8_t pixelMaskLUT[8] = {B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000}; + const uint8_t pixelMaskGLUT[2] = {B00001111, B11110000}; + const uint8_t discharge[16] = {B11111111, B11111100, B11110011, B11110000, B11001111, B11001100, B11000011, B11000000, B00111111, B00111100, B00110011, B00110000, B00001111, B00001100, B00000011, B00000000}; + //BLACK->WHITE + //THIS IS OKAYISH WAVEFORM FOR GRAYSCALE. IT CAN BE MUCH BETTER. + const uint8_t waveform3Bit[8][7] = {{0, 0, 0, 0, 1, 1, 1}, {0, 0, 1, 1, 1, 2, 1}, {0, 1, 1, 2, 1, 2, 1}, {0, 0, 1, 1, 2, 1, 2}, {1, 1, 1, 2, 2, 1, 2}, {0, 0, 1, 1, 1, 2, 2}, {0, 1, 1, 2, 1, 2, 2}, {0, 0, 0, 0, 0, 0, 2}}; + //const uint8_t waveform3Bit[8][12] = {{3,3,3,1,1,1,1,1,1,1,2,0}, {3,3,3,3,1,1,1,1,1,1,2,0}, {3,3,3,3,3,1,1,1,1,1,2,0}, {3,3,3,3,3,3,1,1,1,1,2,0}, {3,3,3,3,3,3,3,1,1,1,2,0}, {3,3,3,3,3,3,3,2,1,1,2,0}, {3,3,3,3,3,3,3,3,3,1,2,0}, {3,3,3,3,3,3,3,3,3,3,2,0}}; + //const uint8_t waveform3Bit[16][12] = {{0,0,0,0,0,0,1,2,1,1,0,3},{0,0,1,1,1,2,2,2,1,1,0,3},{0,0,0,1,1,2,2,2,1,1,0,3}, {0,0,0,1,2,1,2,1,2,1,3}, {0,0,2,1,2,1,2,1,2,1,3}, {0,0,1,2,2,1,1,1,1,2,0,3}, {0,0,0,2,1,1,1,1,0,2,0,3}, {0,0,2,1,2,2,1,1,1,2,0,3}, {0,0,0,2,2,2,1,1,1,2,0,3}, {0,0,0,0,0,0,2,1,1,2,0,3}, {0,0,0,0,0,2,2,1,1,2,0,3}, {0,0,0,0,0,1,1,1,2,2,0,3}, {0,0,0,0,1,2,1,2,1,2,0,3}, {0,0,0,0,1,1,2,2,1,2,0,3},{0,0,0,0,1,1,1,2,2,2,0,3}, {0,0,0,0,0,0,0,0,0,2,0,3}}; + //PVI waveform for cleaning screen, not sure if it is correct, but it cleans screen properly. + const uint32_t waveform[50] = {0x00000008, 0x00000008, 0x00200408, 0x80281888, 0x60a81898, 0x60a8a8a8, 0x60a8a8a8, 0x6068a868, 0x6868a868, 0x6868a868, 0x68686868, 0x6a686868, 0x5a686868, 0x5a686868, 0x5a586a68, 0x5a5a6a68, 0x5a5a6a68, 0x55566a68, 0x55565a64, 0x55555654, 0x55555556, 0x55555556, 0x55555556, 0x55555516, 0x55555596, 0x15555595, 0x95955595, 0x95959595, 0x95949495, 0x94949495, 0x94949495, 0xa4949494, 0x9494a4a4, 0x84a49494, 0x84948484, 0x84848484, 0x84848484, 0x84848484, 0xa5a48484, 0xa9a4a4a8, 0xa9a8a8a8, 0xa5a9a9a4, 0xa5a5a5a4, 0xa1a5a5a1, 0xa9a9a9a9, 0xa9a9a9a9, 0xa9a9a9a9, 0xa9a9a9a9, 0x15151515, 0x11111111}; + + struct bitmapHeader { + uint16_t signature; + uint32_t fileSize; + uint32_t startRAW; + uint32_t dibHeaderSize; + uint32_t width; + uint32_t height; + uint16_t color; + uint32_t compression; + }; + + Inkplate(uint8_t _mode); + void begin(void); + void drawPixel(int16_t x0, int16_t y0, uint16_t color); + void clearDisplay(); + void display(); + void partialUpdate(); + void drawBitmap3Bit(int16_t _x, int16_t _y, const unsigned char* _p, int16_t _w, int16_t _h); + void setRotation(uint8_t); + void einkOff(void); + void einkOn(void); + void selectDisplayMode(uint8_t _mode); + uint8_t getDisplayMode(); + int drawBitmapFromSD(SdFile* p, int x, int y); + int drawBitmapFromSD(char* fileName, int x, int y); + int sdCardInit(); + SdFat getSdFat(); + SPIClass getSPI(); + uint8_t getPanelState(); + uint8_t readTouchpad(uint8_t); + int8_t readTemperature(); + double readBattery(); + void vscan_start(); + void vscan_write(); + void hscan_start(uint32_t _d = 0); + void vscan_end(); + void clean(); + void cleanFast(uint8_t c, uint8_t rep); + void cleanFast2(uint8_t c, uint8_t n, uint16_t d); + void pinsZstate(); + void pinsAsOutputs(); + + private: + uint8_t gammaLUT[256]; + int8_t _temperature; + uint8_t _panelOn = 0; + uint8_t _rotation = 0; + uint8_t _displayMode = 0; //By default, 1 bit mode is used + int sdCardOk = 0; + uint8_t _blockPartial = 1; + uint8_t _beginDone = 0; + + void display1b(); + void display3b(); + uint32_t read32(uint8_t* c); + uint16_t read16(uint8_t* c); + void readBmpHeader(SdFile *_f, struct bitmapHeader *_h); + int drawMonochromeBitmap(SdFile *f, struct bitmapHeader bmpHeader, int x, int y); + int drawGrayscaleBitmap24(SdFile *f, struct bitmapHeader bmpHeader, int x, int y); + void precalculateGamma(uint8_t* c, float gamma); +}; + +#endif