From 102d6ec04bce9289cc78852eded67b3ab9b69629 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Fri, 16 Apr 2021 19:36:45 +0200 Subject: [PATCH] update universal display --- lib/lib_display/UDisplay/uDisplay.cpp | 596 ++++++++++++++++++++--- lib/lib_display/UDisplay/uDisplay.h | 36 +- tasmota/displaydesc/ILI9341_desc.txt | 5 +- tasmota/displaydesc/ILI9342_desc.txt | 3 +- tasmota/displaydesc/ILI9488_desc.txt | 7 +- tasmota/displaydesc/SD1306_desc.txt | 24 + tasmota/displaydesc/SH1106_desc.txt | 3 +- tasmota/displaydesc/SSD1351_desc.txt | 2 +- tasmota/displaydesc/ST7789_desc.txt | 3 +- tasmota/displaydesc/WS_epaper29_desc.txt | 17 + tasmota/displaydesc/readme.md | 19 + tasmota/xdsp_17_universal.ino | 30 +- 12 files changed, 660 insertions(+), 85 deletions(-) create mode 100644 tasmota/displaydesc/SD1306_desc.txt create mode 100644 tasmota/displaydesc/WS_epaper29_desc.txt create mode 100644 tasmota/displaydesc/readme.md diff --git a/lib/lib_display/UDisplay/uDisplay.cpp b/lib/lib_display/UDisplay/uDisplay.cpp index 555b0059b..0694a0d3c 100644 --- a/lib/lib_display/UDisplay/uDisplay.cpp +++ b/lib/lib_display/UDisplay/uDisplay.cpp @@ -18,8 +18,6 @@ */ #include -#include -#include #include "uDisplay.h" #define UDSP_DEBUG @@ -42,6 +40,14 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { sa_mode = 16; saw_3 = 0xff; dim_op = 0xff; + dsp_off = 0xff; + dsp_on = 0xff; + lutpsize = 0; + lutfsize = 0; + lutptime = 35; + lutftime = 350; + lut3time = 10; + ep_mode = 0; startline = 0xA1; uint8_t section = 0; dsp_ncmds = 0; @@ -174,10 +180,20 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { rot_t[3] = next_hex(&lp1); break; case 'A': - saw_1 = next_hex(&lp1); - saw_2 = next_hex(&lp1); - saw_3 = next_hex(&lp1); - sa_mode = next_val(&lp1); + if (interface == _UDSP_I2C) { + saw_1 = next_hex(&lp1); + i2c_page_start = next_hex(&lp1); + i2c_page_end = next_hex(&lp1); + saw_2 = next_hex(&lp1); + i2c_col_start = next_hex(&lp1); + i2c_col_end = next_hex(&lp1); + saw_3 = next_hex(&lp1); + } else { + saw_1 = next_hex(&lp1); + saw_2 = next_hex(&lp1); + saw_3 = next_hex(&lp1); + sa_mode = next_val(&lp1); + } break; case 'P': col_mode = next_val(&lp1); @@ -189,6 +205,31 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { case 'D': dim_op = next_hex(&lp1); break; + case 'L': + while (1) { + if (!str2c(&lp1, ibuff, sizeof(ibuff))) { + lut_full[lutfsize++] = strtol(ibuff, 0, 16); + } else { + break; + } + if (lutfsize >= sizeof(lut_full)) break; + } + break; + case 'l': + while (1) { + if (!str2c(&lp1, ibuff, sizeof(ibuff))) { + lut_partial[lutpsize++] = strtol(ibuff, 0, 16); + } else { + break; + } + if (lutpsize >= sizeof(lut_partial)) break; + } + break; + case 'T': + lutftime = next_val(&lp1); + lutptime = next_val(&lp1); + lut3time = next_val(&lp1); + break; } } } @@ -200,26 +241,54 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { lp++; } } + + if (lutfsize && lutpsize) { + ep_mode = 1; + } + #ifdef UDSP_DEBUG - Serial.printf("Nr. : %d\n", spi_nr); - Serial.printf("CS : %d\n", spi_cs); - Serial.printf("CLK : %d\n", spi_clk); - Serial.printf("MOSI: %d\n", spi_mosi); - Serial.printf("DC : %d\n", spi_dc); - Serial.printf("BPAN: %d\n", bpanel); - Serial.printf("RES : %d\n", reset); - Serial.printf("MISO: %d\n", spi_miso); - Serial.printf("SPED: %d\n", spi_speed*1000000); - Serial.printf("Pixels: %d\n", col_mode); - Serial.printf("SaMode: %d\n", sa_mode); + Serial.printf("xs : %d\n", gxs); + Serial.printf("ys : %d\n", gys); + Serial.printf("bpp: %d\n", bpp); - Serial.printf("opts: %02x,%02x,%02x\n", saw_3, dim_op, startline); + if (interface == _UDSP_SPI) { + Serial.printf("Nr. : %d\n", spi_nr); + Serial.printf("CS : %d\n", spi_cs); + Serial.printf("CLK : %d\n", spi_clk); + Serial.printf("MOSI: %d\n", spi_mosi); + Serial.printf("DC : %d\n", spi_dc); + Serial.printf("BPAN: %d\n", bpanel); + Serial.printf("RES : %d\n", reset); + Serial.printf("MISO: %d\n", spi_miso); + Serial.printf("SPED: %d\n", spi_speed*1000000); + Serial.printf("Pixels: %d\n", col_mode); + Serial.printf("SaMode: %d\n", sa_mode); - Serial.printf("SetAddr : %x,%x,%x\n", saw_1, saw_2, saw_3); + Serial.printf("opts: %02x,%02x,%02x\n", saw_3, dim_op, startline); - Serial.printf("Rot 0: %x,%x - %d - %d\n", madctrl, rot[0], x_addr_offs[0], y_addr_offs[0]); + Serial.printf("SetAddr : %x,%x,%x\n", saw_1, saw_2, saw_3); + Serial.printf("Rot 0: %x,%x - %d - %d\n", madctrl, rot[0], x_addr_offs[0], y_addr_offs[0]); + + if (ep_mode) { + Serial.printf("LUT_Partial : %d\n", lutpsize); + Serial.printf("LUT_Full : %d\n", lutfsize); + } + } + if (interface == _UDSP_I2C) { + Serial.printf("Addr : %02x\n", i2caddr); + Serial.printf("SCL : %d\n", i2c_scl); + Serial.printf("SDA : %d\n", i2c_sda); + + Serial.printf("SPA : %x\n", saw_1); + Serial.printf("pa_sta: %x\n", i2c_page_start); + Serial.printf("pa_end: %x\n", i2c_page_end); + Serial.printf("SCA : %x\n", saw_2); + Serial.printf("ca_sta: %x\n", i2c_col_start); + Serial.printf("pa_end: %x\n", i2c_col_end); + Serial.printf("WRA : %x\n", saw_3); + } #endif } @@ -237,26 +306,46 @@ Renderer *uDisplay::Init(void) { } if (interface == _UDSP_I2C) { - Wire.begin(i2c_sda, i2c_scl); + wire = &Wire; + wire->begin(i2c_sda, i2c_scl); if (bpp < 16) { if (buffer) free(buffer); #ifdef ESP8266 - buffer = (uint8_t*)calloc((width()*height()*bpp)/8, 1); + buffer = (uint8_t*)calloc((gxs * gys * bpp) / 8, 1); #else if (psramFound()) { - buffer = (uint8_t*)heap_caps_malloc((width()*height()*bpp)/8, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + buffer = (uint8_t*)heap_caps_malloc((gxs * gys * bpp) / 8, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); } else { - buffer = (uint8_t*)calloc((width()*height()*bpp)/8, 1); + buffer = (uint8_t*)calloc((gxs * gys * bpp) / 8, 1); } #endif } +#ifdef UDSP_DEBUG + Serial.printf("I2C cmds: %d\n", dsp_ncmds); +#endif for (uint32_t cnt = 0; cnt < dsp_ncmds; cnt++) { i2c_command(dsp_cmds[cnt]); +#ifdef UDSP_DEBUG + Serial.printf("cmd = %x\n", dsp_cmds[cnt]); +#endif } } if (interface == _UDSP_SPI) { + + if (ep_mode) { + #ifdef ESP8266 + buffer = (uint8_t*)calloc((gxs * gys * bpp) / 8, 1); + #else + if (psramFound()) { + buffer = (uint8_t*)heap_caps_malloc((gxs * gys * bpp) / 8, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + } else { + buffer = (uint8_t*)calloc((gxs * gys * bpp) / 8, 1); + } + #endif + } + if (bpanel >= 0) { #ifdef ESP32 ledcSetup(ESP32_PWM_CHANNEL, 4000, 8); @@ -285,8 +374,6 @@ Renderer *uDisplay::Init(void) { digitalWrite(spi_clk, LOW); pinMode(spi_mosi, OUTPUT); digitalWrite(spi_mosi, LOW); - pinMode(spi_dc, OUTPUT); - digitalWrite(spi_dc, LOW); } #endif // ESP8266 @@ -302,8 +389,6 @@ Renderer *uDisplay::Init(void) { digitalWrite(spi_clk, LOW); pinMode(spi_mosi, OUTPUT); digitalWrite(spi_mosi, LOW); - pinMode(spi_dc, OUTPUT); - digitalWrite(spi_dc, LOW); } #endif // ESP32 @@ -321,7 +406,7 @@ Renderer *uDisplay::Init(void) { uint8_t args = dsp_cmds[index++]; #ifdef UDSP_DEBUG - Serial.printf("cmd, args %x, %d ", iob, args&0x1f); + Serial.printf("cmd, args %02x, %d ", iob, args&0x1f); #endif for (uint32_t cnt = 0; cnt < (args & 0x1f); cnt++) { iob = dsp_cmds[index++]; @@ -343,10 +428,35 @@ Renderer *uDisplay::Init(void) { SPI_END_TRANSACTION } + + // must init luts on epaper + if (ep_mode) { + Init_EPD(DISPLAY_INIT_FULL); + Init_EPD(DISPLAY_INIT_PARTIAL); + } + return this; } -void uDisplay::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) { + +void uDisplay::DisplayInit(int8_t p, int8_t size, int8_t rot, int8_t font) { + if (p !=DISPLAY_INIT_MODE && ep_mode) { + if (p == DISPLAY_INIT_PARTIAL) { + if (lutpsize) { + SetLut(lut_partial); + Updateframe_EPD(); + delay(lutptime * 10); + } + return; + } else if (p == DISPLAY_INIT_FULL) { + if (lutfsize) { + SetLut(lut_full); + Updateframe_EPD(); + delay(lutftime * 10); + } + return; + } + } else { setRotation(rot); invertDisplay(false); setTextWrap(false); @@ -357,6 +467,11 @@ void uDisplay::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) { setCursor(0,0); fillScreen(bg_col); Updateframe(); + +#ifdef UDSP_DEBUG + Serial.printf("Dsp Init complete \n"); +#endif + } } void uDisplay::spi_command(uint8_t val) { @@ -444,25 +559,59 @@ void uDisplay::spi_command_one(uint8_t val) { void uDisplay::i2c_command(uint8_t val) { //Serial.printf("%02x\n",val ); - Wire.beginTransmission(i2caddr); - Wire.write(0); - Wire.write(val); - Wire.endTransmission(); + wire->beginTransmission(i2caddr); + wire->write(0); + wire->write(val); + wire->endTransmission(); } +#define WIRE_MAX 32 + void uDisplay::Updateframe(void) { + if (ep_mode) { + Updateframe_EPD(); + return; + } + if (interface == _UDSP_I2C) { - i2c_command(saw_1 | 0x0); // low col = 0 - i2c_command(saw_2 | 0x0); // hi col = 0 - i2c_command(saw_3 | 0x0); // line #0 + + #if 0 + i2c_command(saw_1); + i2c_command(i2c_page_start); + i2c_command(i2c_page_end); + i2c_command(saw_2); + i2c_command(i2c_col_start); + i2c_command(i2c_col_end); + + uint16_t count = gxs * ((gys + 7) / 8); + uint8_t *ptr = buffer; + wire->beginTransmission(i2caddr); + i2c_command(saw_3); + uint8_t bytesOut = 1; + while (count--) { + if (bytesOut >= WIRE_MAX) { + wire->endTransmission(); + wire->beginTransmission(i2caddr); + i2c_command(saw_3); + bytesOut = 1; + } + i2c_command(*ptr++); + bytesOut++; + } + wire->endTransmission(); +#else + + i2c_command(saw_1 | 0x0); // set low col = 0, 0x00 + i2c_command(i2c_page_start | 0x0); // set hi col = 0, 0x10 + i2c_command(i2c_page_end | 0x0); // set startline line #0, 0x40 uint8_t ys = gys >> 3; uint8_t xs = gxs >> 3; //uint8_t xs = 132 >> 3; - uint8_t m_row = 0; - uint8_t m_col = 2; + uint8_t m_row = saw_2; + uint8_t m_col = i2c_col_start; uint16_t p = 0; @@ -470,24 +619,32 @@ void uDisplay::Updateframe(void) { for ( i = 0; i < ys; i++) { // send a bunch of data in one xmission - i2c_command(0xB0 + i + m_row);//set page address - i2c_command(m_col & 0xf);//set lower column address - i2c_command(0x10 | (m_col >> 4));//set higher column address + i2c_command(0xB0 + i + m_row); //set page address + i2c_command(m_col & 0xf); //set lower column address + i2c_command(0x10 | (m_col >> 4)); //set higher column address - for( j = 0; j < 8; j++){ - Wire.beginTransmission(i2caddr); - Wire.write(0x40); + for ( j = 0; j < 8; j++) { + wire->beginTransmission(i2caddr); + wire->write(0x40); for ( k = 0; k < xs; k++, p++) { - Wire.write(buffer[p]); + wire->write(buffer[p]); } - Wire.endTransmission(); - } + wire->endTransmission(); } } +#endif + } + + } void uDisplay::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { + if (ep_mode) { + drawFastVLine_EPD(x, y, h, color); + return; + } + if (interface != _UDSP_SPI) { Renderer::drawFastVLine(x, y, h, color); return; @@ -528,6 +685,12 @@ void uDisplay::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { void uDisplay::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { + + if (ep_mode) { + drawFastHLine_EPD(x, y, w, color); + return; + } + if (interface != _UDSP_SPI) { Renderer::drawFastHLine(x, y, w, color); return; @@ -575,6 +738,12 @@ void uDisplay::fillScreen(uint16_t color) { // fill a rectangle void uDisplay::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { + + if (ep_mode) { + fillRect_EPD(x, y, w, h, color); + return; + } + if (interface != _UDSP_SPI) { Renderer::fillRect(x, y, w, h, color); return; @@ -646,6 +815,9 @@ for(y=h; y>0; y--) { void uDisplay::Splash(void) { + if (ep_mode) { + delay(lut3time * 10); + } setTextFont(splash_font); setTextSize(splash_size); DrawStringAt(splash_xp, splash_yp, dname, fg_col, 0); @@ -670,7 +842,7 @@ void uDisplay::setAddrWindow_int(uint16_t x, uint16_t y, uint16_t w, uint16_t h) x += x_addr_offs[cur_rot]; y += y_addr_offs[cur_rot]; - if (sa_mode == 16) { + if (sa_mode != 8) { uint32_t xa = ((uint32_t)x << 16) | (x+w-1); uint32_t ya = ((uint32_t)y << 16) | (y+h-1); @@ -735,11 +907,19 @@ void uDisplay::WriteColor(uint16_t color) { void uDisplay::drawPixel(int16_t x, int16_t y, uint16_t color) { + + if (ep_mode) { + drawPixel_EPD(x, y, color); + return; + } + if (interface != _UDSP_SPI) { Renderer::drawPixel(x, y, color); return; } + + if ((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) return; @@ -763,7 +943,13 @@ void uDisplay::setRotation(uint8_t rotation) { Renderer::setRotation(cur_rot); return; } + if (interface == _UDSP_SPI) { + + if (ep_mode) { + Renderer::setRotation(cur_rot); + return; + } SPI_BEGIN_TRANSACTION SPI_CS_LOW spi_command(madctrl); @@ -801,6 +987,10 @@ void udisp_bpwr(uint8_t on); void uDisplay::DisplayOnff(int8_t on) { + if (ep_mode) { + return; + } + udisp_bpwr(on); if (interface == _UDSP_I2C) { @@ -811,7 +1001,7 @@ void uDisplay::DisplayOnff(int8_t on) { } } else { if (on) { - spi_command_one(dsp_on); + if (dsp_on != 0xff) spi_command_one(dsp_on); if (bpanel >= 0) { #ifdef ESP32 ledcWrite(ESP32_PWM_CHANNEL, dimmer); @@ -821,7 +1011,7 @@ void uDisplay::DisplayOnff(int8_t on) { } } else { - spi_command_one(dsp_off); + if (dsp_off != 0xff) spi_command_one(dsp_off); if (bpanel >= 0) { #ifdef ESP32 ledcWrite(ESP32_PWM_CHANNEL, 0); @@ -834,10 +1024,24 @@ void uDisplay::DisplayOnff(int8_t on) { } void uDisplay::invertDisplay(boolean i) { - if (i) { - spi_command_one(inv_on); - } else { - spi_command_one(inv_off); + + if (ep_mode) { + return; + } + + if (interface == _UDSP_SPI) { + if (i) { + spi_command_one(inv_on); + } else { + spi_command_one(inv_off); + } + } + if (interface == _UDSP_I2C) { + if (i) { + i2c_command(inv_on); + } else { + i2c_command(inv_off); + } } } @@ -845,22 +1049,30 @@ void udisp_dimm(uint8_t dim); void uDisplay::dim(uint8_t dim) { dimmer = dim; - if (dimmer > 15) dimmer = 15; - dimmer = ((float)dimmer / 15.0) * 255.0; -#ifdef ESP32 - if (bpanel >= 0) { - ledcWrite(ESP32_PWM_CHANNEL, dimmer); - } else { - udisp_dimm(dim); + + if (ep_mode) { + return; } + + if (interface == _UDSP_SPI) { + if (dimmer > 15) dimmer = 15; + dimmer = ((float)dimmer / 15.0) * 255.0; +#ifdef ESP32 + if (bpanel >= 0) { + ledcWrite(ESP32_PWM_CHANNEL, dimmer); + } else { + udisp_dimm(dim); + } #endif - if (dim_op != 0xff) { - SPI_BEGIN_TRANSACTION - SPI_CS_LOW - spi_command(dim_op); - spi_data8(dim); - SPI_CS_HIGH - SPI_END_TRANSACTION + + if (dim_op != 0xff) { + SPI_BEGIN_TRANSACTION + SPI_CS_LOW + spi_command(dim_op); + spi_data8(dim); + SPI_CS_HIGH + SPI_END_TRANSACTION + } } } @@ -1033,3 +1245,249 @@ void USECACHE uDisplay::write32(uint32_t val) { GPIO_SET(spi_clk); } } + + +// epaper section + +// EPD2IN9 commands +#define DRIVER_OUTPUT_CONTROL 0x01 +#define BOOSTER_SOFT_START_CONTROL 0x0C +#define GATE_SCAN_START_POSITION 0x0F +#define DEEP_SLEEP_MODE 0x10 +#define DATA_ENTRY_MODE_SETTING 0x11 +#define SW_RESET 0x12 +#define TEMPERATURE_SENSOR_CONTROL 0x1A +#define MASTER_ACTIVATION 0x20 +#define DISPLAY_UPDATE_CONTROL_1 0x21 +#define DISPLAY_UPDATE_CONTROL_2 0x22 +#define WRITE_RAM 0x24 +#define WRITE_VCOM_REGISTER 0x2C +#define WRITE_LUT_REGISTER 0x32 +#define SET_DUMMY_LINE_PERIOD 0x3A +#define SET_GATE_TIME 0x3B +#define BORDER_WAVEFORM_CONTROL 0x3C +#define SET_RAM_X_ADDRESS_START_END_POSITION 0x44 +#define SET_RAM_Y_ADDRESS_START_END_POSITION 0x45 +#define SET_RAM_X_ADDRESS_COUNTER 0x4E +#define SET_RAM_Y_ADDRESS_COUNTER 0x4F +#define TERMINATE_FRAME_READ_WRITE 0xFF + + +void uDisplay::spi_data8_EPD(uint8_t val) { + SPI_BEGIN_TRANSACTION + SPI_CS_LOW + spi_data8(val); + SPI_CS_HIGH + SPI_END_TRANSACTION +} + +void uDisplay::spi_command_EPD(uint8_t val) { + SPI_BEGIN_TRANSACTION + SPI_CS_LOW + spi_command(val); + SPI_CS_HIGH + SPI_END_TRANSACTION +} + +void uDisplay::Init_EPD(int8_t p) { + if (p == DISPLAY_INIT_PARTIAL) { + SetLut(lut_partial); + } else { + SetLut(lut_full); + } + ClearFrameMemory(0xFF); + Updateframe_EPD(); + if (p == DISPLAY_INIT_PARTIAL) { + delay(lutptime * 10); + } else { + delay(lutftime * 10); + } +} + +void uDisplay::ClearFrameMemory(unsigned char color) { + SetMemoryArea(0, 0, gxs - 1, gys - 1); + SetMemoryPointer(0, 0); + spi_command_EPD(WRITE_RAM); + /* send the color data */ + for (int i = 0; i < gxs / 8 * gys; i++) { + spi_data8_EPD(color); + } +} + +void uDisplay::SetLut(const unsigned char* lut) { + spi_command_EPD(WRITE_LUT_REGISTER); + /* the length of look-up table is 30 bytes */ + for (int i = 0; i < lutfsize; i++) { + spi_data8_EPD(lut[i]); + } +} + +void uDisplay::Updateframe_EPD(void) { + SetFrameMemory(buffer, 0, 0, gxs, gys); + DisplayFrame(); +} + +void uDisplay::DisplayFrame(void) { + spi_command_EPD(DISPLAY_UPDATE_CONTROL_2); + spi_data8_EPD(0xC4); + spi_command_EPD(MASTER_ACTIVATION); + spi_data8_EPD(TERMINATE_FRAME_READ_WRITE); +} + +void uDisplay::SetMemoryArea(int x_start, int y_start, int x_end, int y_end) { + spi_command_EPD(SET_RAM_X_ADDRESS_START_END_POSITION); + /* x point must be the multiple of 8 or the last 3 bits will be ignored */ + spi_data8_EPD((x_start >> 3) & 0xFF); + spi_data8_EPD((x_end >> 3) & 0xFF); + spi_command_EPD(SET_RAM_Y_ADDRESS_START_END_POSITION); + spi_data8_EPD(y_start & 0xFF); + spi_data8_EPD((y_start >> 8) & 0xFF); + spi_data8_EPD(y_end & 0xFF); + spi_data8_EPD((y_end >> 8) & 0xFF); +} + +void uDisplay::SetFrameMemory(const unsigned char* image_buffer) { + SetMemoryArea(0, 0, gxs - 1, gys - 1); + SetMemoryPointer(0, 0); + spi_command_EPD(WRITE_RAM); + /* send the image data */ + for (int i = 0; i < gxs / 8 * gys; i++) { + spi_data8_EPD(image_buffer[i] ^ 0xff); + } +} + +void uDisplay::SetMemoryPointer(int x, int y) { + spi_command_EPD(SET_RAM_X_ADDRESS_COUNTER); + /* x point must be the multiple of 8 or the last 3 bits will be ignored */ + spi_data8_EPD((x >> 3) & 0xFF); + spi_command_EPD(SET_RAM_Y_ADDRESS_COUNTER); + spi_data8_EPD(y & 0xFF); + spi_data8_EPD((y >> 8) & 0xFF); +} + +void uDisplay::SetFrameMemory( + const unsigned char* image_buffer, + uint16_t x, + uint16_t y, + uint16_t image_width, + uint16_t image_height +) { + uint16_t x_end; + uint16_t y_end; + + if ( + image_buffer == NULL || + x < 0 || image_width < 0 || + y < 0 || image_height < 0 + ) { + return; + } + + /* x point must be the multiple of 8 or the last 3 bits will be ignored */ + x &= 0xFFF8; + image_width &= 0xFFF8; + if (x + image_width >= gxs) { + x_end = gxs - 1; + } else { + x_end = x + image_width - 1; + } + if (y + image_height >= gys) { + y_end = gys - 1; + } else { + y_end = y + image_height - 1; + } + + if (!x && !y && image_width == gxs && image_height == gys) { + SetFrameMemory(image_buffer); + return; + } + + SetMemoryArea(x, y, x_end, y_end); + SetMemoryPointer(x, y); + spi_command_EPD(WRITE_RAM); + /* send the image data */ + for (uint16_t j = 0; j < y_end - y + 1; j++) { + for (uint16_t i = 0; i < (x_end - x + 1) / 8; i++) { + spi_data8_EPD(image_buffer[i + j * (image_width / 8)]^0xff); + } + } +} + +#define IF_INVERT_COLOR 1 +#define renderer_swap(a, b) { int16_t t = a; a = b; b = t; } +/** + * @brief: this draws a pixel by absolute coordinates. + * this function won't be affected by the rotate parameter. + * we must use this for epaper because these displays have a strange and different bit pattern + */ +void uDisplay::DrawAbsolutePixel(int x, int y, int16_t color) { + + int16_t w = width(), h = height(); + if (cur_rot == 1 || cur_rot == 3) { + renderer_swap(w, h); + } + + if (x < 0 || x >= w || y < 0 || y >= h) { + return; + } + if (IF_INVERT_COLOR) { + if (color) { + buffer[(x + y * w) / 8] |= 0x80 >> (x % 8); + } else { + buffer[(x + y * w) / 8] &= ~(0x80 >> (x % 8)); + } + } else { + if (color) { + buffer[(x + y * w) / 8] &= ~(0x80 >> (x % 8)); + } else { + buffer[(x + y * w) / 8] |= 0x80 >> (x % 8); + } + } +} + +void uDisplay::drawPixel_EPD(int16_t x, int16_t y, uint16_t color) { + if (!buffer) return; + if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) + return; + + // check rotation, move pixel around if necessary + switch (cur_rot) { + case 1: + renderer_swap(x, y); + x = gxs - x - 1; + break; + case 2: + x = gxs - x - 1; + y = gys - y - 1; + break; + case 3: + renderer_swap(x, y); + y = gys - y - 1; + break; + } + + // x is which column + DrawAbsolutePixel(x, y, color); + +} + + +void uDisplay::fillRect_EPD(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { + for (uint32_t yp = y; yp < y + h; yp++) { + for (uint32_t xp = x; xp < x + w; xp++) { + drawPixel_EPD(xp , yp , color); + } + } +} +void uDisplay::drawFastVLine_EPD(int16_t x, int16_t y, int16_t h, uint16_t color) { + while (h--) { + drawPixel_EPD(x , y , color); + y++; + } +} +void uDisplay::drawFastHLine_EPD(int16_t x, int16_t y, int16_t w, uint16_t color) { + while (w--) { + drawPixel_EPD(x , y , color); + x++; + } +} diff --git a/lib/lib_display/UDisplay/uDisplay.h b/lib/lib_display/UDisplay/uDisplay.h index f3bc1b277..793b1cc8f 100644 --- a/lib/lib_display/UDisplay/uDisplay.h +++ b/lib/lib_display/UDisplay/uDisplay.h @@ -3,6 +3,8 @@ #include #include +#include +#include #define _UDSP_I2C 1 #define _UDSP_SPI 2 @@ -10,6 +12,10 @@ #define UDISP1_WHITE 1 #define UDISP1_BLACK 0 +#define DISPLAY_INIT_MODE 0 +#define DISPLAY_INIT_PARTIAL 1 +#define DISPLAY_INIT_FULL 2 + enum uColorType { uCOLOR_BW, uCOLOR_COLOR }; // Color definitions @@ -93,7 +99,22 @@ class uDisplay : public Renderer { void write32(uint32_t val); void spi_data9(uint8_t d, uint8_t dc); void WriteColor(uint16_t color); - + void SetLut(const unsigned char* lut); + void DisplayFrame(void); + void Updateframe_EPD(); + void SetFrameMemory(const unsigned char* image_buffer); + void SetFrameMemory(const unsigned char* image_buffer, uint16_t x, uint16_t y, uint16_t image_width, uint16_t image_height); + void SetMemoryArea(int x_start, int y_start, int x_end, int y_end); + void SetMemoryPointer(int x, int y); + void DrawAbsolutePixel(int x, int y, int16_t color); + void drawPixel_EPD(int16_t x, int16_t y, uint16_t color); + void fillRect_EPD(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); + void drawFastVLine_EPD(int16_t x, int16_t y, int16_t h, uint16_t color); + void drawFastHLine_EPD(int16_t x, int16_t y, int16_t w, uint16_t color); + void Init_EPD(int8_t p); + void spi_command_EPD(uint8_t val); + void spi_data8_EPD(uint8_t val); + void ClearFrameMemory(unsigned char color); uint8_t strlen_ln(char *str); int32_t next_val(char **sp); uint32_t next_hex(char **sp); @@ -103,7 +124,12 @@ class uDisplay : public Renderer { uint8_t interface; uint8_t i2caddr; int8_t i2c_scl; + TwoWire *wire; int8_t i2c_sda; + uint8_t i2c_col_start; + uint8_t i2c_col_end; + uint8_t i2c_page_start; + uint8_t i2c_page_end; int8_t reset; uint8_t dsp_cmds[128]; uint8_t dsp_ncmds; @@ -144,6 +170,14 @@ class uDisplay : public Renderer { uint8_t inv_off; uint8_t sa_mode; uint8_t dim_op; + uint8_t lutfsize; + uint8_t lutpsize; + uint16_t lutftime; + uint16_t lutptime; + uint16_t lut3time; + uint8_t ep_mode; + uint8_t lut_full[64]; + uint8_t lut_partial[64]; }; diff --git a/tasmota/displaydesc/ILI9341_desc.txt b/tasmota/displaydesc/ILI9341_desc.txt index c9f6e8964..ccc37bc15 100644 --- a/tasmota/displaydesc/ILI9341_desc.txt +++ b/tasmota/displaydesc/ILI9341_desc.txt @@ -25,10 +25,11 @@ E1,0F,00,0E,14,03,11,07,31,C1,48,08,0F,0C,31,36,0F 29,80 :o,28 :O,29 -:A,2A,2B,2C +:A,2A,2B,2C,16 :R,36 :0,48,00,00,00 :1,28,00,00,01 :2,88,00,00,02 -:3,E8,00,00,02 +:3,E8,00,00,03 +:i,20,21 # diff --git a/tasmota/displaydesc/ILI9342_desc.txt b/tasmota/displaydesc/ILI9342_desc.txt index 0f2b32e35..e072d572a 100644 --- a/tasmota/displaydesc/ILI9342_desc.txt +++ b/tasmota/displaydesc/ILI9342_desc.txt @@ -26,11 +26,12 @@ E1,0F,00,0E,14,03,11,07,31,C1,48,08,0F,0C,31,36,0F 29,80 :o,28 :O,29 -:A,2A,2B,2C +:A,2A,2B,2C,16 :R,36 :0,08,00,00,00 :1,A8,00,00,01 :2,C8,00,00,02 :3,68,00,00,03 +:i,20,21 :TI2,38,22,21 # diff --git a/tasmota/displaydesc/ILI9488_desc.txt b/tasmota/displaydesc/ILI9488_desc.txt index 1aac15cd2..36485fc18 100644 --- a/tasmota/displaydesc/ILI9488_desc.txt +++ b/tasmota/displaydesc/ILI9488_desc.txt @@ -1,4 +1,4 @@ -:H,ILI9488,480,320,16,SPI,1,*,*,*,*,*,*,-1,10 +:H,ILI9488,480,320,16,SPI,1,*,*,*,*,*,*,*,10 :S,2,1,1,0,40,20 :I E0,0F,00,03,09,08,16,0A,3F,78,4C,09,0A,08,16,1A,0F @@ -18,7 +18,7 @@ F7,4,A9,51,2C,82 29,0 :o,28 :O,29 -:A,2A,2B,2C +:A,2A,2B,2C,16 :R,36 ;:0,48,00,00,00 :0,28,00,00,01 @@ -26,5 +26,6 @@ F7,4,A9,51,2C,82 :2,E8,00,00,03 :3,88,00,00,02 :P,18 -:TI1,38,4,5 +:i,20,21 +:TI1,38,*,* # diff --git a/tasmota/displaydesc/SD1306_desc.txt b/tasmota/displaydesc/SD1306_desc.txt new file mode 100644 index 000000000..15bafcac1 --- /dev/null +++ b/tasmota/displaydesc/SD1306_desc.txt @@ -0,0 +1,24 @@ +:H,SD1306,128,64,1,I2C,3c,*,*,* +:S,0,2,1,0,30,20 +:I +AE +D5,80 +A8,3f +D3,00 +40 +8D,14 +20,00 +A1 +C8 +DA,12 +81,9F +D9F1 +DB,40 +A4 +A6 +AF +:o,AE +:O,AF +:A,00,10,40,00,00 +:i,A6,A7 +# diff --git a/tasmota/displaydesc/SH1106_desc.txt b/tasmota/displaydesc/SH1106_desc.txt index 2d3f76fdf..81948549e 100644 --- a/tasmota/displaydesc/SH1106_desc.txt +++ b/tasmota/displaydesc/SH1106_desc.txt @@ -19,5 +19,6 @@ A6 AF :o,AE :O,AF -:A,00,10,40 +:A,00,10,40,00,02 +:i,A6,A7 # diff --git a/tasmota/displaydesc/SSD1351_desc.txt b/tasmota/displaydesc/SSD1351_desc.txt index 955ffda19..806c8c7a8 100644 --- a/tasmota/displaydesc/SSD1351_desc.txt +++ b/tasmota/displaydesc/SSD1351_desc.txt @@ -1,4 +1,4 @@ -:H,SSD1351,128,128,16,SPI,1,*,*,*,-1,-1,-1,-1,10 +:H,SSD1351,128,128,16,SPI,1,*,*,*,*,*,*,*,10 :S,1,1,1,0,40,10 :I FD,1,12 diff --git a/tasmota/displaydesc/ST7789_desc.txt b/tasmota/displaydesc/ST7789_desc.txt index fcdbd389c..6b21397ac 100644 --- a/tasmota/displaydesc/ST7789_desc.txt +++ b/tasmota/displaydesc/ST7789_desc.txt @@ -1,5 +1,5 @@ -:H,ST7789,240,240,16,SPI,1,*,*,*,*,*,-1,-1,40 +:H,ST7789,240,240,16,SPI,1,*,*,*,*,*,*,*,40 :S,2,1,3,0,80,30 :I 01,A0 @@ -17,5 +17,6 @@ :1,A0,50,00,01 :2,00,00,00,02 :3,60,00,00,03 +i:20,21 :TI2,38,32,23 # diff --git a/tasmota/displaydesc/WS_epaper29_desc.txt b/tasmota/displaydesc/WS_epaper29_desc.txt new file mode 100644 index 000000000..6a8b455fb --- /dev/null +++ b/tasmota/displaydesc/WS_epaper29_desc.txt @@ -0,0 +1,17 @@ +H,E-PAPER-29,128,296,1,SPI,1,*,*,*,*,*,*,*,10 +:S,1,1,1,0,10,10 +:I +01,3,27,01,00 +0C,3,D7,D6,9D +2C,1,A8 +3A,1,1A +3B,1,08 +11,1,03 +:L +02,02,01,11,12,12,22,22,66,69,69,59,58,99,99 +88,00,00,00,00,F8,B4,13,51,35,51,51,19,01,00 +:l +10,18,18,08,18,18,08,00,00,00,00,00,00,00,00 +00,00,00,00,00,13,14,44,12,00,00,00,00,00,00 +:T,350,35,10 +# diff --git a/tasmota/displaydesc/readme.md b/tasmota/displaydesc/readme.md new file mode 100644 index 000000000..a9e29555d --- /dev/null +++ b/tasmota/displaydesc/readme.md @@ -0,0 +1,19 @@ +Display Descriptor files for use with universal display driver. +2 (3) options to select a display driver + +1. file system driven if UFILESYSTEM is in place (preferred option for normal use) + to select a display rename the file to "dispdesc.txt" and put into flash file system. +2. scripter driven as a special section >d in scripter. + copy the file to a script section >d and place a ->displayreinit cmd into the >B section + (preferred for developing or modifying display driver) +3. rule buffer 3 driven + copy descriptor to rule buffer number 3 but do not enable rule 3 + (descriptor may not contain ANY spaces in this mode) +(4.) compile the descriptor into driver. + convert the file to a const char array and place into source xdsp_universal.ino + and replace the example array there + #define DSP_ROM_DESC + +for further info about display descriptors read the tasmota docs display part. + +the current files define the standard resolutions. if you change the resolution settings +sometimes you also have to change some register values in the init or address map section diff --git a/tasmota/xdsp_17_universal.ino b/tasmota/xdsp_17_universal.ino index 419a94d12..b91cc0550 100644 --- a/tasmota/xdsp_17_universal.ino +++ b/tasmota/xdsp_17_universal.ino @@ -71,7 +71,8 @@ const char DSP_SAMPLE_DESC[] PROGMEM = ":o,AE\n" // switch display on ":O,AF\n" -":A,00,10,40\n" +":A,00,10,40,00,02\n" +":i,A6,A7\n" "#\n"; #endif // DSP_ROM_DESC @@ -117,6 +118,24 @@ char *fbuff; } #endif // USE_SCRIPT +#ifdef USE_RULES + if (!bitRead(Settings.rule_enabled, 2) && !ddesc) { + // only if rule3 is not enabled for rules + char *cp = Settings.rules[2]; + while (*cp == ' ') cp++; + memcpy(fbuff, cp, DISPDESC_SIZE - 1); + if (fbuff[0] == ':' && fbuff[1] == 'H') { + // assume display descriptor, replace space with line feed + for (uint32_t cnt = 0; cnt < DISPDESC_SIZE; cnt++) { + if (fbuff[cnt] == ' ') fbuff[cnt] = '\n'; + } + ddesc = fbuff; + AddLog(LOG_LEVEL_INFO, PSTR("DSP: Rule 3 descriptor used")); + } + + } +#endif // USE_RULES + #ifdef DSP_ROM_DESC if (!ddesc) { @@ -176,17 +195,16 @@ char *fbuff; } } - // init renderer - if (udisp) delete udisp; - udisp = new uDisplay(ddesc); - /* File fp; fp = ffsp->open("/dump.txt", "w"); fp.write((uint8_t*)ddesc, DISPDESC_SIZE); fp.close(); - */ +*/ + // init renderer + if (udisp) delete udisp; + udisp = new uDisplay(ddesc); // checck for touch option TI1 or TI2 #ifdef USE_FT5206