diff --git a/lib/lib_display/Display_Renderer-gemu-1.0/src/renderer.cpp b/lib/lib_display/Display_Renderer-gemu-1.0/src/renderer.cpp index bcef703a6..7ea9aa0e6 100644 --- a/lib/lib_display/Display_Renderer-gemu-1.0/src/renderer.cpp +++ b/lib/lib_display/Display_Renderer-gemu-1.0/src/renderer.cpp @@ -86,6 +86,10 @@ void Renderer::Begin(int16_t p1,int16_t p2,int16_t p3) { } +void Renderer::Sleep(void) { + +} + void Renderer::Updateframe() { } diff --git a/lib/lib_display/Display_Renderer-gemu-1.0/src/renderer.h b/lib/lib_display/Display_Renderer-gemu-1.0/src/renderer.h index 36196b962..279ab514d 100644 --- a/lib/lib_display/Display_Renderer-gemu-1.0/src/renderer.h +++ b/lib/lib_display/Display_Renderer-gemu-1.0/src/renderer.h @@ -35,7 +35,7 @@ typedef struct LVGL_PARAMS { uint8_t use_dma : 1; uint8_t swap_color : 1; uint8_t async_dma : 1; // force DMA completion before returning, avoid conflict with other devices on same bus. If set you should make sure the display is the only device on the bus - uint8_t resvd_1 : 1; + uint8_t busy_invert : 1; uint8_t resvd_2 : 1; uint8_t resvd_3 : 1; uint8_t resvd_4 : 1; @@ -86,6 +86,7 @@ public: virtual uint16_t bgcol(void); virtual int8_t color_type(void); virtual void Splash(void); + virtual void Sleep(void); virtual char *devname(void); virtual LVGL_PARAMS *lvgl_pars(void); virtual void ep_update_mode(uint8_t mode); diff --git a/lib/lib_display/Epaper_29-gemu-1.0/epd2in9.cpp b/lib/lib_display/Epaper_29-gemu-1.0/epd2in9.cpp index 96b83645e..9f6fe4f74 100644 --- a/lib/lib_display/Epaper_29-gemu-1.0/epd2in9.cpp +++ b/lib/lib_display/Epaper_29-gemu-1.0/epd2in9.cpp @@ -30,6 +30,9 @@ #define EPD_29_V2 +//#define BUSY_PIN 16 + + Epd::Epd(int16_t width, int16_t height) : Paint(width,height) { } @@ -38,30 +41,35 @@ void Epd::DisplayOnff(int8_t on) { } void Epd::Updateframe() { +#ifdef EPD_29_V2 + if (mode == DISPLAY_INIT_PARTIAL) { + SetFrameMemory_Partial(framebuffer, 0, 0, EPD_WIDTH,EPD_HEIGHT); + DisplayFrame_Partial(); + } else { + SetFrameMemory(framebuffer, 0, 0, EPD_WIDTH,EPD_HEIGHT); + DisplayFrame(); + } +#else SetFrameMemory(framebuffer, 0, 0, EPD_WIDTH,EPD_HEIGHT); DisplayFrame(); +#endif //Serial.printf("update\n"); } -#define DISPLAY_INIT_MODE 0 -#define DISPLAY_INIT_PARTIAL 1 -#define DISPLAY_INIT_FULL 2 - - void Epd::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) { // ignore update mode - if (p==DISPLAY_INIT_PARTIAL) { + if (p == DISPLAY_INIT_PARTIAL) { Init(lut_partial_update); //ClearFrameMemory(0xFF); // bit set = white, bit reset = black DisplayFrame(); - delay(500); + delay_busy(500); return; //Serial.printf("partial\n"); - } else if (p==DISPLAY_INIT_FULL) { + } else if (p == DISPLAY_INIT_FULL) { Init(lut_full_update); //ClearFrameMemory(0xFF); // bit set = white, bit reset = black DisplayFrame(); - delay(3500); + delay_busy(3500); //Serial.printf("full\n"); return; } else { @@ -80,25 +88,31 @@ void Epd::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) { disp_bpp = 1; } -void Epd::Begin(int16_t cs,int16_t mosi,int16_t sclk) { - cs_pin=cs; - mosi_pin=mosi; - sclk_pin=sclk; +void Epd::Begin(int16_t cs,int16_t mosi,int16_t sclk, int16_t rst, int16_t busy) { + cs_pin = cs; + mosi_pin = mosi; + sclk_pin = sclk; + rst_pin = rst; + busy_pin = busy; +#ifdef BUSY_PIN + busy_pin = BUSY_PIN; +#endif } void Epd::Init(int8_t p) { - if (p==DISPLAY_INIT_PARTIAL) { + if (p == DISPLAY_INIT_PARTIAL) { Init(lut_partial_update); } else { Init(lut_full_update); } + mode = p; ClearFrameMemory(0xFF); DisplayFrame(); - if (p==DISPLAY_INIT_PARTIAL) { - delay(350); + if (p == DISPLAY_INIT_PARTIAL) { + delay_busy(350); } else { - delay(3500); + delay_busy(3500); } } @@ -114,6 +128,9 @@ int Epd::Init(const unsigned char* lut) { sclk_pin=pin[GPIO_SSPI_SCLK]; */ + if (framebuffer) { + // free(framebuffer); + } framebuffer = (uint8_t*)malloc(EPD_WIDTH * EPD_HEIGHT / 8); if (!framebuffer) return -1; @@ -125,14 +142,24 @@ int Epd::Init(const unsigned char* lut) { digitalWrite(mosi_pin,LOW); digitalWrite(sclk_pin,LOW); + if (rst_pin >= 0) { + pinMode(rst_pin, OUTPUT); + digitalWrite(rst_pin, HIGH); + } + + if (busy_pin >= 0) { + pinMode(busy_pin, INPUT_PULLUP); + } + width = EPD_WIDTH; height = EPD_HEIGHT; #ifdef EPD_29_V2 /* EPD hardware init start */ - WaitUntilIdle(); + Reset(); + SendCommand(0x12); //SWRESET - WaitUntilIdle(); + delay_busy(100); SendCommand(0x01); //Driver output control SendData(0x27); @@ -149,9 +176,10 @@ int Epd::Init(const unsigned char* lut) { SendData(0x80); SetMemoryPointer(0, 0); - WaitUntilIdle(); + delay_busy(10); SetLut_by_host(lut_full_update); + mode = DISPLAY_INIT_FULL; #else /* EPD hardware init start */ @@ -197,31 +225,33 @@ void Epd::SendData(unsigned char data) { // SpiTransfer(data); } -/** - * @brief: Wait until the busy_pin goes LOW - */ -void Epd::WaitUntilIdle(void) { -#ifdef EPD_29_V2 - delay(100); -#endif - return; - //while(DigitalRead(busy_pin) == HIGH) { //LOW: idle, HIGH: busy - // DelayMs(100); - //} +void Epd::delay_busy(uint32_t wait) { + if (busy_pin >= 0) { + while (digitalRead(busy_pin) == HIGH) { //LOW: idle, HIGH: busy + delay(10); + } + } else { + delay(wait); + } } + /** * @brief: module reset. * often used to awaken the module in deep sleep, * see Epd::Sleep(); */ void Epd::Reset(void) { - //DigitalWrite(reset_pin, LOW); //module reset - //delay(200); - //DigitalWrite(reset_pin, HIGH); - //delay(200); + if (rst_pin >= 0) { + digitalWrite(rst_pin, LOW); //module reset + delay(200); + digitalWrite(rst_pin, HIGH); + delay(200); + } else { + SendCommand(0x12); + } } #ifdef EPD_29_V2 @@ -230,7 +260,7 @@ void Epd::SetLut(const unsigned char *lut) { SendCommand(0x32); for(count=0; count<153; count++) SendData(lut[count]); - WaitUntilIdle(); + delay_busy(50); } @@ -276,6 +306,7 @@ void Epd::SetFrameMemory( uint16_t image_width, uint16_t image_height ) { + uint16_t x_end; uint16_t y_end; @@ -366,15 +397,94 @@ void Epd::ClearFrameMemory(unsigned char color) { * set the other memory area. */ void Epd::DisplayFrame(void) { - SendCommand(DISPLAY_UPDATE_CONTROL_2); + SendCommand(DISPLAY_UPDATE_CONTROL_2); // 0x22 +#ifdef EPD_29_V2 + SendData(0xC7); +#else SendData(0xC4); - SendCommand(MASTER_ACTIVATION); +#endif + SendCommand(MASTER_ACTIVATION); // 0x20 +#ifndef EPD_29_V2 SendCommand(TERMINATE_FRAME_READ_WRITE); - WaitUntilIdle(); +#endif + delay_busy(10); +} + +void Epd::DisplayFrame_Partial(void) { + SendCommand(0x22); + SendData(0x0F); + SendCommand(0x20); + delay_busy(10); +} + +#ifdef EPD_29_V2 + +void Epd::SetFrameMemory_Partial(const unsigned char* image_buffer, int x, int y, int image_width, int image_height) { + int x_end; + int 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 &= 0xF8; + image_width &= 0xF8; + if (x + image_width >= this->width) { + x_end = this->width - 1; + } else { + x_end = x + image_width - 1; + } + if (y + image_height >= this->height) { + y_end = this->height - 1; + } else { + y_end = y + image_height - 1; + } + + if (rst_pin >= 0) { + digitalWrite(rst_pin, LOW); + delay(2); + digitalWrite(rst_pin, HIGH); + delay(2); + } else { + SendCommand(0x12); + } + SetLut(lut_partial_update); + SendCommand(0x37); + SendData(0x00); + SendData(0x00); + SendData(0x00); + SendData(0x00); + SendData(0x00); + SendData(0x40); + SendData(0x00); + SendData(0x00); + SendData(0x00); + SendData(0x00); + + SendCommand(0x3C); //BorderWavefrom + SendData(0x80); + + SendCommand(0x22); + SendData(0xC0); + SendCommand(0x20); + delay_busy(100); + + SetMemoryArea(x, y, x_end, y_end); + SetMemoryPointer(x, y); + SendCommand(0x24); + /* send the image data */ + for (int j = 0; j < y_end - y + 1; j++) { + for (int i = 0; i < (x_end - x + 1) / 8; i++) { + SendData(image_buffer[i + j * (image_width / 8)]^0xff); + } + } } -#ifdef EPD_29_V2 /** * @brief: private function to specify the memory area for data R/W */ @@ -390,6 +500,15 @@ void Epd::SetMemoryArea(int x_start, int y_start, int x_end, int y_end) { SendData((y_end >> 8) & 0xFF); } #else + +void Epd::SetFrameMemory_Partial( + const unsigned char* image_buffer, + int x, + int y, + int image_width, + int image_height +) { +} /** * @brief: private function to specify the memory area for data R/W */ @@ -419,7 +538,7 @@ void Epd::SetMemoryPointer(int x, int y) { SendCommand(0x4F); SendData(y & 0xFF); SendData((y >> 8) & 0xFF); - WaitUntilIdle(); + delay_busy(10); } #else /** @@ -432,7 +551,7 @@ void Epd::SetMemoryPointer(int x, int y) { SendCommand(SET_RAM_Y_ADDRESS_COUNTER); SendData(y & 0xFF); SendData((y >> 8) & 0xFF); - WaitUntilIdle(); + delay_busy(10); } #endif @@ -444,7 +563,7 @@ void Epd::SetMemoryPointer(int x, int y) { */ void Epd::Sleep() { SendCommand(DEEP_SLEEP_MODE); - WaitUntilIdle(); + delay_busy(10); } #ifdef EPD_29_V2 diff --git a/lib/lib_display/Epaper_29-gemu-1.0/epd2in9.h b/lib/lib_display/Epaper_29-gemu-1.0/epd2in9.h index 3be3071ec..1fdba92fd 100644 --- a/lib/lib_display/Epaper_29-gemu-1.0/epd2in9.h +++ b/lib/lib_display/Epaper_29-gemu-1.0/epd2in9.h @@ -30,6 +30,10 @@ #include "epdpaint.h" +#define DISPLAY_INIT_MODE 0 +#define DISPLAY_INIT_PARTIAL 1 +#define DISPLAY_INIT_FULL 2 + // Display resolution #define EPD_WIDTH 128 #define EPD_HEIGHT 296 @@ -91,23 +95,27 @@ public: void DisplayOnff(int8_t on); void DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font); - void Begin(int16_t p1,int16_t p2,int16_t p3); + void Begin(int16_t cs,int16_t mosi,int16_t sclk, int16_t rst = -1, int16_t busy = -1); + void Updateframe(); private: unsigned int reset_pin; unsigned int dc_pin; - unsigned int busy_pin; const unsigned char* lut; unsigned int cs_pin; + signed int rst_pin; + signed int busy_pin; unsigned int mosi_pin; unsigned int sclk_pin; - + unsigned char mode; + void delay_busy(uint32_t wait); void SetLut(const unsigned char* lut); void SetMemoryArea(int x_start, int y_start, int x_end, int y_end); void SetMemoryPointer(int x, int y); void SetLut_by_host(const unsigned char* lut); - + void SetFrameMemory_Partial(const unsigned char* image_buffer,int x,int y,int image_width,int image_height); + void DisplayFrame_Partial(void); //void fastSPIwrite(uint8_t d,uint8_t dc); }; diff --git a/lib/lib_display/UDisplay/uDisplay.cpp b/lib/lib_display/UDisplay/uDisplay.cpp index d9e2b5310..bba40257a 100755 --- a/lib/lib_display/UDisplay/uDisplay.cpp +++ b/lib/lib_display/UDisplay/uDisplay.cpp @@ -66,6 +66,18 @@ uDisplay::~uDisplay(void) { if (_i80_bus) { esp_lcd_del_i80_bus(_i80_bus); } + + if (lut_full) { + free(lut_full); + } + if (lut_partial) { + free(lut_partial); + } + for (uint16_t cnt = 0; cnt < MAX_LUTS; cnt++ ) { + if (lut_array[cnt]) { + free(lut_array[cnt]); + } + } #endif // USE_ESP32_S3 } @@ -86,6 +98,7 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { lutptime = 35; lutftime = 350; lut3time = 10; + busy_pin = -1; ep_mode = 0; fg_col = 1; bg_col = 0; @@ -96,6 +109,8 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { startline = 0xA1; uint8_t section = 0; dsp_ncmds = 0; + epc_part_cnt = 0; + epc_full_cnt = 0; lut_num = 0; lvgl_param.data = 0; lvgl_param.fluslines = 40; @@ -103,8 +118,10 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { rot_t[1] = 1; rot_t[2] = 2; rot_t[3] = 3; + epcoffs_full = 0; + epcoffs_part = 0; - for (uint32_t cnt = 0; cnt < 5; cnt++) { + for (uint32_t cnt = 0; cnt < MAX_LUTS; cnt++) { lut_cnt[cnt] = 0; lut_cmd[cnt] = 0xff; } @@ -135,9 +152,22 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { } else if (section == 'L') { if (*lp1 >= '1' && *lp1 <= '5') { lut_num = (*lp1 & 0x07); - lp1+=2; + lp1 += 2; + lut_siz[lut_num - 1] = next_val(&lp1); + lut_array[lut_num - 1] = (uint8_t*)malloc(lut_siz[lut_num - 1]); lut_cmd[lut_num - 1] = next_hex(&lp1); + } else { + lut_num = 0; + lp1++; + lut_siz_full = next_val(&lp1); + lut_full = (uint8_t*)malloc(lut_siz_full); + lut_cmd[0] = next_hex(&lp1); } + } else if (section == 'l') { + lp1++; + lut_siz_partial = next_val(&lp1); + lut_partial = (uint8_t*)malloc(lut_siz_partial); + lut_cmd[0] = next_hex(&lp1); } if (*lp1 == ',') lp1++; } @@ -182,7 +212,6 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { reset = next_val(&lp1); spi_miso = next_val(&lp1); spi_speed = next_val(&lp1); - section = 0; } else if (!strncmp(ibuff, "PAR", 3)) { #ifdef USE_ESP32_S3 @@ -256,13 +285,42 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { } } else { while (1) { + if (dsp_ncmds >= sizeof(dsp_cmds)) break; if (!str2c(&lp1, ibuff, sizeof(ibuff))) { dsp_cmds[dsp_ncmds++] = strtol(ibuff, 0, 16); } else { break; } - if (dsp_ncmds >= sizeof(dsp_cmds)) break; - + } + } + break; + case 'f': + // epaper full update cmds + if (!epcoffs_full) { + epcoffs_full = dsp_ncmds; + epc_full_cnt = 0; + } + while (1) { + if (epc_full_cnt >= sizeof(dsp_cmds)) break; + if (!str2c(&lp1, ibuff, sizeof(ibuff))) { + dsp_cmds[epcoffs_full + epc_full_cnt++] = strtol(ibuff, 0, 16); + } else { + break; + } + } + break; + case 'p': + // epaper partial update cmds + if (!epcoffs_part) { + epcoffs_part = dsp_ncmds + epc_full_cnt; + epc_part_cnt = 0; + } + while (1) { + if (epc_part_cnt >= sizeof(dsp_cmds)) break; + if (!str2c(&lp1, ibuff, sizeof(ibuff))) { + dsp_cmds[epcoffs_part + epc_part_cnt++] = strtol(ibuff, 0, 16); + } else { + break; } } break; @@ -338,6 +396,11 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { sa_mode = next_val(&lp1); } break; + case 'a': + saw_1 = next_hex(&lp1); + saw_2 = next_hex(&lp1); + saw_3 = next_hex(&lp1); + break; case 'P': col_mode = next_val(&lp1); break; @@ -350,34 +413,43 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { break; case 'L': if (!lut_num) { + if (!lut_full) { + break; + } while (1) { if (!str2c(&lp1, ibuff, sizeof(ibuff))) { lut_full[lutfsize++] = strtol(ibuff, 0, 16); } else { break; } - if (lutfsize >= LUTMAXSIZE) break; + if (lutfsize >= lut_siz_full) break; } } else { uint8_t index = lut_num - 1; + if (!lut_array[index]) { + break; + } while (1) { if (!str2c(&lp1, ibuff, sizeof(ibuff))) { - lut_array[lut_cnt[index]++][index] = strtol(ibuff, 0, 16); + lut_array[index][lut_cnt[index]++] = strtol(ibuff, 0, 16); } else { break; } - if (lut_cnt[index] >= LUTMAXSIZE) break; + if (lut_cnt[index] >= lut_siz[index]) break; } } break; case 'l': + if (!lut_partial) { + break; + } while (1) { if (!str2c(&lp1, ibuff, sizeof(ibuff))) { lut_partial[lutpsize++] = strtol(ibuff, 0, 16); } else { break; } - if (lutpsize >= LUTMAXSIZE) break; + if (lutpsize >= lut_siz_partial) break; } break; case 'T': @@ -452,8 +524,8 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { Serial.printf("Rot 0: %x,%x - %d - %d\n", madctrl, rot[0], x_addr_offs[0], y_addr_offs[0]); if (ep_mode == 1) { - Serial.printf("LUT_Partial : %d\n", lutpsize); - Serial.printf("LUT_Full : %d\n", lutfsize); + Serial.printf("LUT_Partial : %d - %d - %x - %d - %d\n", lut_siz_partial, lutpsize, lut_cmd[0], epcoffs_part, epc_part_cnt); + Serial.printf("LUT_Full : %d - %d - %x - %d - %d\n", lut_siz_full, lutfsize, lut_cmd[0], epcoffs_full, epc_full_cnt); } if (ep_mode == 2) { Serial.printf("LUT_SIZE 1: %d\n", lut_cnt[0]); @@ -533,11 +605,144 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { } #endif +#ifdef UDSP_DEBUG + Serial.printf("Dsp class init complete\n"); +#endif +} + +void uDisplay::delay_arg(uint32_t args) { + uint32_t delay_ms = 0; + switch (args & 0xE0) { + case 0x80: delay_ms = 150; break; + case 0xA0: delay_ms = 10; break; + case 0xE0: delay_ms = 500; break; + } + if (delay_ms > 0) { + delay(delay_ms); +#ifdef UDSP_DEBUG + Serial.printf("delay %d ms\n", delay_ms); +#endif + } +} + +// epaper pseudo opcodes +#define EP_RESET 0x60 +#define EP_LUT_FULL 0x61 +#define EP_LUT_PARTIAL 0x62 +#define EP_WAITIDLE 0x63 +#define EP_SET_MEM_AREA 0x64 +#define EP_SET_MEM_PTR 0x65 +#define EP_SEND_DATA 0x66 +#define EP_CLR_FRAME 0x67 +#define EP_SEND_FRAME 0x68 + +void uDisplay::send_spi_cmds(uint16_t cmd_offset, uint16_t cmd_size) { +uint16_t index = 0; +#ifdef UDSP_DEBUG + Serial.printf("start send cmd table\n"); +#endif + while (1) { + uint8_t iob; + SPI_CS_LOW + iob = dsp_cmds[cmd_offset++]; + index++; + if (ep_mode == 1 && iob >= EP_RESET) { + // epaper pseudo opcodes + uint8_t args = dsp_cmds[cmd_offset++]; + index++; +#ifdef UDSP_DEBUG + Serial.printf("cmd, args %02x, %d ", iob, args & 0x1f); +#endif + switch (iob) { + case EP_RESET: + if (args & 1) { + iob = dsp_cmds[cmd_offset++]; + index++; + } + reset_pin(iob, iob); + break; + case EP_LUT_FULL: + SetLut(lut_full); + ep_update_mode = DISPLAY_INIT_FULL; + break; + case EP_LUT_PARTIAL: + SetLut(lut_partial); + ep_update_mode = DISPLAY_INIT_PARTIAL; + break; + case EP_WAITIDLE: + if (args & 1) { + iob = dsp_cmds[cmd_offset++]; + index++; + } + //delay(iob * 10); + delay_sync(iob * 10); + break; + case EP_SET_MEM_AREA: + SetMemoryArea(0, 0, gxs - 1, gys - 1); + break; + case EP_SET_MEM_PTR: + SetMemoryPointer(0, 0); + break; + case EP_SEND_DATA: + Send_EP_Data(); + break; + case EP_CLR_FRAME: + ClearFrameMemory(0xFF); + break; + case EP_SEND_FRAME: + SetFrameMemory(framebuffer); + break; + } +#ifdef UDSP_DEBUG + if (args & 1) { + Serial.printf("%02x ", iob ); + } + Serial.printf("\n"); +#endif + if (args & 0x80) { // delay after the command + delay_arg(args); + } + } else { + ulcd_command(iob); + uint8_t args = dsp_cmds[cmd_offset++]; + index++; +#ifdef UDSP_DEBUG + Serial.printf("cmd, args %02x, %d ", iob, args & 0x1f); +#endif + for (uint32_t cnt = 0; cnt < (args & 0x1f); cnt++) { + iob = dsp_cmds[cmd_offset++]; + index++; +#ifdef UDSP_DEBUG + Serial.printf("%02x ", iob ); +#endif + if (!allcmd_mode) { + ulcd_data8(iob); + } else { + ulcd_command(iob); + } + } + SPI_CS_HIGH +#ifdef UDSP_DEBUG + Serial.printf("\n"); +#endif + if (args & 0x80) { // delay after the command + delay_arg(args); + } + } + if (index >= cmd_size) break; + } +#ifdef UDSP_DEBUG + Serial.printf("end send cmd table\n"); +#endif } Renderer *uDisplay::Init(void) { extern bool UsePSRAM(void); + #ifdef UDSP_DEBUG + Serial.printf("Dsp Init 1 start \n"); + #endif + // for any bpp below native 16 bits, we allocate a local framebuffer to copy into if (ep_mode || bpp < 16) { if (framebuffer) free(framebuffer); @@ -549,10 +754,9 @@ Renderer *uDisplay::Init(void) { } else { framebuffer = (uint8_t*)calloc((gxs * gys * bpp) / 8, 1); } - #endif +#endif // ESP8266 } - if (interface == _UDSP_I2C) { if (wire_n == 0) { wire = &Wire; @@ -561,7 +765,7 @@ Renderer *uDisplay::Init(void) { if (wire_n == 1) { wire = &Wire1; } -#endif +#endif // ESP32 wire->begin(i2c_sda, i2c_scl); // TODO: aren't I2C buses already initialized? Shouldn't this be moved to display driver? #ifdef UDSP_DEBUG @@ -604,6 +808,10 @@ Renderer *uDisplay::Init(void) { digitalWrite(spi_clk, LOW); pinMode(spi_mosi, OUTPUT); digitalWrite(spi_mosi, LOW); + if (spi_miso >= 0) { + pinMode(spi_miso, INPUT_PULLUP); + busy_pin = spi_miso; + } } #endif // ESP8266 @@ -628,6 +836,13 @@ Renderer *uDisplay::Init(void) { digitalWrite(spi_clk, LOW); pinMode(spi_mosi, OUTPUT); digitalWrite(spi_mosi, LOW); + if (spi_miso >= 0) { + busy_pin = spi_miso; + pinMode(spi_miso, INPUT_PULLUP); +#ifdef UDSP_DEBUG + Serial.printf("Dsp busy pin: %d\n", busy_pin); +#endif + } } #endif // ESP32 @@ -639,56 +854,11 @@ Renderer *uDisplay::Init(void) { pinMode(reset, OUTPUT); digitalWrite(reset, HIGH); delay(50); - digitalWrite(reset, LOW); - delay(50); - digitalWrite(reset, HIGH); - delay(200); + reset_pin(50, 200); } - uint16_t index = 0; - while (1) { - uint8_t iob; - SPI_CS_LOW + send_spi_cmds(0, dsp_ncmds); - iob = dsp_cmds[index++]; - ulcd_command(iob); - - uint8_t args = dsp_cmds[index++]; -#ifdef UDSP_DEBUG - Serial.printf("cmd, args %02x, %d ", iob, args&0x1f); -#endif - for (uint32_t cnt = 0; cnt < (args & 0x1f); cnt++) { - iob = dsp_cmds[index++]; -#ifdef UDSP_DEBUG - Serial.printf("%02x ", iob ); -#endif - if (!allcmd_mode) { - ulcd_data8(iob); - } else { - ulcd_command(iob); - } - } - SPI_CS_HIGH -#ifdef UDSP_DEBUG - Serial.printf("\n"); -#endif - if (args & 0x80) { // delay after the command - uint32_t delay_ms = 0; - switch (args & 0xE0) { - case 0x80: delay_ms = 150; break; - case 0xA0: delay_ms = 10; break; - case 0xE0: delay_ms = 500; break; - } - if (delay_ms > 0) { - delay(delay_ms); -#ifdef UDSP_DEBUG - Serial.printf("delay %d ms\n", delay_ms); -#endif - } - - } - if (index >= dsp_ncmds) break; - } SPI_END_TRANSACTION } @@ -795,10 +965,7 @@ Renderer *uDisplay::Init(void) { pinMode(reset, OUTPUT); digitalWrite(reset, HIGH); delay(50); - digitalWrite(reset, LOW); - delay(50); - digitalWrite(reset, HIGH); - delay(200); + reset_pin(50, 200); } esp_lcd_i80_bus_config_t bus_config = { @@ -900,8 +1067,8 @@ Renderer *uDisplay::Init(void) { // must init luts on epaper if (ep_mode) { - Init_EPD(DISPLAY_INIT_FULL); - if (ep_mode == 1) Init_EPD(DISPLAY_INIT_PARTIAL); + if (ep_mode == 2) Init_EPD(DISPLAY_INIT_FULL); + //if (ep_mode == 1) Init_EPD(DISPLAY_INIT_PARTIAL); } #ifdef UDSP_DEBUG @@ -910,18 +1077,23 @@ Renderer *uDisplay::Init(void) { return this; } - - void uDisplay::DisplayInit(int8_t p, int8_t size, int8_t rot, int8_t font) { if (p != DISPLAY_INIT_MODE && ep_mode) { + ep_update_mode = p; if (p == DISPLAY_INIT_PARTIAL) { if (lutpsize) { +#ifdef UDSP_DEBUG + Serial.printf("init partial epaper mode\n"); +#endif SetLut(lut_partial); Updateframe_EPD(); - delay(lutptime * 10); + delay_sync(lutptime * 10); } return; } else if (p == DISPLAY_INIT_FULL) { +#ifdef UDSP_DEBUG + Serial.printf("init full epaper mode\n"); +#endif if (lutfsize) { SetLut(lut_full); Updateframe_EPD(); @@ -930,7 +1102,7 @@ void uDisplay::DisplayInit(int8_t p, int8_t size, int8_t rot, int8_t font) { ClearFrame_42(); DisplayFrame_42(); } - delay(lutftime * 10); + delay_sync(lutftime * 10); return; } } else { @@ -953,6 +1125,36 @@ void uDisplay::DisplayInit(int8_t p, int8_t size, int8_t rot, int8_t font) { } } +void uDisplay::reset_pin(int32_t msl, int32_t msh) { + if (reset > 0) { + digitalWrite(reset, LOW); + delay(msl); + digitalWrite(reset, HIGH); + delay(msh); + } +} + +#define UDSP_BUSY_TIMEOUT 3000 +// epaper sync or delay +void uDisplay::delay_sync(int32_t ms) { + uint8_t busy_level = HIGH; + if (lvgl_param.busy_invert) { + busy_level = LOW; + } + uint32_t time = millis(); + if (busy_pin > 0) { + + while (digitalRead(busy_pin) == busy_level) { + delay(1); + if ((millis() - time) > UDSP_BUSY_TIMEOUT) { + break; + } + } + } else { + delay(ms); + } +} + void uDisplay::ulcd_command(uint8_t val) { @@ -1462,12 +1664,16 @@ void uDisplay::Splash(void) { if (ep_mode) { Updateframe(); - delay(lut3time * 10); + delay_sync(lut3time * 10); } setTextFont(splash_font); setTextSize(splash_size); DrawStringAt(splash_xp, splash_yp, dname, fg_col, 0); Updateframe(); + +#ifdef UDSP_DEBUG + Serial.printf("draw splash\n"); +#endif } void uDisplay::setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { @@ -2116,7 +2322,7 @@ uint32_t uDisplay::str2c(char **sp, char *vp, uint32_t len) { } } } else { - uint8_t slen = strlen(lp); + uint16_t slen = strlen(lp); if (slen) { strlcpy(vp, *sp, len); *sp = lp + slen; @@ -2320,9 +2526,9 @@ void uDisplay::Init_EPD(int8_t p) { ClearFrame_42(); } if (p == DISPLAY_INIT_PARTIAL) { - delay(lutptime * 10); + delay_sync(lutptime * 10); } else { - delay(lutftime * 10); + delay_sync(lutftime * 10); } } @@ -2338,58 +2544,66 @@ void uDisplay::ClearFrameMemory(unsigned char color) { void uDisplay::SetLuts(void) { uint8_t index, count; - for (index = 0; index < 5; index++) { - spi_command_EPD(lut_cmd[index]); //vcom + for (index = 0; index < MAX_LUTS; index++) { + spi_command_EPD(lut_cmd[index]); for (count = 0; count < lut_cnt[index]; count++) { - spi_data8_EPD(lut_array[count][index]); + spi_data8_EPD(lut_array[index][count]); } } } void uDisplay::DisplayFrame_42(void) { - uint16_t Width, Height; - Width = (gxs % 8 == 0) ? (gxs / 8 ): (gxs / 8 + 1); - Height = gys; + + spi_command_EPD(saw_1); + for(int i = 0; i < gxs / 8 * gys; i++) { + spi_data8_EPD(0xFF); + } + delay(2); spi_command_EPD(saw_2); - for (uint16_t j = 0; j < Height; j++) { - for (uint16_t i = 0; i < Width; i++) { - spi_data8_EPD(framebuffer[i + j * Width] ^ 0xff); - } + for(int i = 0; i < gxs / 8 * gys; i++) { + spi_data8_EPD(framebuffer[i]^0xff); } + delay(2); + + SetLuts(); + spi_command_EPD(saw_3); - delay(100); + delay_sync(100); + +#ifdef UDSP_DEBUG Serial.printf("EPD Diplayframe\n"); +#endif } -void uDisplay::ClearFrame_42(void) { - uint16_t Width, Height; - Width = (gxs % 8 == 0)? (gxs / 8 ): (gxs / 8 + 1); - Height = gys; + +void uDisplay::ClearFrame_42(void) { spi_command_EPD(saw_1); - for (uint16_t j = 0; j < Height; j++) { - for (uint16_t i = 0; i < Width; i++) { + for (uint16_t j = 0; j < gys; j++) { + for (uint16_t i = 0; i < gxs; i++) { spi_data8_EPD(0xFF); } } spi_command_EPD(saw_2); - for (uint16_t j = 0; j < Height; j++) { - for (uint16_t i = 0; i < Width; i++) { + for (uint16_t j = 0; j < gys; j++) { + for (uint16_t i = 0; i < gxs; i++) { spi_data8_EPD(0xFF); } } spi_command_EPD(saw_3); - delay(100); + delay_sync(100); +#ifdef UDSP_DEBUG Serial.printf("EPD Clearframe\n"); +#endif } - void uDisplay::SetLut(const unsigned char* lut) { - spi_command_EPD(WRITE_LUT_REGISTER); + //spi_command_EPD(WRITE_LUT_REGISTER); + spi_command_EPD(lut_cmd[0]); /* the length of look-up table is 30 bytes */ for (int i = 0; i < lutfsize; i++) { spi_data8_EPD(lut[i]); @@ -2398,8 +2612,21 @@ void uDisplay::SetLut(const unsigned char* lut) { void uDisplay::Updateframe_EPD(void) { if (ep_mode == 1) { - SetFrameMemory(framebuffer, 0, 0, gxs, gys); - DisplayFrame_29(); + switch (ep_update_mode) { + case DISPLAY_INIT_PARTIAL: + if (epc_part_cnt) { + send_spi_cmds(epcoffs_part, epc_part_cnt); + } + break; + case DISPLAY_INIT_FULL: + if (epc_full_cnt) { + send_spi_cmds(epcoffs_full, epc_full_cnt); + } + break; + default: + SetFrameMemory(framebuffer, 0, 0, gxs, gys); + DisplayFrame_29(); + } } else { DisplayFrame_42(); } @@ -2443,6 +2670,28 @@ void uDisplay::SetMemoryPointer(int x, int y) { spi_data8_EPD((y >> 8) & 0xFF); } +#if 0 +void uDisplay::Send_EP_Data() { + for (int i = 0; i < gys / 8 * gys; i++) { + spi_data8_EPD(framebuffer[i]^0xff); + } +} +#else +void uDisplay::Send_EP_Data() { + uint16_t image_width = gxs & 0xFFF8; + uint16_t x = 0; + uint16_t y = 0; + uint16_t x_end = gxs - 1; + uint16_t y_end = gys - 1; + + 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(framebuffer[i + j * (image_width / 8)]^0xff); + } + } +} +#endif + void uDisplay::SetFrameMemory( const unsigned char* image_buffer, uint16_t x, diff --git a/lib/lib_display/UDisplay/uDisplay.h b/lib/lib_display/UDisplay/uDisplay.h index 25a27bab7..de5a8479c 100755 --- a/lib/lib_display/UDisplay/uDisplay.h +++ b/lib/lib_display/UDisplay/uDisplay.h @@ -48,6 +48,8 @@ static inline void gpio_lo(int_fast8_t pin) { if (pin >= 0) *get_gpio_lo_reg(pin #define UDISP1_WHITE 1 #define UDISP1_BLACK 0 +#define MAX_LUTS 5 + #define DISPLAY_INIT_MODE 0 #define DISPLAY_INIT_PARTIAL 1 #define DISPLAY_INIT_FULL 2 @@ -107,7 +109,6 @@ enum uColorType { uCOLOR_BW, uCOLOR_COLOR }; #define SPI_DC_LOW if (spi_dc >= 0) GPIO_CLR_SLOW(spi_dc); #define SPI_DC_HIGH if (spi_dc >= 0) GPIO_SET_SLOW(spi_dc); -#define LUTMAXSIZE 64 #ifdef USE_ESP32_S3 struct esp_lcd_i80_bus_t { @@ -245,7 +246,7 @@ class uDisplay : public Renderer { uint8_t i2c_page_start; uint8_t i2c_page_end; int8_t reset; - uint8_t dsp_cmds[128]; + uint8_t dsp_cmds[256]; uint8_t dsp_ncmds; uint8_t dsp_on; uint8_t dsp_off; @@ -289,16 +290,27 @@ class uDisplay : public Renderer { uint8_t dim_op; uint8_t lutfsize; uint8_t lutpsize; - uint16_t lutftime; + int16_t lutftime; + int8_t busy_pin; uint16_t lutptime; uint16_t lut3time; uint16_t lut_num; uint8_t ep_mode; - uint8_t lut_full[LUTMAXSIZE]; - uint8_t lut_partial[LUTMAXSIZE]; - uint8_t lut_array[LUTMAXSIZE][5]; - uint8_t lut_cnt[5]; - uint8_t lut_cmd[5]; + uint8_t ep_update_mode; + uint8_t *lut_full; + uint8_t lut_siz_full; + uint8_t *lut_partial; + uint8_t lut_siz_partial; + + uint8_t epcoffs_full; + uint8_t epc_full_cnt; + uint8_t epcoffs_part; + uint8_t epc_part_cnt; + + uint8_t *lut_array[MAX_LUTS]; + uint8_t lut_cnt[MAX_LUTS]; + uint8_t lut_cmd[MAX_LUTS]; + uint8_t lut_siz[MAX_LUTS]; uint16_t seta_xp1; uint16_t seta_xp2; uint16_t seta_yp1; @@ -308,6 +320,11 @@ class uDisplay : public Renderer { int16_t rotmap_ymin; int16_t rotmap_ymax; void pushColorsMono(uint16_t *data, uint16_t len, bool rgb16_swap = false); + void delay_sync(int32_t time); + void reset_pin(int32_t delayl, int32_t delayh); + void delay_arg(uint32_t arg); + void Send_EP_Data(void); + void send_spi_cmds(uint16_t cmd_offset, uint16_t cmd_size); #ifdef USE_ESP32_S3 int8_t par_cs; diff --git a/tasmota/displaydesc/WS_epaper29_display.ini b/tasmota/displaydesc/WS_epaper29_v1_display.ini similarity index 67% rename from tasmota/displaydesc/WS_epaper29_display.ini rename to tasmota/displaydesc/WS_epaper29_v1_display.ini index 0e63a13b1..e608fb0d0 100644 --- a/tasmota/displaydesc/WS_epaper29_display.ini +++ b/tasmota/displaydesc/WS_epaper29_v1_display.ini @@ -1,4 +1,4 @@ -:H,E-PAPER-29,128,296,1,SPI,1,*,*,*,*,*,*,*,10 +:H,E-PAPER-29-V1,128,296,1,SPI,3,*,*,*,*,*,*,*,10 :S,1,1,1,0,10,10 :I 01,3,27,01,00 @@ -7,10 +7,23 @@ 3A,1,1A 3B,1,08 11,1,03 -:L +62,0 +:f +61,0 +68,0 +22,1,c4 +20,0 +ff,0 +:p +62,0 +68,0 +22,1,c4 +20,0 +ff,0 +:L,30,32 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 +:l,30,32 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/WS_epaper29_v2_display.ini b/tasmota/displaydesc/WS_epaper29_v2_display.ini new file mode 100644 index 000000000..4d0681713 --- /dev/null +++ b/tasmota/displaydesc/WS_epaper29_v2_display.ini @@ -0,0 +1,96 @@ +:H,E-PAPER-29-V2,128,296,1,SPI,4,*,*,*,*,*,*,*,10 +:S,1,1,1,0,10,10 +:I +12,0 +63,1,80 +01,3,27,01,00 +11,1,03 +64,0 +21,2,00,80 +65,0 +63,1,80 +61,0 +63,1,80 +3f,1,22 +03,1,17 +04,3,41,00,32 +2c,1,36 +67,0 +22,1,c7; +20,0 +63,1,80 +24,0 +66,0 +22,1,c7 +20,0 +63,1,80 +62 +:f +64,0 +65,0 +24,0 +66,0 +22,1,c7 +20,0 +63,1,80 +:p +60,1,2 +62,0 +63,1,80 +37,0a,00,00,00,00,00,40,00,00,00,00 +3c,1,80 +22,1,c0 +20,0 +63,1,80 +64,0 +65,0 +24,0 +66,0 +22,1,0f +20,0 +63,1,80 +62,0 + +:L,153,32 +80,66,00,00,00,00,00,00,40,00,00,00 +10,66,00,00,00,00,00,00,20,00,00,00 +80,66,00,00,00,00,00,00,40,00,00,00 +10,66,00,00,00,00,00,00,20,00,00,00 +00,00,00,00,00,00,00,00,00,00,00,00 +14,08,00,00,00,00,01 +0A,0A,00,0A,0A,00,01 +00,00,00,00,00,00,00 +00,00,00,00,00,00,00 +00,00,00,00,00,00,00 +00,00,00,00,00,00,00 +00,00,00,00,00,00,00 +00,00,00,00,00,00,00 +14,08,00,01,00,00,01 +00,00,00,00,00,00,01 +00,00,00,00,00,00,00 +00,00,00,00,00,00,00 +44,44,44,44,44,44,00,00,00 + +:l,153,32 +00,40,00,00,00,00,00,00,00,00,00,00 +80,80,00,00,00,00,00,00,00,00,00,00 +40,40,00,00,00,00,00,00,00,00,00,00 +00,80,00,00,00,00,00,00,00,00,00,00 +00,00,00,00,00,00,00,00,00,00,00,00 +0A,00,00,00,00,00,02 +01,00,00,00,00,00,00 +01,00,00,00,00,00,00 +00,00,00,00,00,00,00 +00,00,00,00,00,00,00 +00,00,00,00,00,00,00 +00,00,00,00,00,00,00 +00,00,00,00,00,00,00 +00,00,00,00,00,00,00 +00,00,00,00,00,00,00 +00,00,00,00,00,00,00 +00,00,00,00,00,00,00 +22,22,22,22,22,22,00,00,00 + + +:T,350,35,10 +# diff --git a/tasmota/displaydesc/WS_epaper42_display.ini b/tasmota/displaydesc/WS_epaper42_display.ini index 56b926116..89190b2be 100644 --- a/tasmota/displaydesc/WS_epaper42_display.ini +++ b/tasmota/displaydesc/WS_epaper42_display.ini @@ -1,25 +1,26 @@ -:H,E-PAPER-42,400,300,1,SPI,1,*,*,*,*,*,*,*,10 +:H,E-PAPER-42,400,300,1,SPI,4,*,*,*,*,*,*,*,10 :S,1,1,1,0,10,10 +:B,60,8 :I -01,5,03,00,2b,2b,03 +01,5,03,00,2b,2b,ff 06,3,17,17,17 04,80 00,1,3F 30,1,3C 61,4,01,90,01,2C -82,1,28 +82,1,12 50,1,97 -:A,10,13,12 +:a,10,13,12 :T,450,10,450 -:L1,20 -00,17,00,00,00,02 +:L1,44,20 +40,17,00,00,00,02 00,17,17,00,00,02 00,0A,01,00,00,01 00,0E,0E,00,00,02 00,00,00,00,00,00 00,00,00,00,00,00 00,00,00,00,00,00,00,00 -:L2,21 +:L2,42,21 40,17,00,00,00,02 90,17,17,00,00,02 40,0A,01,00,00,01 @@ -27,15 +28,15 @@ A0,0E,0E,00,00,02 00,00,00,00,00,00 00,00,00,00,00,00 00,00,00,00,00,00 -:L3,22 +:L3,42,22 40,17,00,00,00,02 90,17,17,00,00,02 -A0,0A,01,00,00,01 -00,0E,0E,00,00,02 +40,0A,01,00,00,01 +A0,0E,0E,00,00,02 00,00,00,00,00,00 00,00,00,00,00,00 00,00,00,00,00,00 -:L4,23 +:L4,42,23 80,17,00,00,00,02 90,17,17,00,00,02 80,0A,01,00,00,01 @@ -43,7 +44,7 @@ A0,0A,01,00,00,01 00,00,00,00,00,00 00,00,00,00,00,00 00,00,00,00,00,00 -:L5,24 +:L5,42,24 80,17,00,00,00,02 90,17,17,00,00,02 80,0A,01,00,00,01 diff --git a/tasmota/tasmota_xdsp_display/xdsp_17_universal.ino b/tasmota/tasmota_xdsp_display/xdsp_17_universal.ino index 01dd6495a..c3154cd60 100644 --- a/tasmota/tasmota_xdsp_display/xdsp_17_universal.ino +++ b/tasmota/tasmota_xdsp_display/xdsp_17_universal.ino @@ -50,7 +50,9 @@ extern uint16_t fg_color; extern uint16_t bg_color; #endif +#ifndef DISPDESC_SIZE #define DISPDESC_SIZE 1000 +#endif void Core2DisplayPower(uint8_t on); void Core2DisplayDim(uint8_t dim); @@ -94,6 +96,11 @@ int8_t cs; fp = ffsp->open(DISP_DESC_FILE, "r"); if (fp > 0) { uint32_t size = fp.size(); + if (size > DISPDESC_SIZE - 50) { + free(fbuff); + fbuff = (char*)calloc(size + 50, 1); + if (!fbuff) return 0; + } fp.read((uint8_t*)fbuff, size); fp.close(); ddesc = fbuff; @@ -148,6 +155,7 @@ int8_t cs; if (fbuff) free(fbuff); return 0; } + // now replace tasmota vars before passing to driver char *cp = strstr(ddesc, "I2C"); if (cp) { @@ -296,6 +304,7 @@ int8_t cs; delete renderer; AddLog(LOG_LEVEL_DEBUG, PSTR("DSP: reinit")); } + udisp = new uDisplay(ddesc); // checck for touch option TI1 or TI2