From 0eec031d49f93877b1cd5b69eea9832c242c32d3 Mon Sep 17 00:00:00 2001 From: Christian Baars Date: Sun, 12 Oct 2025 17:15:02 +0200 Subject: [PATCH] split udisplay.cpp into multiple files (#24007) --- lib/lib_display/UDisplay/uDisplay.cpp | 2980 +---------------- lib/lib_display/UDisplay/uDisplay.h | 364 +- lib/lib_display/UDisplay/uDisplayColors.cpp | 31 + lib/lib_display/UDisplay/uDisplay_config.h | 56 + lib/lib_display/UDisplay/uDisplay_control.cpp | 146 + lib/lib_display/UDisplay/uDisplay_dma.cpp | 133 + lib/lib_display/UDisplay/uDisplay_epd.cpp | 362 ++ lib/lib_display/UDisplay/uDisplay_esp32s3.cpp | 158 + .../UDisplay/uDisplay_graphics.cpp | 565 ++++ .../UDisplay/uDisplay_parallel.cpp | 142 + lib/lib_display/UDisplay/uDisplay_spi.cpp | 154 + lib/lib_display/UDisplay/uDisplay_spi.h | 71 + .../UDisplay/uDisplay_spi_comm.cpp | 158 + lib/lib_display/UDisplay/uDisplay_timing.cpp | 60 + lib/lib_display/UDisplay/uDisplay_touch.cpp | 568 ++++ lib/lib_display/UDisplay/uDisplay_utils.cpp | 111 + 16 files changed, 2923 insertions(+), 3136 deletions(-) mode change 100755 => 100644 lib/lib_display/UDisplay/uDisplay.cpp mode change 100755 => 100644 lib/lib_display/UDisplay/uDisplay.h create mode 100644 lib/lib_display/UDisplay/uDisplayColors.cpp create mode 100644 lib/lib_display/UDisplay/uDisplay_config.h create mode 100644 lib/lib_display/UDisplay/uDisplay_control.cpp create mode 100644 lib/lib_display/UDisplay/uDisplay_dma.cpp create mode 100644 lib/lib_display/UDisplay/uDisplay_epd.cpp create mode 100644 lib/lib_display/UDisplay/uDisplay_esp32s3.cpp create mode 100644 lib/lib_display/UDisplay/uDisplay_graphics.cpp create mode 100644 lib/lib_display/UDisplay/uDisplay_parallel.cpp create mode 100644 lib/lib_display/UDisplay/uDisplay_spi.cpp create mode 100644 lib/lib_display/UDisplay/uDisplay_spi.h create mode 100644 lib/lib_display/UDisplay/uDisplay_spi_comm.cpp create mode 100644 lib/lib_display/UDisplay/uDisplay_timing.cpp create mode 100644 lib/lib_display/UDisplay/uDisplay_touch.cpp create mode 100644 lib/lib_display/UDisplay/uDisplay_utils.cpp diff --git a/lib/lib_display/UDisplay/uDisplay.cpp b/lib/lib_display/UDisplay/uDisplay.cpp old mode 100755 new mode 100644 index 0619d796b..142eecfcd --- a/lib/lib_display/UDisplay/uDisplay.cpp +++ b/lib/lib_display/UDisplay/uDisplay.cpp @@ -19,55 +19,24 @@ #include #include "uDisplay.h" +#include "uDisplay_config.h" +#include "uDisplay_spi.h" #ifdef ESP32 #include "esp8266toEsp32.h" #endif - -#ifdef USE_ESP32_S3 -#include "esp_cache.h" -#endif // USE_ESP32_S3 - #include "tasmota_options.h" -extern int Cache_WriteBack_Addr(uint32_t addr, uint32_t size); - - //#define UDSP_DEBUG #ifndef UDSP_LBSIZE #define UDSP_LBSIZE 256 #endif -#define renderer_swap(a, b) { int16_t t = a; a = b; b = t; } - -enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE}; -extern void AddLog(uint32_t loglevel, PGM_P formatP, ...); - -const uint16_t udisp_colors[]={UDISP_BLACK,UDISP_WHITE,UDISP_RED,UDISP_GREEN,UDISP_BLUE,UDISP_CYAN,UDISP_MAGENTA,\ - UDISP_YELLOW,UDISP_NAVY,UDISP_DARKGREEN,UDISP_DARKCYAN,UDISP_MAROON,UDISP_PURPLE,UDISP_OLIVE,\ -UDISP_LIGHTGREY,UDISP_DARKGREY,UDISP_ORANGE,UDISP_GREENYELLOW,UDISP_PINK}; - -uint16_t uDisplay::GetColorFromIndex(uint8_t index) { - if (index >= sizeof(udisp_colors) / 2) index = 0; - return udisp_colors[index]; -} - -uint16_t uDisplay::fgcol(void) { - return fg_col; -} -uint16_t uDisplay::bgcol(void) { - return bg_col; -} - -int8_t uDisplay::color_type(void) { - return col_type; -} - uDisplay::~uDisplay(void) { #ifdef UDSP_DEBUG - Serial.printf("dealloc\n"); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: dealloc"); #endif if (frame_buffer) { free(frame_buffer); @@ -176,7 +145,7 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { if (*lp1 == '#') break; if (*lp1 == '\n') lp1++; while (*lp1 == ' ') lp1++; - //Serial.printf(">> %s\n",lp1); + //AddLog(LOG_LEVEL_DEBUG, ">> %s\n",lp1); if (*lp1 != ';') { // check ids: if (*lp1 == ':') { @@ -221,10 +190,7 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { reset_pin(50, 200); } #ifdef UDSP_DEBUG - Serial.printf("SSPI_MOSI : %d\n", spi_mosi); - Serial.printf("SSPI_SCLK : %d\n", spi_clk); - Serial.printf("SSPI_CS : %d\n", spi_cs); - Serial.printf("DSP RESET : %d\n", reset); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: SSPI_MOSI:%d SSPI_SCLK:%d SSPI_CS:%d DSP_RESET:%d", spi_mosi, spi_clk, spi_cs, reset); #endif } } else if (*lp1 == 'I') { @@ -236,8 +202,7 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { wire_n = next_val(&lp1); i2caddr = next_hex(&lp1); #ifdef UDSP_DEBUG - Serial.printf("I2C_INIT bus : %d\n", wire_n); - Serial.printf("I2C_INIT addr : %02x\n", i2caddr); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: I2C_INIT bus:%d addr:%02x", wire_n, i2caddr); #endif if (wire_n == 1) { wire = &Wire; @@ -405,12 +370,12 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { wire->write(dsp_cmds[1]); wire->endTransmission(); #ifdef UDSP_DEBUG - Serial.printf("reg = %02x, val = %02x\n", dsp_cmds[0], dsp_cmds[1]); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: reg=%02x val=%02x", dsp_cmds[0], dsp_cmds[1]); #endif } else { delay(dsp_cmds[0]); #ifdef UDSP_DEBUG - Serial.printf("delay = %d ms\n", dsp_cmds[0]); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: delay=%d ms", dsp_cmds[0]); #endif } } @@ -718,151 +683,77 @@ void UfsCheckSDCardInit(void); } #ifdef UDSP_DEBUG - Serial.printf("Device : %s\n", dname); - Serial.printf("xs : %d\n", gxs); - Serial.printf("ys : %d\n", gys); - Serial.printf("bpp: %d\n", bpp); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: Device:%s xs:%d ys:%d bpp:%d", dname, gxs, gys, bpp); 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("TS_CS: %d\n", ut_spi_cs); - Serial.printf("TS_RST: %d\n", ut_reset); - Serial.printf("TS_IRQ: %d\n", ut_irq); - 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("DMA-Mode: %d\n", lvgl_param.use_dma); - - Serial.printf("opts: %02x,%02x,%02x\n", saw_3, dim_op, startline); - - 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]); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: Nr:%d CS:%d CLK:%d MOSI:%d DC:%d TS_CS:%d TS_RST:%d TS_IRQ:%d", + spi_nr, spi_cs, spi_clk, spi_mosi, spi_dc, ut_spi_cs, ut_reset, ut_irq); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: BPAN:%d RES:%d MISO:%d SPED:%d Pixels:%d SaMode:%d DMA-Mode:%d opts:%02x,%02x,%02x SetAddr:%x,%x,%x", + bpanel, reset, spi_miso, spi_speed*1000000, col_mode, sa_mode, lvgl_param.use_dma, saw_3, dim_op, startline, saw_1, saw_2, saw_3); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: Rot 0: %x,%x - %d - %d", madctrl, rot[0], x_addr_offs[0], y_addr_offs[0]); if (ep_mode == 1) { - 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); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: LUT_Partial:%d-%d-%x-%d-%d LUT_Full:%d-%d-%x-%d-%d", + lut_siz_partial, lutpsize, lut_cmd[0], epcoffs_part, epc_part_cnt, + 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]); - Serial.printf("LUT_SIZE 2: %d\n", lut_cnt[1]); - Serial.printf("LUT_SIZE 3: %d\n", lut_cnt[2]); - Serial.printf("LUT_SIZE 4: %d\n", lut_cnt[3]); - Serial.printf("LUT_SIZE 5: %d\n", lut_cnt[4]); - Serial.printf("LUT_CMDS %02x-%02x-%02x-%02x-%02x\n", lut_cmd[0], lut_cmd[1], lut_cmd[2], lut_cmd[3], lut_cmd[4]); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: LUT_SIZE 1:%d 2:%d 3:%d 4:%d 5:%d", + lut_cnt[0], lut_cnt[1], lut_cnt[2], lut_cnt[3], lut_cnt[4]); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: LUT_CMDS %02x-%02x-%02x-%02x-%02x", + lut_cmd[0], lut_cmd[1], lut_cmd[2], lut_cmd[3], lut_cmd[4]); } } if (interface == _UDSP_I2C) { - Serial.printf("Addr : %02x\n", i2caddr); - Serial.printf("SCL : %d\n", i2c_scl); - Serial.printf("SDA : %d\n", i2c_sda); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: Addr:%02x SCL:%d SDA:%d", i2caddr, i2c_scl, 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); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: SPA:%x pa_sta:%x pa_end:%x SCA:%x ca_sta:%x ca_end:%x WRA:%x", + saw_1, i2c_page_start, i2c_page_end, saw_2, i2c_col_start, i2c_col_end, saw_3); } if (interface == _UDSP_PAR8 || interface == _UDSP_PAR16) { #ifdef USE_ESP32_S3 - Serial.printf("par mode: %d\n", interface); - Serial.printf("par res: %d\n", reset); - Serial.printf("par cs : %d\n", par_cs); - Serial.printf("par rs : %d\n", par_rs); - Serial.printf("par wr : %d\n", par_wr); - Serial.printf("par rd : %d\n", par_rd); - Serial.printf("par bp : %d\n", bpanel); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: par mode:%d res:%d cs:%d rs:%d wr:%d rd:%d bp:%d", + interface, reset, par_cs, par_rs, par_wr, par_rd, bpanel); for (uint32_t cnt = 0; cnt < 8; cnt ++) { - Serial.printf("par d%d: %d\n", cnt, par_dbl[cnt]); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: par d%d:%d", cnt, par_dbl[cnt]); } if (interface == _UDSP_PAR16) { for (uint32_t cnt = 0; cnt < 8; cnt ++) { - Serial.printf("par d%d: %d\n", cnt + 8, par_dbh[cnt]); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: par d%d:%d", cnt + 8, par_dbh[cnt]); } } - Serial.printf("par freq : %d\n", spi_speed); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: par freq:%d", spi_speed); #endif // USE_ESP32_S3 } if (interface == _UDSP_RGB) { #ifdef USE_ESP32_S3 - Serial.printf("rgb de: %d\n", de); - Serial.printf("rgb vsync: %d\n", vsync); - Serial.printf("rgb hsync : %d\n", hsync); - Serial.printf("rgb pclk : %d\n", pclk); - Serial.printf("rgb bp : %d\n", bpanel); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: rgb de:%d vsync:%d hsync:%d pclk:%d bp:%d", de, vsync, hsync, pclk, bpanel); for (uint32_t cnt = 0; cnt < 8; cnt ++) { - Serial.printf("rgb d%d: %d\n", cnt, par_dbl[cnt]); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: rgb d%d:%d", cnt, par_dbl[cnt]); } for (uint32_t cnt = 0; cnt < 8; cnt ++) { - Serial.printf("rgb d%d: %d\n", cnt + 8, par_dbh[cnt]); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: rgb d%d:%d", cnt + 8, par_dbh[cnt]); } - Serial.printf("rgb freq : %d\n", spi_speed); - - Serial.printf("rgb hsync_polarity: %d\n", hsync_polarity); - Serial.printf("rgb hsync_front_porch: %d\n", hsync_front_porch); - Serial.printf("rgb hsync_pulse_width : %d\n", hsync_pulse_width); - Serial.printf("rgb hsync_back_porch : %d\n", hsync_back_porch); - Serial.printf("rgb vsync_polarity : %d\n", vsync_polarity); - Serial.printf("rgb vsync_front_porch : %d\n", vsync_front_porch); - Serial.printf("rgb vsync_pulse_width : %d\n", vsync_pulse_width); - Serial.printf("rgb vsync_back_porch : %d\n", vsync_back_porch); - Serial.printf("rgb pclk_active_neg : %d\n", pclk_active_neg); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: rgb freq:%d hsync_pol:%d hsync_fp:%d hsync_pw:%d hsync_bp:%d vsync_pol:%d vsync_fp:%d vsync_pw:%d vsync_bp:%d pclk_neg:%d", + spi_speed, hsync_polarity, hsync_front_porch, hsync_pulse_width, hsync_back_porch, + vsync_polarity, vsync_front_porch, vsync_pulse_width, vsync_back_porch, pclk_active_neg); #endif // USE_ESP32_S3 } #endif #ifdef UDSP_DEBUG - Serial.printf("Dsp class init complete\n"); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: Dsp class init complete"); #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 -#define EP_BREAK_RR_EQU 0x69 -#define EP_BREAK_RR_NEQ 0x6a - -extern int32_t ESP_ResetInfoReason(); - // special init for GC displays void uDisplay::send_spi_icmds(uint16_t cmd_size) { uint16_t index = 0; @@ -870,7 +761,7 @@ uint16_t cmd_offset = 0; #ifdef UDSP_DEBUG - Serial.printf("start send icmd table\n"); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: start send icmd table"); #endif while (1) { uint8_t iob; @@ -881,27 +772,24 @@ uint16_t cmd_offset = 0; uint8_t args = dsp_cmds[cmd_offset++]; index++; #ifdef UDSP_DEBUG - Serial.printf("cmd, args %02x, %d ", iob, args & 0x7f); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: cmd, args %02x, %d", iob, args & 0x7f); #endif for (uint32_t cnt = 0; cnt < (args & 0x7f); cnt++) { iob = dsp_cmds[cmd_offset++]; index++; #ifdef UDSP_DEBUG - Serial.printf("%02x ", iob); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: %02x", iob); #endif ulcd_data8(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 icmd table\n"); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: end send icmd table"); #endif return; } @@ -910,7 +798,7 @@ uint16_t cmd_offset = 0; 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"); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: start send cmd table"); #endif while (1) { uint8_t iob; @@ -922,7 +810,7 @@ uint16_t index = 0; uint8_t args = dsp_cmds[cmd_offset++]; index++; #ifdef UDSP_DEBUG - Serial.printf("cmd, args %02x, %d ", iob, args & 0x1f); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: cmd, args %02x, %d", iob, args & 0x1f); #endif switch (iob) { case EP_RESET: @@ -986,9 +874,8 @@ uint16_t index = 0; } #ifdef UDSP_DEBUG if (args & 1) { - Serial.printf("%02x ", iob ); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: %02x", iob); } - Serial.printf("\n"); #endif if (args & 0x80) { // delay after the command delay_arg(args); @@ -1009,13 +896,13 @@ uint16_t index = 0; uint8_t args = dsp_cmds[cmd_offset++]; index++; #ifdef UDSP_DEBUG - Serial.printf("cmd, args %02x, %d ", iob, args & 0x1f); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: 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 ); + AddLog(LOG_LEVEL_DEBUG, "%02x ", iob ); #endif if (!allcmd_mode) { ulcd_data8(iob); @@ -1024,9 +911,6 @@ uint16_t index = 0; } } SPI_CS_HIGH -#ifdef UDSP_DEBUG - Serial.printf("\n"); -#endif if (args & 0x80) { // delay after the command delay_arg(args); } @@ -1036,23 +920,21 @@ uint16_t index = 0; exit: #ifdef UDSP_DEBUG - Serial.printf("end send cmd table\n"); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: end send cmd table"); #endif return; } Renderer *uDisplay::Init(void) { - extern bool UsePSRAM(void); - if (!interface) { // no valid configuration, abort #ifdef UDSP_DEBUG - Serial.printf("Dsp Init no valid configuration\n"); + AddLog(LOG_LEVEL_INFO, "UDisplay: Dsp Init no valid configuration"); #endif return NULL; } #ifdef UDSP_DEBUG - Serial.printf("Dsp Init 1 start \n"); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: Dsp Init 1 start"); #endif // for any bpp below native 16 bits, we allocate a local framebuffer to copy into @@ -1085,12 +967,12 @@ Renderer *uDisplay::Init(void) { } */ #ifdef UDSP_DEBUG - Serial.printf("I2C cmds: %d\n", dsp_ncmds); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: I2C cmds:%d", 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]); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: cmd=%x", dsp_cmds[cnt]); #endif } @@ -1156,7 +1038,7 @@ Renderer *uDisplay::Init(void) { busy_pin = spi_miso; pinMode(spi_miso, INPUT_PULLUP); #ifdef UDSP_DEBUG - Serial.printf("Dsp busy pin: %d\n", busy_pin); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: Dsp busy pin:%d", busy_pin); #endif } } @@ -1183,7 +1065,7 @@ Renderer *uDisplay::Init(void) { #ifdef USE_ESP32_S3 if (!UsePSRAM()) { // RGB is not supported on S3 without PSRAM #ifdef UDSP_DEBUG - Serial.printf("Dsp RGB requires PSRAM, abort\n"); + AddLog(LOG_LEVEL_INFO, "UDisplay: Dsp RGB requires PSRAM, abort"); #endif return NULL; } @@ -1355,19 +1237,16 @@ Renderer *uDisplay::Init(void) { uint8_t args = dsp_cmds[index++]; #ifdef UDSP_DEBUG - Serial.printf("cmd, args %02x, %d ", iob, args&0x1f); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: 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 ); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: %02x", iob); #endif pb_writeData(iob, 8); } cs_control(1); - #ifdef UDSP_DEBUG - Serial.printf("\n"); - #endif if (args & 0x80) { // delay after the command uint32_t delay_ms = 0; switch (args & 0xE0) { @@ -1378,7 +1257,7 @@ Renderer *uDisplay::Init(void) { if (delay_ms > 0) { delay(delay_ms); #ifdef UDSP_DEBUG - Serial.printf("delay %d ms\n", delay_ms); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: delay %d ms", delay_ms); #endif } @@ -1400,7 +1279,7 @@ Renderer *uDisplay::Init(void) { } #ifdef UDSP_DEBUG - Serial.printf("Dsp Init 1 complete \n"); + AddLog(LOG_LEVEL_DEBUG, "UDisplay: Dsp Init 1 complete"); #endif return this; } @@ -1411,7 +1290,7 @@ void uDisplay::DisplayInit(int8_t p, int8_t size, int8_t rot, int8_t font) { if (p == DISPLAY_INIT_PARTIAL) { if (lutpsize) { #ifdef UDSP_DEBUG - Serial.printf("init partial epaper mode\n"); + AddLog(LOG_LEVEL_DEBUG, "init partial epaper mode"); #endif SetLut(lut_partial); Updateframe_EPD(); @@ -1420,7 +1299,7 @@ void uDisplay::DisplayInit(int8_t p, int8_t size, int8_t rot, int8_t font) { return; } else if (p == DISPLAY_INIT_FULL) { #ifdef UDSP_DEBUG - Serial.printf("init full epaper mode\n"); + AddLog(LOG_LEVEL_DEBUG, "init full epaper mode"); #endif if (lutfsize) { SetLut(lut_full); @@ -1448,185 +1327,13 @@ void uDisplay::DisplayInit(int8_t p, int8_t size, int8_t rot, int8_t font) { } #ifdef UDSP_DEBUG - Serial.printf("Dsp Init 2 complete \n"); + AddLog(LOG_LEVEL_DEBUG, "Dsp Init 2 complete"); #endif } } -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) { - - if (interface == _UDSP_SPI) { - if (spi_dc < 0) { - if (spi_nr > 2) { - if (spi_nr == 3) { - write9(val, 0); - } else { - write9_slow(val, 0); - } - } else { - hw_write9(val, 0); - } - } else { - SPI_DC_LOW - if (spi_nr > 2) { - if (spi_nr == 3) { - write8(val); - } else { - write8_slow(val); - } - } else { - uspi->write(val); - } - SPI_DC_HIGH - } - return; - } - -#ifdef USE_ESP32_S3 - if (interface == _UDSP_PAR8 || interface == _UDSP_PAR16) { - pb_writeCommand(val, 8); - } -#endif // USE_ESP32_S3 -} - -void uDisplay::ulcd_data8(uint8_t val) { - - if (interface == _UDSP_SPI) { - if (spi_dc < 0) { - if (spi_nr > 2) { - if (spi_nr == 3) { - write9(val, 1); - } else { - write9_slow(val, 1); - } - } else { - hw_write9(val, 1); - } - } else { - if (spi_nr > 2) { - if (spi_nr == 3) { - write8(val); - } else { - write8_slow(val); - } - } else { - uspi->write(val); - } - } - return; - } - -#ifdef USE_ESP32_S3 - if (interface == _UDSP_PAR8 || interface == _UDSP_PAR16) { - pb_writeData(val, 8); - } -#endif // USE_ESP32_S3 -} - -void uDisplay::ulcd_data16(uint16_t val) { - - if (interface == _UDSP_SPI) { - if (spi_dc < 0) { - if (spi_nr > 2) { - write9(val >> 8, 1); - write9(val, 1); - } else { - hw_write9(val >> 8, 1); - hw_write9(val, 1); - } - } else { - if (spi_nr > 2) { - write16(val); - } else { - uspi->write16(val); - } - } - return; - } - -#ifdef USE_ESP32_S3 - if (interface == _UDSP_PAR8 || interface == _UDSP_PAR16) { - pb_writeData(val, 16); - } -#endif // USE_ESP32_S3 -} - -void uDisplay::ulcd_data32(uint32_t val) { - - if (interface == _UDSP_SPI) { - if (spi_dc < 0) { - if (spi_nr > 2) { - write9(val >> 24, 1); - write9(val >> 16, 1); - write9(val >> 8, 1); - write9(val, 1); - } else { - hw_write9(val >> 24, 1); - hw_write9(val >> 16, 1); - hw_write9(val >> 8, 1); - hw_write9(val, 1); - } - } else { - if (spi_nr > 2) { - write32(val); - } else { - uspi->write32(val); - } - } - return; - } - -#ifdef USE_ESP32_S3 - if (interface == _UDSP_PAR8 || interface == _UDSP_PAR16) { - pb_writeData(val, 32); - } -#endif // USE_ESP32_S3 -} - -void uDisplay::ulcd_command_one(uint8_t val) { - - if (interface == _UDSP_SPI) { - SPI_BEGIN_TRANSACTION - SPI_CS_LOW - ulcd_command(val); - SPI_CS_HIGH - SPI_END_TRANSACTION - } -} - void uDisplay::i2c_command(uint8_t val) { - //Serial.printf("%02x\n",val ); + //AddLog(LOG_LEVEL_DEBUG, "%02x\n",val ); wire->beginTransmission(i2caddr); wire->write(0); wire->write(val); @@ -1725,7 +1432,7 @@ void uDisplay::Updateframe(void) { //uint8_t xs = 132 >> 3; uint8_t m_row = saw_2; uint8_t m_col = i2c_col_start; - // Serial.printf("m_row=%d m_col=%d xs=%d ys=%d\n", m_row, m_col, xs, ys); + // AddLog(LOG_LEVEL_DEBUG, "m_row=%d m_col=%d xs=%d ys=%d\n", m_row, m_col, xs, ys); uint16_t p = 0; @@ -1749,2568 +1456,3 @@ void uDisplay::Updateframe(void) { } } - -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 (framebuffer) { - Renderer::drawFastVLine(x, y, h, color); - return; - } - - // Rudimentary clipping - if ((x >= _width) || (y >= _height)) return; - if ((y + h - 1) >= _height) h = _height - y; - - - if (interface == _UDSP_RGB) { - #ifdef USE_ESP32_S3 - if (cur_rot > 0) { - while (h--) { - drawPixel_RGB(x , y , color); - y++; - } - } else { - uint16_t *fb = rgb_fb; - fb += (int32_t)y * _width; - fb += x; - while (h--) { - *fb = color; - Cache_WriteBack_Addr((uint32_t)fb, 2); - fb+=_width; - y++; - } - } - #endif - return; - } - - SPI_BEGIN_TRANSACTION - - SPI_CS_LOW - - setAddrWindow_int(x, y, 1, h); - - if (col_mode == 18) { - uint8_t r = (color & 0xF800) >> 11; - uint8_t g = (color & 0x07E0) >> 5; - uint8_t b = color & 0x001F; - r = (r * 255) / 31; - g = (g * 255) / 63; - b = (b * 255) / 31; - - while (h--) { - ulcd_data8(r); - ulcd_data8(g); - ulcd_data8(b); - } - } else { - while (h--) { - WriteColor(color); - } - } - - SPI_CS_HIGH - - SPI_END_TRANSACTION -} - -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 (framebuffer) { - Renderer::drawFastHLine(x, y, w, color); - return; - } - - // Rudimentary clipping - if((x >= _width) || (y >= _height)) return; - if((x + w - 1) >= _width) w = _width - x; - - - if (interface == _UDSP_RGB) { -#ifdef USE_ESP32_S3 - if (cur_rot > 0) { - while (w--) { - drawPixel_RGB(x , y , color); - x++; - } - } else { - uint16_t *fb = rgb_fb; - fb += (int32_t)y * _width; - fb += x; - while (w--) { - *fb = color; - Cache_WriteBack_Addr((uint32_t)fb, 2); - fb++; - x++; - } - } - #endif - return; - } - - - SPI_BEGIN_TRANSACTION - - SPI_CS_LOW - - setAddrWindow_int(x, y, w, 1); - - if (col_mode == 18) { - uint8_t r = (color & 0xF800) >> 11; - uint8_t g = (color & 0x07E0) >> 5; - uint8_t b = color & 0x001F; - r = (r * 255) / 31; - g = (g * 255) / 63; - b = (b * 255) / 31; - - while (w--) { - ulcd_data8(r); - ulcd_data8(g); - ulcd_data8(b); - } - } else { - while (w--) { - WriteColor(color); - } - } - - SPI_CS_HIGH - - SPI_END_TRANSACTION -} - -//#define CD_XS gxs -//#define CD_YS gys -#define CD_XS width() -#define CD_YS height() - -void uDisplay::fillScreen(uint16_t color) { - fillRect(0, 0, CD_XS, CD_YS, color); -} - -// fill a rectangle -void uDisplay::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { - - if (interface == _UDSP_RGB) { - for (uint32_t yp = y; yp < y + h; yp++) { - drawFastHLine(x, yp, w, color); - } - return; - } - - - if (ep_mode) { - fillRect_EPD(x, y, w, h, color); - return; - } - - if (framebuffer) { - Renderer::fillRect(x, y, w, h, color); - return; - } - - if((x >= CD_XS) || (y >= CD_YS)) return; - if((x + w - 1) >= CD_XS) w = CD_XS - x; - if((y + h - 1) >= CD_YS) h = CD_YS - y; - - - SPI_BEGIN_TRANSACTION - SPI_CS_LOW - - setAddrWindow_int(x, y, w, h); - - if (col_mode == 18) { - uint8_t r = (color & 0xF800) >> 11; - uint8_t g = (color & 0x07E0) >> 5; - uint8_t b = color & 0x001F; - r = (r * 255) / 31; - g = (g * 255) / 63; - b = (b * 255) / 31; - - for (y = h; y > 0; y--) { - for (x = w; x > 0; x--) { - ulcd_data8(r); - ulcd_data8(g); - ulcd_data8(b); - } - } - - } else { - for (y = h; y > 0; y--) { - for (x = w; x > 0; x--) { - WriteColor(color); - } - } - } - SPI_CS_HIGH - SPI_END_TRANSACTION -} - -/* - -// pack RGB into uint32 -uint32_t pack_rgb(uint32_t r, uint32_t g, uint32_t b) { - uint32_t data; - data=r<<23; - data|=g<<14; - data|=b<<5; - data|=0b10000000010000000010000000000000; - return ulswap(data); -} - -// init 27 bit mode -uint32_t data=pack_rgb(r,g,b); -REG_SET_BIT(SPI_USER_REG(3), SPI_USR_MOSI); -REG_WRITE(SPI_MOSI_DLEN_REG(3), 27 - 1); -uint32_t *dp=(uint32_t*)SPI_W0_REG(3); -digitalWrite( _cs, LOW); -for(y=h; y>0; y--) { - for(x=w; x>0; x--) { - while (REG_GET_FIELD(SPI_CMD_REG(3), SPI_USR)); - *dp=data; - REG_SET_BIT(SPI_CMD_REG(3), SPI_USR); - } -} -*/ - -#ifdef USE_UNIVERSAL_TOUCH - -uint8_t ut_irq_flg; - -void IRAM_ATTR ut_touch_irq(void) { - ut_irq_flg = 1; -} - -// universal touch driver -bool uDisplay::utouch_Init(char **name) { - *name = ut_name; - if (ut_init_code) { - if (ut_reset >= 0) { - pinMode(ut_reset, OUTPUT); - digitalWrite(ut_reset, HIGH); - delay(10); - digitalWrite(ut_reset, LOW); - delay(5); - digitalWrite(ut_reset, HIGH); - delay(10); - } - if (ut_irq >= 0) { - pinMode(ut_irq, INPUT ); - attachInterrupt(ut_irq, ut_touch_irq, FALLING); - } - -extern SPIClass *SpiBegin(uint32 bus); - - if (ut_spi_nr == spi_nr) { - // same as display - ut_spi = uspi; - } else { -#ifdef ESP32 - ut_spi = SpiBegin(ut_spi_nr); -#endif - } - return ut_execute(ut_init_code); - } - return false; -} - -uint16_t uDisplay::touched(void) { - if (ut_irq >= 0) { - if (!ut_irq_flg) { - return false; - } - ut_irq_flg = 0; - } - if (ut_touch_code) { - return ut_execute(ut_touch_code); - } - return 0; -} - -int16_t uDisplay::getPoint_x(void) { - if (ut_getx_code) { - return ut_execute(ut_getx_code); - } - return 0; -} - -int16_t uDisplay::getPoint_y(void) { - if (ut_gety_code) { - return ut_execute(ut_gety_code); - } - return 0; -} -#endif // USE_UNIVERSAL_TOUCH - - -void uDisplay::Splash(void) { - - if (splash_font < 0) return; - - if (ep_mode) { - Updateframe(); - 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) { - - if (bpp != 16 || interface == _UDSP_RGB) { - // just save params or update frame - if (!x0 && !y0 && !x1 && !y1) { - if (!ep_mode) { - Updateframe(); - } - } else { - seta_xp1 = x0; - seta_xp2 = x1; - seta_yp1 = y0; - seta_yp2 = y1; - // Serial.printf("xp1=%d xp2=%d yp1=%d yp2=%d\n", seta_xp1, seta_xp2, seta_yp1, seta_yp2); - } - return; - } - - if (interface == _UDSP_RGB) { - return; - } - - if (!x0 && !y0 && !x1 && !y1) { - SPI_CS_HIGH - SPI_END_TRANSACTION - } else { - SPI_BEGIN_TRANSACTION - SPI_CS_LOW - setAddrWindow_int(x0, y0, x1 - x0, y1 - y0 ); - } -} - -#define udisp_swap(a, b) (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b))) ///< No-temp-var swap operation - -void uDisplay::setAddrWindow_int(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { - - if (interface == _UDSP_RGB) { - return; - } - - x += x_addr_offs[cur_rot]; - y += y_addr_offs[cur_rot]; - - if (sa_mode != 8) { - uint32_t xa = ((uint32_t)x << 16) | (x + w - 1); - uint32_t ya = ((uint32_t)y << 16) | (y + h - 1); - - ulcd_command(saw_1); - ulcd_data32(xa); - - ulcd_command(saw_2); - ulcd_data32(ya); - - if (saw_3 != 0xff) { - ulcd_command(saw_3); // write to RAM - } - } else { - uint16_t x2 = x + w - 1, - y2 = y + h - 1; - - if (cur_rot & 1) { // Vertical address increment mode - udisp_swap(x,y); - udisp_swap(x2,y2); - } - ulcd_command(saw_1); - if (allcmd_mode) { - ulcd_data8(x); - ulcd_data8(x2); - } else { - ulcd_command(x); - ulcd_command(x2); - } - ulcd_command(saw_2); - if (allcmd_mode) { - ulcd_data8(y); - ulcd_data8(y2); - } else { - ulcd_command(y); - ulcd_command(y2); - } - if (saw_3 != 0xff) { - ulcd_command(saw_3); // write to RAM - } - } -} - -#define RGB16_TO_MONO 0x8410 -#define RGB16_SWAP_TO_MONO 0x1084 -// #define CNV_B1_OR ((0x10<<11) | (0x20<<5) | 0x10) -// static inline uint8_t ulv_color_to1(uint16_t color) { -// if (color & CNV_B1_OR) { -// return 1; -// } -// else { -// return 0; -// } -/* -// this needs optimization - if (((color>>11) & 0x10) || ((color>>5) & 0x20) || (color & 0x10)) { - return 1; - } - else { - return 0; - }*/ -// } - -// convert to mono, these are framebuffer based -void uDisplay::pushColorsMono(uint16_t *data, uint16_t len, bool rgb16_swap) { - // pixel is white if at least one of the 3 components is above 50% - // this is tested with a simple mask, swapped if needed - uint16_t rgb16_to_mono_mask = rgb16_swap ? RGB16_SWAP_TO_MONO : RGB16_TO_MONO; - - for (uint32_t y = seta_yp1; y < seta_yp2; y++) { - seta_yp1++; - if (lvgl_param.invert_bw) { - for (uint32_t x = seta_xp1; x < seta_xp2; x++) { - uint16_t color = *data++; - if (bpp == 1) color = (color & rgb16_to_mono_mask) ? 0 : 1; - drawPixel(x, y, color); // todo - inline the method to save speed - len--; - if (!len) return; // failsafe - exist if len (pixel number) is exhausted - } - } else { - for (uint32_t x = seta_xp1; x < seta_xp2; x++) { - uint16_t color = *data++; - if (bpp == 1) color = (color & rgb16_to_mono_mask) ? 1 : 0; - drawPixel(x, y, color); // todo - inline the method to save speed - len--; - if (!len) return; // failsafe - exist if len (pixel number) is exhausted - } - } - } -} - -// swap high low byte -static inline void lvgl_color_swap(uint16_t *data, uint16_t len) { for (uint32_t i = 0; i < len; i++) (data[i] = data[i] << 8 | data[i] >> 8); } - -void uDisplay::pushColors(uint16_t *data, uint16_t len, boolean not_swapped) { - - if (lvgl_param.swap_color) { - not_swapped = !not_swapped; - } - - //Serial.printf("push %x - %d - %d - %d\n", (uint32_t)data, len, not_swapped, lvgl_param.data); - - // Isolating _UDSP_RGB to increase code sharing - // - // Use ESP-IDF LCD driver to push colors and rely on the following assumptions: - // * bytes swapping is already handled in the driver configuration (see uDisplay::Init()), - // * pushColors() is only called with not_swapped equals true, - // * cache flushing is done by the LCD driver. - if (interface == _UDSP_RGB) { -#ifdef USE_ESP32_S3 - if (!not_swapped) { - // internal error -> write error message but continue (with possibly wrong colors) - AddLog(LOG_LEVEL_ERROR, PSTR("DSP: Unexpected byte-swapping requested in pushColors()")); - } - - // check that bytes count matches the size of area, and remove from inner loop - if ((seta_yp2 - seta_yp1) * (seta_xp2 - seta_xp2) > len) { return; } - - esp_lcd_panel_draw_bitmap(_panel_handle, seta_xp1, seta_yp1, seta_xp2, seta_yp2, (void *)data); -#endif - return; - } - - - if (not_swapped == false) { - // called from LVGL bytes are swapped - if (bpp != 16) { - // lvgl_color_swap(data, len); -- no need to swap anymore, we have inverted the mask - pushColorsMono(data, len, true); - return; - } - - if ( (col_mode != 18) && (spi_dc >= 0) && (spi_nr <= 2) ) { - // special version 8 bit spi I or II -#ifdef ESP8266 - lvgl_color_swap(data, len); - while (len--) { - uspi->write(*data++); - } -#else - if (lvgl_param.use_dma) { - pushPixelsDMA(data, len ); - } else { - uspi->writeBytes((uint8_t*)data, len * 2); - } -#endif - } else { - -#ifdef ESP32 - if ( (col_mode == 18) && (spi_dc >= 0) && (spi_nr <= 2) ) { - uint8_t *line = (uint8_t*)malloc(len * 3); - uint8_t *lp = line; - if (line) { - uint16_t color; - for (uint32_t cnt = 0; cnt < len; cnt++) { - color = *data++; - color = (color << 8) | (color >> 8); - uint8_t r = (color & 0xF800) >> 11; - uint8_t g = (color & 0x07E0) >> 5; - uint8_t b = color & 0x001F; - r = (r * 255) / 31; - g = (g * 255) / 63; - b = (b * 255) / 31; - *lp++ = r; - *lp++ = g; - *lp++ = b; - } - - if (lvgl_param.use_dma) { - pushPixels3DMA(line, len ); - } else { - uspi->writeBytes(line, len * 3); - } - free(line); - } - - } else { - // 9 bit and others - if (interface == _UDSP_PAR8 || interface == _UDSP_PAR16) { - #ifdef USE_ESP32_S3 - pb_pushPixels(data, len, true, false); - #endif // USE_ESP32_S3 - } else { - lvgl_color_swap(data, len); - while (len--) { - WriteColor(*data++); - } - } - } -#endif // ESP32 - -#ifdef ESP8266 - lvgl_color_swap(data, len); - while (len--) { - WriteColor(*data++); - } -#endif - } - } else { - // called from displaytext, no byte swap, currently no dma here - - if (bpp != 16) { - pushColorsMono(data, len); - return; - } - if ( (col_mode != 18) && (spi_dc >= 0) && (spi_nr <= 2) ) { - // special version 8 bit spi I or II - #ifdef ESP8266 - while (len--) { - //uspi->write(*data++); - WriteColor(*data++); - } - #else - uspi->writePixels(data, len * 2); - #endif - } else { - // 9 bit and others - if (interface == _UDSP_PAR8 || interface == _UDSP_PAR16) { -#ifdef USE_ESP32_S3 - pb_pushPixels(data, len, false, false); -#endif // USE_ESP32_S3 - } else { - while (len--) { - WriteColor(*data++); - } - } - } - } -} - -void uDisplay::WriteColor(uint16_t color) { - - if (col_mode == 18) { - uint8_t r = (color & 0xF800) >> 11; - uint8_t g = (color & 0x07E0) >> 5; - uint8_t b = color & 0x001F; - - r = (r * 255) / 31; - g = (g * 255) / 63; - b = (b * 255) / 31; - - ulcd_data8(r); - ulcd_data8(g); - ulcd_data8(b); - } else { - ulcd_data16(color); - } -} - -#ifdef USE_ESP32_S3 -void uDisplay::drawPixel_RGB(int16_t x, int16_t y, uint16_t color) { -int16_t w = _width, h = _height; - - if ((x < 0) || (x >= w) || (y < 0) || (y >= h)) { - return; - } - - // check rotation, move pixel around if necessary - switch (cur_rot) { - case 1: - renderer_swap(w, h); - renderer_swap(x, y); - x = w - x - 1; - break; - case 2: - x = w - x - 1; - y = h - y - 1; - break; - case 3: - renderer_swap(w, h); - renderer_swap(x, y); - y = h - y - 1; - break; - } - - uint16_t *fb = rgb_fb; - fb += (int32_t)y * w; - fb += x; - *fb = color; - Cache_WriteBack_Addr((uint32_t)fb, 2); - -} -#endif // USE_ESP32_S3 - -void uDisplay::drawPixel(int16_t x, int16_t y, uint16_t color) { - -#ifdef USE_ESP32_S3 - if (interface == _UDSP_RGB) { - drawPixel_RGB(x, y, color); - return; - } -#endif - - if (ep_mode) { - drawPixel_EPD(x, y, color); - return; - } - - if (framebuffer) { - Renderer::drawPixel(x, y, color); - return; - } - - if ((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) return; - - - SPI_BEGIN_TRANSACTION - - SPI_CS_LOW - - setAddrWindow_int(x, y, 1, 1); - - WriteColor(color); - - SPI_CS_HIGH - - SPI_END_TRANSACTION -} - -void uDisplay::setRotation(uint8_t rotation) { - cur_rot = rotation; - - if (framebuffer) { - Renderer::setRotation(cur_rot); - return; - } - - if (interface == _UDSP_SPI || interface == _UDSP_PAR8 || interface == _UDSP_PAR16) { - - if (ep_mode) { - Renderer::setRotation(cur_rot); - return; - } - SPI_BEGIN_TRANSACTION - SPI_CS_LOW - ulcd_command(madctrl); - - if (!allcmd_mode) { - ulcd_data8(rot[cur_rot]); - } else { - ulcd_command(rot[cur_rot]); - } - - if ((sa_mode == 8) && !allcmd_mode) { - ulcd_command(startline); - ulcd_data8((cur_rot < 2) ? height() : 0); - } - - SPI_CS_HIGH - SPI_END_TRANSACTION - } - switch (rotation) { - case 0: - _width = gxs; - _height = gys; - break; - case 1: - _width = gys; - _height = gxs; - break; - case 2: - _width = gxs; - _height = gys; - break; - case 3: - _width = gys; - _height = gxs; - break; - } - -#ifdef USE_ESP32_S3 - if (interface == _UDSP_RGB) { - // utilize the ESP-IDF LCD driver's support for display rotation: - // mirror x axis for rotation 1 and 2, mirror y axis for rotation 2 and 3 - esp_lcd_panel_mirror(_panel_handle, rotation == 1 || rotation == 2, rotation & 2); - // swap x/y for rotation 1 and 3 - esp_lcd_panel_swap_xy(_panel_handle, rotation & 1); - } -#endif // USE_ESP32_S3 -} - -void udisp_bpwr(uint8_t on); - -void uDisplay::DisplayOnff(int8_t on) { - - if (ep_mode) { - return; - } - - if (pwr_cbp) { - pwr_cbp(on); - } - -#define AW_PWMRES 1024 - - if (interface == _UDSP_I2C) { - if (on) { - i2c_command(dsp_on); - } else { - i2c_command(dsp_off); - } - } else { - if (on) { - if (dsp_on != 0xff) ulcd_command_one(dsp_on); - if (bpanel >= 0) { -#ifdef ESP32 - if (!bpmode) { - analogWrite(bpanel, dimmer10_gamma); - } else { - analogWrite(bpanel, AW_PWMRES - dimmer10_gamma); - } -#else - if (!bpmode) { - digitalWrite(bpanel, HIGH); - } else { - digitalWrite(bpanel, LOW); - } -#endif - } - - } else { - if (dsp_off != 0xff) ulcd_command_one(dsp_off); - if (bpanel >= 0) { -#ifdef ESP32 - if (!bpmode) { - analogWrite(bpanel, 0); - } else { - analogWrite(bpanel, AW_PWMRES - 1); - } -#else - if (!bpmode) { - digitalWrite(bpanel, LOW); - } else { - digitalWrite(bpanel, HIGH); - } -#endif - } - } - } -} - -void uDisplay::invertDisplay(boolean i) { - - if (ep_mode) { - return; - } - - if (interface == _UDSP_SPI || interface == _UDSP_PAR8 || interface == _UDSP_PAR16) { - if (i) { - ulcd_command_one(inv_on); - } else { - ulcd_command_one(inv_off); - } - } - if (interface == _UDSP_I2C) { - if (i) { - i2c_command(inv_on); - } else { - i2c_command(inv_off); - } - } -} - -void udisp_dimm(uint8_t dim); - -// input value is 0..15 -// void uDisplay::dim(uint8_t dim) { -// dim8(((uint32_t)dim * 255) / 15); -// } - -// dim is 0..255 -void uDisplay::dim10(uint8_t dim, uint16_t dim_gamma) { // dimmer with 8 bits resolution, 0..255. Gamma correction must be done by caller - dimmer8 = dim; - dimmer10_gamma = dim_gamma; - if (ep_mode) { - return; - } - -#ifdef ESP32 // TODO should we also add a ESP8266 version for bpanel? - if (bpanel >= 0) { // is the BaclPanel GPIO configured - if (!bpmode) { - analogWrite(bpanel, dimmer10_gamma); - } else { - analogWrite(bpanel, AW_PWMRES - dimmer10_gamma); - } - - // ledcWrite(ESP32_PWM_CHANNEL, dimmer8_gamma); - } else if (dim_cbp) { - dim_cbp(dim); - } -#endif - if (interface == _UDSP_SPI) { - if (dim_op != 0xff) { // send SPI command if dim configured - SPI_BEGIN_TRANSACTION - SPI_CS_LOW - ulcd_command(dim_op); - ulcd_data8(dimmer8); - SPI_CS_HIGH - SPI_END_TRANSACTION - } - } -} - -// the cases are PSEUDO_OPCODES from MODULE_DESCRIPTOR -// and may be expanded with more opcodes -void uDisplay::TS_RotConvert(int16_t *x, int16_t *y) { - int16_t temp; - - if (rot_t[cur_rot] & 0x80) { - temp = *y; - *y = *x; - *x = temp; - } - - if (rotmap_xmin >= 0) { - *y = map(*y, rotmap_ymin, rotmap_ymax, 0, gys); - *x = map(*x, rotmap_xmin, rotmap_xmax, 0, gxs); - *x = constrain(*x, 0, gxs); - *y = constrain(*y, 0, gys); - } -// *x = constrain(*x, 0, gxs); -// *y = constrain(*y, 0, gys); - - //Serial.printf("rot 1 %d - %d\n",*x,*y ); - - switch (rot_t[cur_rot] & 0xf) { - case 0: - break; - case 1: - temp = *y; - *y = height() - *x; - *x = temp; - break; - case 2: - *x = width() - *x; - *y = height() - *y; - break; - case 3: - temp = *y; - *y = *x; - *x = width() - temp; - break; - case 4: - *x = width() - *x; - break; - case 5: - *y = height() - *y; - break; - } - - //Serial.printf("rot 2 %d - %d\n",*x,*y ); -} - -uint8_t uDisplay::strlen_ln(char *str) { - for (uint32_t cnt = 0; cnt < 256; cnt++) { - if (!str[cnt] || str[cnt] == '\n' || str[cnt] == ' ') return cnt; - } - return 0; -} - -char *uDisplay::devname(void) { - return dname; -} - - - -#ifdef USE_UNIVERSAL_TOUCH - -float CharToFloat(const char *str); - -uint32_t uDisplay::ut_par(char **lp, uint32_t mode) { - char *cp = *lp; - while (*cp != ' ') { - if (!cp) break; - cp++; - } - cp++; - uint32_t result; - if (!mode) { - // hex - result = strtol(cp, &cp, 16); - } else if (mode == 1) { - // word - result = strtol(cp, &cp, 10); - } else { - // float as 32bit integer - float fval = CharToFloat(cp); - result = *(uint32_t*)&fval; - while (*cp) { - if (*cp == ' ' || *cp =='\n') { - break; - } - cp++; - } - } - *lp = cp; - return result; -} - -// translate pseudo opcodes to tokens -void uDisplay::ut_trans(char **sp, uint8_t **code) { - char *cp = *sp; - uint16_t wval; - uint8_t tmp_code[64]; - uint8_t *ut_code = tmp_code; - while (*cp) { - if (*cp == ':' || *cp == '#') { - break; - } - if (*cp == ';') { - // skip comment line - while (*cp) { - if (*cp == '\n') { - cp++; - break; - } - cp++; - } - } - if (!strncmp(cp, "RDWM", 4)) { - // read word many - *ut_code++ = UT_RDWM; - wval = ut_par(&cp, 0); - *ut_code++ = wval>>8; - *ut_code++ = wval; - wval = ut_par(&cp, 1); - if (wval > sizeof(ut_array)) { - wval = sizeof(ut_array); - } - *ut_code++ = wval; - } else if (!strncmp(cp, "RDW", 3)) { - // read word one - *ut_code++ = UT_RDW; - wval = ut_par(&cp, 0); - *ut_code++ = wval>>8; - *ut_code++ = wval; - } else if (!strncmp(cp, "RDM", 3)) { - // read many - *ut_code++ = UT_RDM; - *ut_code++ = ut_par(&cp, 0); - wval = ut_par(&cp, 1); - if (wval > sizeof(ut_array)) { - wval = sizeof(ut_array); - } - *ut_code++ = wval; - } else if (!strncmp(cp, "RD", 2)) { - // read one - *ut_code++ = UT_RD; - *ut_code++ = ut_par(&cp, 0); - } else if (!strncmp(cp, "CPR", 3)) { - // cmp and set - *ut_code++ = UT_CPR; - *ut_code++ = ut_par(&cp, 0); - } else if (!strncmp(cp, "CPM", 3)) { - // cmp multiple and set - *ut_code++ = UT_CPM; - uint8_t num = ut_par(&cp, 0); - *ut_code++ = num; - for (uint32_t cnt = 0; cnt < num; cnt++) { - *ut_code++ = ut_par(&cp, 0); - } - } else if (!strncmp(cp, "CP", 2)) { - // cmp and set - *ut_code++ = UT_CP; - *ut_code++ = ut_par(&cp, 0); - } else if (!strncmp(cp, "RTF", 3)) { - // return when false - *ut_code++ = UT_RTF; - } else if (!strncmp(cp, "RTT", 3)) { - // return when true - *ut_code++ = UT_RTT; - } else if (!strncmp(cp, "MVB", 3)) { - // move - *ut_code++ = UT_MVB; - *ut_code++ = ut_par(&cp, 1); - *ut_code++ = ut_par(&cp, 1); - } else if (!strncmp(cp, "MV", 2)) { - // move - *ut_code++ = UT_MV; - *ut_code++ = ut_par(&cp, 1); - *ut_code++ = ut_par(&cp, 1); - } else if (!strncmp(cp, "RT", 2)) { - // return status - *ut_code++ = UT_RT; - } else if (!strncmp(cp, "WRW", 3)) { - *ut_code++ = UT_WRW; - wval = ut_par(&cp, 0); - *ut_code++ = wval>>8; - *ut_code++ = wval; - wval = ut_par(&cp, 0); - *ut_code++ = wval; - } else if (!strncmp(cp, "WR", 2)) { - *ut_code++ = UT_WR; - wval = ut_par(&cp, 0); - *ut_code++ = wval; - wval = ut_par(&cp, 0); - *ut_code++ = wval; - } else if (!strncmp(cp, "AND", 3)) { - *ut_code++ = UT_AND; - wval = ut_par(&cp, 0); - *ut_code++ = wval >> 8; - *ut_code++ = wval; - } else if (!strncmp(cp, "SCL", 3)) { - *ut_code++ = UT_SCALE; - wval = ut_par(&cp, 1); - *ut_code++ = wval >> 8; - *ut_code++ = wval; - uint32_t lval = ut_par(&cp, 2); - *ut_code++ = lval >> 24; - *ut_code++ = lval >> 16; - *ut_code++ = lval >> 8; - *ut_code++ = lval; - } else if (!strncmp(cp, "LIM", 3)) { - *ut_code++ = UT_LIM; - wval = ut_par(&cp, 1); - *ut_code++ = wval >> 8; - *ut_code++ = wval; - } else if (!strncmp(cp, "GSRT", 4)) { - *ut_code++ = UT_GSRT; - wval = ut_par(&cp, 1); - *ut_code++ = wval >> 8; - *ut_code++ = wval; - } else if (!strncmp(cp, "XPT", 3)) { - *ut_code++ = UT_XPT; - wval = ut_par(&cp, 1); - *ut_code++ = wval >> 8; - *ut_code++ = wval; - } else if (!strncmp(cp, "DBG", 3)) { - *ut_code++ = UT_DBG; - wval = ut_par(&cp, 1); - *ut_code++ = wval; - } - cp++; - } - *ut_code++ = UT_END; - *sp = cp - 1; - uint16_t memsize = (uint32_t)ut_code - (uint32_t)tmp_code; - // allocate memory - //AddLog(LOG_LEVEL_INFO, PSTR("UT-code: %d bytes"),memsize); -#ifdef UDSP_DEBUG - Serial.printf("Utouch code size : %d\n", memsize); -#endif - uint8_t *mp = (uint8_t*)malloc(memsize + 2); - if (mp) { - memmove(mp, tmp_code, memsize); - *code = mp; - } -} - -uint8_t *uDisplay::ut_rd(uint8_t *iop, uint32_t len, uint32_t amode) { - if (ut_wire) { - // i2c mode - ut_wire->beginTransmission(ut_i2caddr); - ut_wire->write(*iop++); - if (amode == 2) { - ut_wire->write(*iop++); - } - ut_wire->endTransmission(false); - if (len > 1) { - len = *iop++; - } - ut_wire->requestFrom(ut_i2caddr, (size_t)len); - uint8_t index = 0; - while (ut_wire->available()) { - ut_array[index++] = ut_wire->read(); - } - } else { - // spi mode - if (amode == 1) { - uint16_t val = *iop++; - uint16_t len = *iop++; - if (ut_spi) { - digitalWrite(ut_spi_cs, LOW); - ut_spi->beginTransaction(ut_spiSettings); - ut_spi->transfer(val); - val = ut_spi->transfer16(0); - ut_spi->endTransaction(); - ut_array[len] = val << 8; - ut_array[len + 1] = val; - digitalWrite(ut_spi_cs, HIGH); - } - } - } - return iop; -} - -uint8_t *uDisplay::ut_wr(uint8_t *iop, uint32_t amode) { - if (ut_wire) { - // i2c mode - ut_wire->beginTransmission(ut_i2caddr); - ut_wire->write(*iop++); - if (amode == 2) { - ut_wire->write(*iop++); - } - ut_wire->write(*iop++); - ut_wire->endTransmission(true); - } else { - // spi mode - } - return iop; -} - - -int16_t uDisplay::besttwoavg( int16_t x , int16_t y , int16_t z ) { - int16_t da, db, dc; - int16_t reta = 0; - if ( x > y ) da = x - y; else da = y - x; - if ( x > z ) db = x - z; else db = z - x; - if ( z > y ) dc = z - y; else dc = y - z; - - if ( da <= db && da <= dc ) reta = (x + y) >> 1; - else if ( db <= da && db <= dc ) reta = (x + z) >> 1; - else reta = (y + z) >> 1; - - return (reta); -} - -uint16_t uDisplay::ut_XPT2046(uint16_t z_th) { - uint16_t result = 0; - if (ut_spi) { - int16_t data[6]; - ut_spi->beginTransaction(ut_spiSettings); - digitalWrite(ut_spi_cs, LOW); - ut_spi->transfer(0xB1 /* Z1 */); - int16_t z1 = ut_spi->transfer16(0xC1 /* Z2 */) >> 3; - int16_t z = z1 + 4095; - int16_t z2 = ut_spi->transfer16(0x91 /* X */) >> 3; - z -= z2; - if (z >= z_th) { - ut_spi->transfer16(0x91 /* X */); // dummy X measure, 1st is always noisy - data[0] = ut_spi->transfer16(0xD1 /* Y */) >> 3; - data[1] = ut_spi->transfer16(0x91 /* X */) >> 3; // make 3 x-y measurements - data[2] = ut_spi->transfer16(0xD1 /* Y */) >> 3; - data[3] = ut_spi->transfer16(0x91 /* X */) >> 3; - result = 1; - } - else { - data[0] = data[1] = data[2] = data[3] = 0; - } - data[4] = ut_spi->transfer16(0xD0 /* Y */) >> 3; // Last Y touch power down - data[5] = ut_spi->transfer16(0) >> 3; - digitalWrite(ut_spi_cs, HIGH); - ut_spi->endTransaction(); - - uint16_t x = besttwoavg( data[0], data[2], data[4] ); - uint16_t y = besttwoavg( data[1], data[3], data[5] ); - - ut_array[0] = x >> 8; - ut_array[1] = x; - ut_array[2] = y >> 8; - ut_array[3] = y; - } - return result; -} - -int16_t uDisplay::ut_execute(uint8_t *ut_code) { -int16_t result = 0; -uint8_t iob, len; -uint16_t wval; - - while (*ut_code != UT_END) { - iob = *ut_code++; - switch (iob) { - case UT_RD: - // read 1 byte - ut_code = ut_rd(ut_code, 1, 1); - break; - - case UT_RDM: - // read multiple bytes - ut_code = ut_rd(ut_code, 2, 1); - break; - - case UT_RDW: - // read 1 byte - ut_code = ut_rd(ut_code, 1, 2); - break; - - case UT_RDWM: - // read multiple bytes - ut_code = ut_rd(ut_code, 2, 2); - break; - - case UT_WR: - ut_code = ut_wr(ut_code, 1); - break; - - case UT_WRW: - ut_code = ut_wr(ut_code, 2); - break; - - case UT_CP: - // compare - iob = *ut_code++; - result = (iob == ut_array[0]); - break; - - case UT_CPM: - // compare multiple - len = *ut_code++; - result = 0; - for (uint32_t cnt = 0; cnt < len; cnt++) { - iob = *ut_code++; - result |= (iob == ut_array[0]); - } - break; - - case UT_CPR: - // compare - iob = *ut_code++; - result = (iob == result); - break; - - case UT_RTF: - // return when false - if (result == 0) { - return false; - } - break; - - case UT_RTT: - // return when true - if (result > 0) { - return false; - } - break; - - case UT_MVB: - // move byte from index to high or low result - wval = *ut_code++; - iob = *ut_code++; - if (wval == 0) { - result &= 0xff00; - result |= ut_array[iob]; - } else { - result &= 0x00ff; - result |= (ut_array[iob] << 8); - } - break; - - case UT_MV: - // move - // source - result = *ut_code++; - iob = *ut_code++; - if (iob == 1) { - result = ut_array[result]; - } else if (iob == 2) { - iob = result; - result = ut_array[iob] << 8; - result |= ut_array[iob + 1]; - } else { - iob = result; - result = ut_array[iob + 1] << 8; - result |= ut_array[iob]; - } - result &= 0xfff; - break; - - case UT_AND: - // and - wval = *ut_code++ << 8; - wval |= *ut_code++; - result &= wval; - break; - - case UT_SCALE: - { - wval = *ut_code++ << 8; - wval |= *ut_code++; - result -= wval; - uint32_t lval = (uint32_t)*ut_code++ << 24; - lval |= (uint32_t)*ut_code++ << 16; - lval |= (uint32_t)*ut_code++ << 8; - lval |= (uint32_t)*ut_code++; - float fval = *(float*)&lval; - fval *= (float)result; - result = fval; - } - break; - - case UT_LIM: - wval = *ut_code++ << 8; - wval |= *ut_code++; - if (result > wval) { - result = wval; - } - break; - - case UT_RT: - // result - return result; - break; - - case UT_GSRT: -#ifdef USE_ESP32_S3 - { uint32_t val = get_sr_touch(SIMPLERS_XP, SIMPLERS_XM, SIMPLERS_YP, SIMPLERS_YM); - if (val == 0) { - return false; - } - uint16_t xp = val >> 16; - uint16_t yp = val; - - wval = *ut_code++ << 8; - wval |= *ut_code++; - if (xp > wval && yp > wval) { - ut_array[0] = val >> 24; - ut_array[1] = val >> 16; - ut_array[2] = val >> 8; - ut_array[3] = val; - return true; - } - return false; - } -#endif // USE_ESP32_S3 - break; - - case UT_XPT: - wval = *ut_code++ << 8; - wval |= *ut_code++; - result = ut_XPT2046(wval); - break; - - case UT_DBG: - // debug show result - //Serial.printf("UTDBG: %d\n", result); - wval = *ut_code++; - AddLog(LOG_LEVEL_INFO, PSTR("UTDBG %d: %02x : %02x,%02x,%02x,%02x"), wval, result, ut_array[0], ut_array[1], ut_array[2], ut_array[3]); - break; - - case UT_END: - break; - } - } - return result; -} -#endif // USE_UNIVERSAL_TOUCH - -uint32_t uDisplay::str2c(char **sp, char *vp, uint32_t len) { - char *lp = *sp; - if (len) len--; - char *cp = strchr(lp, ','); - if (cp) { - while (1) { - if (*lp == ',') { - *vp = 0; - *sp = lp + 1; - return 0; - } - if (len) { - *vp++ = *lp++; - len--; - } else { - lp++; - } - } - } else { - uint16_t slen = strlen(lp); - if (slen) { - strlcpy(vp, *sp, len); - *sp = lp + slen; - return 0; - } - } - return 1; -} - -int32_t uDisplay::next_val(char **sp) { - char ibuff[16]; - if (!str2c(sp, ibuff, sizeof(ibuff))) { - return atoi(ibuff); - } - return 0xff; -} - -uint32_t uDisplay::next_hex(char **sp) { - char ibuff[16]; - if (!str2c(sp, ibuff, sizeof(ibuff))) { - return strtol(ibuff, 0, 16); - } - return 0xff; -} - -#ifdef ESP32 -#include "soc/spi_reg.h" -#include "soc/spi_struct.h" -#include "esp32-hal-spi.h" -#include "esp32-hal.h" -#include "soc/spi_struct.h" - -// since ardunio transferBits is completely disfunctional -// we use our own hardware driver for 9 bit spi -void uDisplay::hw_write9(uint8_t val, uint8_t dc) { - - if (spi_dc < -1) { - // RA8876 mode - if (!dc) { - uspi->write(RA8876_CMD_WRITE); - uspi->write(val); - } else { - uspi->write(RA8876_DATA_WRITE); - uspi->write(val); - } - } else { - uint32_t regvalue = val >> 1; - if (dc) regvalue |= 0x80; - else regvalue &= 0x7f; - if (val & 1) regvalue |= 0x8000; - - REG_SET_BIT(SPI_USER_REG(3), SPI_USR_MOSI); - REG_WRITE(SPI_MOSI_DLEN_REG(3), 9 - 1); - uint32_t *dp = (uint32_t*)SPI_W0_REG(3); - *dp = regvalue; - REG_SET_BIT(SPI_CMD_REG(3), SPI_USR); - while (REG_GET_FIELD(SPI_CMD_REG(3), SPI_USR)); - } -} - -#else -#include "spi_register.h" -void uDisplay::hw_write9(uint8_t val, uint8_t dc) { - - if (spi_dc < -1) { - // RA8876 mode - if (!dc) { - uspi->write(RA8876_CMD_WRITE); - uspi->write(val); - } else { - uspi->write(RA8876_DATA_WRITE); - uspi->write(val); - } - } else { - uint32_t regvalue; - uint8_t bytetemp; - if (!dc) { - bytetemp = (val>> 1) & 0x7f; - } else { - bytetemp = (val >> 1) | 0x80; - } - regvalue = ((8 & SPI_USR_COMMAND_BITLEN) << SPI_USR_COMMAND_BITLEN_S) | ((uint32)bytetemp); //configure transmission variable,9bit transmission length and first 8 command bit - if (val & 0x01) regvalue |= BIT15; //write the 9th bit - while (READ_PERI_REG(SPI_CMD(1)) & SPI_USR); //waiting for spi module available - WRITE_PERI_REG(SPI_USER2(1), regvalue); //write command and command length into spi reg - SET_PERI_REG_MASK(SPI_CMD(1), SPI_USR); //transmission start - } -} -#endif - -#define USECACHE ICACHE_RAM_ATTR - -// slow software spi needed for displays with max 10 Mhz clck - -void USECACHE uDisplay::write8(uint8_t val) { - for (uint8_t bit = 0x80; bit; bit >>= 1) { - GPIO_CLR(spi_clk); - if (val & bit) GPIO_SET(spi_mosi); - else GPIO_CLR(spi_mosi); - GPIO_SET(spi_clk); - } -} - -uint8_t uDisplay::writeReg16(uint8_t reg, uint16_t wval) { - hw_write9(reg, 0); - hw_write9(wval, 1); - hw_write9(reg + 1, 0); - hw_write9(wval >> 8, 1); - return 0; -} - -uint8_t uDisplay::readData(void) { - uspi->write(RA8876_DATA_READ); - uint8_t val = uspi->transfer(0); - return val; -} - -uint8_t uDisplay::readStatus(void) { - uspi->write(RA8876_STATUS_READ); - uint8_t val = uspi->transfer(0); - return val; -} - -void uDisplay::write8_slow(uint8_t val) { - for (uint8_t bit = 0x80; bit; bit >>= 1) { - GPIO_CLR_SLOW(spi_clk); - if (val & bit) GPIO_SET_SLOW(spi_mosi); - else GPIO_CLR_SLOW(spi_mosi); - GPIO_SET_SLOW(spi_clk); - } -} - -void USECACHE uDisplay::write9(uint8_t val, uint8_t dc) { - - GPIO_CLR(spi_clk); - if (dc) GPIO_SET(spi_mosi); - else GPIO_CLR(spi_mosi); - GPIO_SET(spi_clk); - - for (uint8_t bit = 0x80; bit; bit >>= 1) { - GPIO_CLR(spi_clk); - if (val & bit) GPIO_SET(spi_mosi); - else GPIO_CLR(spi_mosi); - GPIO_SET(spi_clk); - } -} - -void uDisplay::write9_slow(uint8_t val, uint8_t dc) { - - GPIO_CLR_SLOW(spi_clk); - if (dc) GPIO_SET_SLOW(spi_mosi); - else GPIO_CLR_SLOW(spi_mosi); - GPIO_SET_SLOW(spi_clk); - - for (uint8_t bit = 0x80; bit; bit >>= 1) { - GPIO_CLR_SLOW(spi_clk); - if (val & bit) GPIO_SET_SLOW(spi_mosi); - else GPIO_CLR_SLOW(spi_mosi); - GPIO_SET_SLOW(spi_clk); - } -} - -void USECACHE uDisplay::write16(uint16_t val) { - for (uint16_t bit = 0x8000; bit; bit >>= 1) { - GPIO_CLR(spi_clk); - if (val & bit) GPIO_SET(spi_mosi); - else GPIO_CLR(spi_mosi); - GPIO_SET(spi_clk); - } -} - -void USECACHE uDisplay::write32(uint32_t val) { - for (uint32_t bit = 0x80000000; bit; bit >>= 1) { - GPIO_CLR(spi_clk); - if (val & bit) GPIO_SET(spi_mosi); - else GPIO_CLR(spi_mosi); - 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 - ulcd_data8(val); - SPI_CS_HIGH - SPI_END_TRANSACTION -} - -void uDisplay::spi_command_EPD(uint8_t val) { - SPI_BEGIN_TRANSACTION - SPI_CS_LOW - ulcd_command(val); - SPI_CS_HIGH - SPI_END_TRANSACTION -} - -void uDisplay::Init_EPD(int8_t p) { - if (p == DISPLAY_INIT_PARTIAL) { - if (lutpsize) { - SetLut(lut_partial); - } - } else { - if (lutfsize) { - SetLut(lut_full); - } - if (lut_cnt[0]) { - SetLuts(); - } - } - if (ep_mode == 1) { - ClearFrameMemory(0xFF); - Updateframe_EPD(); - } else if (ep_mode == 3) { - ClearFrameMemory(0xFF); - Updateframe_EPD(); - } else { - ClearFrame_42(); - } - if (p == DISPLAY_INIT_PARTIAL) { - delay_sync(lutptime * 10); - } else { - delay_sync(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::SetLuts(void) { - uint8_t index, count; - 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[index][count]); - } - } -} - -void uDisplay::DisplayFrame_42(void) { - - 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(int i = 0; i < gxs / 8 * gys; i++) { - spi_data8_EPD(framebuffer[i]^0xff); - } - delay(2); - - SetLuts(); - - spi_command_EPD(saw_3); - delay_sync(100); - -#ifdef UDSP_DEBUG - Serial.printf("EPD Diplayframe\n"); -#endif -} - - - - -void uDisplay::ClearFrame_42(void) { - spi_command_EPD(saw_1); - 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 < gys; j++) { - for (uint16_t i = 0; i < gxs; i++) { - spi_data8_EPD(0xFF); - } - } - - spi_command_EPD(saw_3); - 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(lut_cmd[0]); - /* 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) { - if (ep_mode == 1 || ep_mode == 3) { - 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(); - } -} - -void uDisplay::DisplayFrame_29(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) { - int x_start1 = (x_start >> 3) & 0xFF; - int x_end1 = (x_end >> 3) & 0xFF; - int y_start1 = y_start & 0xFF; - int y_start2 = (y_start >> 8) & 0xFF; - int y_end1 = y_end & 0xFF; - int y_end2 = (y_end >> 8) & 0xFF; - /* x point must be the multiple of 8 or the last 3 bits will be ignored */ - - if (ep_mode == 3) { - spi_command_EPD(SET_RAM_X_ADDRESS_START_END_POSITION); - spi_data8_EPD(x_start1); - spi_data8_EPD(x_end1); - spi_command_EPD(SET_RAM_Y_ADDRESS_START_END_POSITION); - spi_data8_EPD(y_end1); - spi_data8_EPD(y_end2); - spi_data8_EPD(y_start1); - spi_data8_EPD(y_start2); - } else { - spi_command_EPD(SET_RAM_X_ADDRESS_START_END_POSITION); - spi_data8_EPD(x_start1); - spi_data8_EPD(x_end1); - spi_command_EPD(SET_RAM_Y_ADDRESS_START_END_POSITION); - spi_data8_EPD(y_start1); - spi_data8_EPD(y_start2); - spi_data8_EPD(y_end1); - spi_data8_EPD(y_end2); - } -} - -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) { - int x1; - int y1; - int y2; - - if (ep_mode == 3) { - x1 = (x >> 3) & 0xFF; - y--; - y1 = y & 0xFF; - y2 = (y >> 8) & 0xFF; - } else { - x1 = (x >> 3) & 0xFF; - y1 = y & 0xFF; - y2 = (y >> 8) & 0xFF; - } - - 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(x1); - spi_command_EPD(SET_RAM_Y_ADDRESS_COUNTER); - spi_data8_EPD(y1); - spi_data8_EPD(y2); -} - -#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, - 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 - -/** - * @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) { - framebuffer[(x + y * w) / 8] |= 0x80 >> (x % 8); - } else { - framebuffer[(x + y * w) / 8] &= ~(0x80 >> (x % 8)); - } - } else { - if (color) { - framebuffer[(x + y * w) / 8] &= ~(0x80 >> (x % 8)); - } else { - framebuffer[(x + y * w) / 8] |= 0x80 >> (x % 8); - } - } -} - -void uDisplay::drawPixel_EPD(int16_t x, int16_t y, uint16_t color) { - if (!framebuffer) 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++; - } -} - - -void uDisplay::beginTransaction(SPISettings s) { -#ifdef ESP32 - if (lvgl_param.use_dma) { - dmaWait(); - } -#endif - uspi->beginTransaction(s); -} - -void uDisplay::endTransaction(void) { - uspi->endTransaction(); -} - - -// ESP 32 DMA section , derived from TFT_eSPI -#ifdef ESP32 - -/*************************************************************************************** -** Function name: initDMA -** Description: Initialise the DMA engine - returns true if init OK -***************************************************************************************/ -bool uDisplay::initDMA(int32_t ctrl_cs) -{ - if (DMA_Enabled) return false; - - esp_err_t ret; - spi_bus_config_t buscfg = { - .mosi_io_num = spi_mosi, - .miso_io_num = -1, - .sclk_io_num = spi_clk, - .quadwp_io_num = -1, - .quadhd_io_num = -1, - .max_transfer_sz = width() * height() * 2 + 8, // TFT screen size - .flags = 0, - .intr_flags = 0 - }; - - spi_device_interface_config_t devcfg = { - .command_bits = 0, - .address_bits = 0, - .dummy_bits = 0, - .mode = SPI_MODE3, - .duty_cycle_pos = 0, - .cs_ena_pretrans = 0, - .cs_ena_posttrans = 0, - .clock_speed_hz = spi_speed*1000000, - .input_delay_ns = 0, - .spics_io_num = ctrl_cs, - .flags = SPI_DEVICE_NO_DUMMY, //0, - .queue_size = 1, - .pre_cb = 0, //dc_callback, //Callback to handle D/C line - .post_cb = 0 - }; - ret = spi_bus_initialize(spi_host, &buscfg, 1); - ESP_ERROR_CHECK(ret); - ret = spi_bus_add_device(spi_host, &devcfg, &dmaHAL); - ESP_ERROR_CHECK(ret); - - DMA_Enabled = true; - spiBusyCheck = 0; - return true; -} - -/*************************************************************************************** -** Function name: deInitDMA -** Description: Disconnect the DMA engine from SPI -***************************************************************************************/ -void uDisplay::deInitDMA(void) { - if (!DMA_Enabled) return; - spi_bus_remove_device(dmaHAL); - spi_bus_free(spi_host); - DMA_Enabled = false; -} - -/*************************************************************************************** -** Function name: dmaBusy -** Description: Check if DMA is busy -***************************************************************************************/ -bool uDisplay::dmaBusy(void) { - if (!DMA_Enabled || !spiBusyCheck) return false; - - spi_transaction_t *rtrans; - esp_err_t ret; - uint8_t checks = spiBusyCheck; - for (int i = 0; i < checks; ++i) { - ret = spi_device_get_trans_result(dmaHAL, &rtrans, 0); - if (ret == ESP_OK) spiBusyCheck--; - } - - //Serial.print("spiBusyCheck=");Serial.println(spiBusyCheck); - if (spiBusyCheck == 0) return false; - return true; -} - - -/*************************************************************************************** -** Function name: dmaWait -** Description: Wait until DMA is over (blocking!) -***************************************************************************************/ -void uDisplay::dmaWait(void) { - if (!DMA_Enabled || !spiBusyCheck) return; - spi_transaction_t *rtrans; - esp_err_t ret; - for (int i = 0; i < spiBusyCheck; ++i) { - ret = spi_device_get_trans_result(dmaHAL, &rtrans, portMAX_DELAY); - assert(ret == ESP_OK); - } - spiBusyCheck = 0; -} - - -/*************************************************************************************** -** Function name: pushPixelsDMA -** Description: Push pixels to TFT (len must be less than 32767) -***************************************************************************************/ -// This will byte swap the original image if setSwapBytes(true) was called by sketch. -void uDisplay::pushPixelsDMA(uint16_t* image, uint32_t len) { - - if ((len == 0) || (!DMA_Enabled)) return; - - dmaWait(); - - esp_err_t ret; - - memset(&trans, 0, sizeof(spi_transaction_t)); - - trans.user = (void *)1; - trans.tx_buffer = image; //finally send the line data - trans.length = len * 16; //Data length, in bits - trans.flags = 0; //SPI_TRANS_USE_TXDATA flag - - ret = spi_device_queue_trans(dmaHAL, &trans, portMAX_DELAY); - assert(ret == ESP_OK); - - spiBusyCheck++; - if (!lvgl_param.async_dma) { - dmaWait(); - } -} - -/*************************************************************************************** -** Function name: pushPixelsDMA -** Description: Push pixels to TFT (len must be less than 32767) -***************************************************************************************/ -// This will byte swap the original image if setSwapBytes(true) was called by sketch. -void uDisplay::pushPixels3DMA(uint8_t* image, uint32_t len) { - - if ((len == 0) || (!DMA_Enabled)) return; - - dmaWait(); - - esp_err_t ret; - - memset(&trans, 0, sizeof(spi_transaction_t)); - - trans.user = (void *)1; - trans.tx_buffer = image; //finally send the line data - trans.length = len * 24; //Data length, in bits - trans.flags = 0; //SPI_TRANS_USE_TXDATA flag - - ret = spi_device_queue_trans(dmaHAL, &trans, portMAX_DELAY); - assert(ret == ESP_OK); - - spiBusyCheck++; - if (!lvgl_param.async_dma) { - dmaWait(); - } -} - -#ifdef USE_ESP32_S3 -void uDisplay::calcClockDiv(uint32_t* div_a, uint32_t* div_b, uint32_t* div_n, uint32_t* clkcnt, uint32_t baseClock, uint32_t targetFreq) { - uint32_t diff = INT32_MAX; - *div_n = 256; - *div_a = 63; - *div_b = 62; - *clkcnt = 64; - uint32_t start_cnt = std::min(64u, (baseClock / (targetFreq * 2) + 1)); - uint32_t end_cnt = std::max(2u, baseClock / 256u / targetFreq); - if (start_cnt <= 2) { end_cnt = 1; } - for (uint32_t cnt = start_cnt; diff && cnt >= end_cnt; --cnt) - { - float fdiv = (float)baseClock / cnt / targetFreq; - uint32_t n = std::max(2u, (uint32_t)fdiv); - fdiv -= n; - - for (uint32_t a = 63; diff && a > 0; --a) - { - uint32_t b = roundf(fdiv * a); - if (a == b && n == 256) { - break; - } - uint32_t freq = baseClock / ((n * cnt) + (float)(b * cnt) / (float)a); - uint32_t d = abs((int)targetFreq - (int)freq); - if (diff <= d) { continue; } - diff = d; - *clkcnt = cnt; - *div_n = n; - *div_b = b; - *div_a = a; - if (b == 0 || a == b) { - break; - } - } - } - if (*div_a == *div_b) - { - *div_b = 0; - *div_n += 1; - } - } - -void uDisplay::_alloc_dmadesc(size_t len) { - if (_dmadesc) heap_caps_free(_dmadesc); - _dmadesc_size = len; - _dmadesc = (lldesc_t*)heap_caps_malloc(sizeof(lldesc_t) * len, MALLOC_CAP_DMA); -} - -void uDisplay::_setup_dma_desc_links(const uint8_t *data, int32_t len) { - static constexpr size_t MAX_DMA_LEN = (4096-4); -/* - if (_dmadesc_size * MAX_DMA_LEN < len) { - _alloc_dmadesc(len / MAX_DMA_LEN + 1); - } - lldesc_t *dmadesc = _dmadesc; - - while (len > MAX_DMA_LEN) { - len -= MAX_DMA_LEN; - dmadesc->buffer = (uint8_t *)data; - data += MAX_DMA_LEN; - *(uint32_t*)dmadesc = MAX_DMA_LEN | MAX_DMA_LEN << 12 | 0x80000000; - dmadesc->next = dmadesc + 1; - dmadesc++; - } - *(uint32_t*)dmadesc = ((len + 3) & ( ~3 )) | len << 12 | 0xC0000000; - dmadesc->buffer = (uint8_t *)data; - dmadesc->next = nullptr; - */ - } - -#define WAIT_LCD_NOT_BUSY while (*reg_lcd_user & LCD_CAM_LCD_START) {} - - -void uDisplay::pb_beginTransaction(void) { - auto dev = _dev; - dev->lcd_clock.val = _clock_reg_value; - // int clk_div = std::min(63u, std::max(1u, 120*1000*1000 / (_cfg.freq_write+1))); - // dev->lcd_clock.lcd_clk_sel = 2; // clock_select: 1=XTAL CLOCK / 2=240MHz / 3=160MHz - // dev->lcd_clock.lcd_clkcnt_n = clk_div; - // dev->lcd_clock.lcd_clk_equ_sysclk = 0; - // dev->lcd_clock.lcd_ck_idle_edge = true; - // dev->lcd_clock.lcd_ck_out_edge = false; - - dev->lcd_misc.val = LCD_CAM_LCD_CD_IDLE_EDGE; - // dev->lcd_misc.lcd_cd_idle_edge = 1; - // dev->lcd_misc.lcd_cd_cmd_set = 0; - // dev->lcd_misc.lcd_cd_dummy_set = 0; - // dev->lcd_misc.lcd_cd_data_set = 0; - - dev->lcd_user.val = 0; - // dev->lcd_user.lcd_byte_order = false; - // dev->lcd_user.lcd_bit_order = false; - // dev->lcd_user.lcd_8bits_order = false; - - dev->lcd_user.val = LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG; - - _cache_flip = _cache[0]; - } - -void uDisplay::pb_endTransaction(void) { - auto dev = _dev; - while (dev->lcd_user.val & LCD_CAM_LCD_START) {} -} - -void uDisplay::pb_wait(void) { - auto dev = _dev; - while (dev->lcd_user.val & LCD_CAM_LCD_START) {} -} - -bool uDisplay::pb_busy(void) { - auto dev = _dev; - return (dev->lcd_user.val & LCD_CAM_LCD_START); -} - -bool uDisplay::pb_writeCommand(uint32_t data, uint_fast8_t bit_length) { - auto dev = _dev; - auto reg_lcd_user = &(dev->lcd_user.val); - dev->lcd_misc.val = LCD_CAM_LCD_CD_IDLE_EDGE | LCD_CAM_LCD_CD_CMD_SET; - - if (interface == _UDSP_PAR8) { - // 8bit bus - auto bytes = bit_length >> 3; - do { - dev->lcd_cmd_val.lcd_cmd_value = data; - data >>= 8; - WAIT_LCD_NOT_BUSY - *reg_lcd_user = LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; - } while (--bytes); - return true; - } else { - dev->lcd_cmd_val.val = data; - WAIT_LCD_NOT_BUSY - *reg_lcd_user = LCD_CAM_LCD_2BYTE_EN | LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; - return true; - } -} - -void uDisplay::pb_writeData(uint32_t data, uint_fast8_t bit_length) { - auto dev = _dev; - auto reg_lcd_user = &(dev->lcd_user.val); - dev->lcd_misc.val = LCD_CAM_LCD_CD_IDLE_EDGE; - auto bytes = bit_length >> 3; - - if (interface == _UDSP_PAR8) { - uint8_t shift = (bytes - 1) * 8; - for (uint32_t cnt = 0; cnt < bytes; cnt++) { - dev->lcd_cmd_val.lcd_cmd_value = (data >> shift) & 0xff; - shift -= 8; - WAIT_LCD_NOT_BUSY - *reg_lcd_user = LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; - } - return; - - } else { - if (bytes == 1 || bytes == 4) { - uint8_t shift = (bytes - 1) * 8; - for (uint32_t cnt = 0; cnt < bytes; cnt++) { - dev->lcd_cmd_val.lcd_cmd_value = (data >> shift) & 0xff; - shift -= 8; - WAIT_LCD_NOT_BUSY - *reg_lcd_user = LCD_CAM_LCD_2BYTE_EN | LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; - } - return; - } - - dev->lcd_cmd_val.val = data; - WAIT_LCD_NOT_BUSY - *reg_lcd_user = LCD_CAM_LCD_2BYTE_EN | LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; - return; - } -} - -void uDisplay::pb_pushPixels(uint16_t* data, uint32_t length, bool swap_bytes, bool use_dma) { - auto dev = _dev; - auto reg_lcd_user = &(dev->lcd_user.val); - dev->lcd_misc.val = LCD_CAM_LCD_CD_IDLE_EDGE; - - if (interface == _UDSP_PAR8) { - if (swap_bytes) { - for (uint32_t cnt = 0; cnt < length; cnt++) { - dev->lcd_cmd_val.lcd_cmd_value = *data; - while (*reg_lcd_user & LCD_CAM_LCD_START) {} - *reg_lcd_user = LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; - dev->lcd_cmd_val.lcd_cmd_value = *data >> 8; - WAIT_LCD_NOT_BUSY - *reg_lcd_user = LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; - data++; - } - } else { - for (uint32_t cnt = 0; cnt < length; cnt++) { - dev->lcd_cmd_val.lcd_cmd_value = *data >> 8; - while (*reg_lcd_user & LCD_CAM_LCD_START) {} - *reg_lcd_user = LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; - dev->lcd_cmd_val.lcd_cmd_value = *data; - WAIT_LCD_NOT_BUSY - *reg_lcd_user = LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; - data++; - } - } - } else { - if (swap_bytes) { - uint16_t iob; - for (uint32_t cnt = 0; cnt < length; cnt++) { - iob = *data++; - iob = (iob << 8) | (iob >> 8); - dev->lcd_cmd_val.lcd_cmd_value = iob; - WAIT_LCD_NOT_BUSY - *reg_lcd_user = LCD_CAM_LCD_2BYTE_EN | LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; - } - } else { - for (uint32_t cnt = 0; cnt < length; cnt++) { - dev->lcd_cmd_val.lcd_cmd_value = *data++; - WAIT_LCD_NOT_BUSY - *reg_lcd_user = LCD_CAM_LCD_2BYTE_EN | LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; - } - } - } -} - - -void uDisplay::pb_writeBytes(const uint8_t* data, uint32_t length, bool use_dma) { - -/* - uint32_t freq = spi_speed * 1000000; - uint32_t slow = (freq< 4000000) ? 2 : (freq < 8000000) ? 1 : 0; - - auto dev = _dev; - do { - auto reg_lcd_user = &(dev->lcd_user.val); - dev->lcd_misc.lcd_cd_cmd_set = 0; - dev->lcd_cmd_val.lcd_cmd_value = data[0] | data[1] << 16; - uint32_t cmd_val = data[2] | data[3] << 16; - while (*reg_lcd_user & LCD_CAM_LCD_START) {} - *reg_lcd_user = LCD_CAM_LCD_CMD | LCD_CAM_LCD_CMD_2_CYCLE_EN | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; - - if (use_dma) { - if (slow) { ets_delay_us(slow); } - _setup_dma_desc_links(&data[4], length - 4); - gdma_start(_dma_chan, (intptr_t)(_dmadesc)); - length = 0; - } else { - size_t len = length; - if (len > CACHE_SIZE) { - len = (((len - 1) % CACHE_SIZE) + 4) & ~3u; - } - memcpy(_cache_flip, &data[4], (len-4+3)&~3); - _setup_dma_desc_links((const uint8_t*)_cache_flip, len-4); - gdma_start(_dma_chan, (intptr_t)(_dmadesc)); - length -= len; - data += len; - _cache_flip = _cache[(_cache_flip == _cache[0])]; - } - dev->lcd_cmd_val.lcd_cmd_value = cmd_val; - dev->lcd_misc.lcd_cd_data_set = 0; - *reg_lcd_user = LCD_CAM_LCD_ALWAYS_OUT_EN | LCD_CAM_LCD_DOUT | LCD_CAM_LCD_CMD | LCD_CAM_LCD_CMD_2_CYCLE_EN | LCD_CAM_LCD_UPDATE_REG; - while (*reg_lcd_user & LCD_CAM_LCD_START) {} - *reg_lcd_user = LCD_CAM_LCD_ALWAYS_OUT_EN | LCD_CAM_LCD_DOUT | LCD_CAM_LCD_CMD | LCD_CAM_LCD_CMD_2_CYCLE_EN | LCD_CAM_LCD_START; - } while (length); -*/ -} - - -void uDisplay::_send_align_data(void) { - _has_align_data = false; - auto dev = _dev; - dev->lcd_cmd_val.lcd_cmd_value = _align_data; - auto reg_lcd_user = &(dev->lcd_user.val); - while (*reg_lcd_user & LCD_CAM_LCD_START) {} - *reg_lcd_user = LCD_CAM_LCD_2BYTE_EN | LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; -} - - -void uDisplay::cs_control(bool level) { - auto pin = par_cs; - if (pin < 0) return; - if (level) { - gpio_hi(pin); - } - else { - gpio_lo(pin); - } -} - -void uDisplay::_pb_init_pin(bool read) { - if (read) { - if (interface == _UDSP_PAR8) { - for (size_t i = 0; i < 8; ++i) { - gpio_ll_output_disable(&GPIO, (gpio_num_t)par_dbl[i]); - } - } else { - for (size_t i = 0; i < 8; ++i) { - gpio_ll_output_disable(&GPIO, (gpio_num_t)par_dbl[i]); - } - for (size_t i = 0; i < 8; ++i) { - gpio_ll_output_disable(&GPIO, (gpio_num_t)par_dbh[i]); - } - } - } - else { - auto idx_base = LCD_DATA_OUT0_IDX; - if (interface == _UDSP_PAR8) { - for (size_t i = 0; i < 8; ++i) { - gpio_matrix_out(par_dbl[i], idx_base + i, 0, 0); - } - } else { - for (size_t i = 0; i < 8; ++i) { - gpio_matrix_out(par_dbl[i], idx_base + i, 0, 0); - } - for (size_t i = 0; i < 8; ++i) { - gpio_matrix_out(par_dbh[i], idx_base + 8 + i, 0, 0); - } - } - } -} - -/* read analog value from pin for simple digitizer -X+ = d1 -X- = CS -Y+ = RS -Y- = D0 - -define YP A2 // must be an analog pin, use "An" notation! -#define XM A3 // must be an analog pin, use "An" notation! -#define YM 8 // can be a digital pin -#define XP 9 // can be a digital pin - -*/ -uint32_t uDisplay::get_sr_touch(uint32_t _xp, uint32_t _xm, uint32_t _yp, uint32_t _ym) { - uint32_t aval = 0; - uint16_t xp,yp; - if (pb_busy()) return 0; - - _pb_init_pin(true); - gpio_matrix_out(par_rs, 0x100, 0, 0); - - pinMode(_ym, INPUT_PULLUP); // d0 - pinMode(_yp, INPUT_PULLUP); // rs - - pinMode(_xm, OUTPUT); // cs - pinMode(_xp, OUTPUT); // d1 - digitalWrite(_xm, HIGH); // cs - digitalWrite(_xp, LOW); // d1 - - xp = 4096 - analogRead(_ym); // d0 - - pinMode(_xm, INPUT_PULLUP); // cs - pinMode(_xp, INPUT_PULLUP); // d1 - - pinMode(_ym, OUTPUT); // d0 - pinMode(_yp, OUTPUT); // rs - digitalWrite(_ym, HIGH); // d0 - digitalWrite(_yp, LOW); // rs - - yp = 4096 - analogRead(_xp); // d1 - - aval = (xp << 16) | yp; - - pinMode(_yp, OUTPUT); // rs - pinMode(_xm, OUTPUT); // cs - pinMode(_ym, OUTPUT); // d0 - pinMode(_xp, OUTPUT); // d1 - digitalWrite(_yp, HIGH); // rs - digitalWrite(_xm, HIGH); // cs - - _pb_init_pin(false); - gpio_matrix_out(par_rs, LCD_DC_IDX, 0, 0); - - return aval; -} - - - - -#if 0 -void TFT_eSPI::startWrite(void) -{ - begin_tft_write(); - lockTransaction = true; // Lock transaction for all sequentially run sketch functions - inTransaction = true; -} - -/*************************************************************************************** -** Function name: endWrite -** Description: end transaction with CS high -***************************************************************************************/ -void TFT_eSPI::endWrite(void) -{ - lockTransaction = false; // Release sketch induced transaction lock - inTransaction = false; - DMA_BUSY_CHECK; // Safety check - user code should have checked this! - end_tft_write(); // Release SPI bus -} -#endif - - -#endif // USE_ESP32_S3 - -#endif // ESP32 diff --git a/lib/lib_display/UDisplay/uDisplay.h b/lib/lib_display/UDisplay/uDisplay.h old mode 100755 new mode 100644 index 37f4f1589..53d565c26 --- a/lib/lib_display/UDisplay/uDisplay.h +++ b/lib/lib_display/UDisplay/uDisplay.h @@ -10,20 +10,15 @@ #ifdef CONFIG_IDF_TARGET_ESP32S3 #define USE_ESP32_S3 #endif -#include "driver/spi_master.h" #include "soc/gpio_periph.h" #include +#include "driver/spi_master.h" #endif enum { UT_RD,UT_RDM,UT_CP,UT_RTF,UT_MV,UT_MVB,UT_RT,UT_RTT,UT_RDW,UT_RDWM,UT_WR,UT_WRW,UT_CPR,UT_AND,UT_SCALE,UT_LIM,UT_DBG,UT_GSRT,UT_XPT,UT_CPM,UT_END }; -#define RA8876_DATA_WRITE 0x80 -#define RA8876_DATA_READ 0xC0 -#define RA8876_CMD_WRITE 0x00 -#define RA8876_STATUS_READ 0x40 - #define UDSP_WRITE_16 0xf0 #define UDSP_READ_DATA 0xf1 #define UDSP_READ_STATUS 0xf2 @@ -41,13 +36,6 @@ enum { #include #include #include -static inline volatile uint32_t* get_gpio_hi_reg(int_fast8_t pin) { return (pin & 32) ? &GPIO.out1_w1ts.val : &GPIO.out_w1ts; } -//static inline volatile uint32_t* get_gpio_hi_reg(int_fast8_t pin) { return (volatile uint32_t*)((pin & 32) ? 0x60004014 : 0x60004008) ; } // workaround Eratta -static inline volatile uint32_t* get_gpio_lo_reg(int_fast8_t pin) { return (pin & 32) ? &GPIO.out1_w1tc.val : &GPIO.out_w1tc; } -//static inline volatile uint32_t* get_gpio_lo_reg(int_fast8_t pin) { return (volatile uint32_t*)((pin & 32) ? 0x60004018 : 0x6000400C) ; } -static inline bool gpio_in(int_fast8_t pin) { return ((pin & 32) ? GPIO.in1.data : GPIO.in) & (1 << (pin & 31)); } -static inline void gpio_hi(int_fast8_t pin) { if (pin >= 0) *get_gpio_hi_reg(pin) = 1 << (pin & 31); } // ESP_LOGI("LGFX", "gpio_hi: %d", pin); } -static inline void gpio_lo(int_fast8_t pin) { if (pin >= 0) *get_gpio_lo_reg(pin) = 1 << (pin & 31); } // ESP_LOGI("LGFX", "gpio_lo: %d", pin); } #include "esp_lcd_panel_interface.h" #include "esp_lcd_panel_rgb.h" #include "esp_pm.h" @@ -72,65 +60,6 @@ static inline void gpio_lo(int_fast8_t pin) { if (pin >= 0) *get_gpio_lo_reg(pin #define DISPLAY_INIT_PARTIAL 1 #define DISPLAY_INIT_FULL 2 -enum uColorType { uCOLOR_BW, uCOLOR_COLOR }; - -// Color definitions -#define UDISP_BLACK 0x0000 /* 0, 0, 0 */ -#define UDISP_NAVY 0x000F /* 0, 0, 128 */ -#define UDISP_DARKGREEN 0x03E0 /* 0, 128, 0 */ -#define UDISP_DARKCYAN 0x03EF /* 0, 128, 128 */ -#define UDISP_MAROON 0x7800 /* 128, 0, 0 */ -#define UDISP_PURPLE 0x780F /* 128, 0, 128 */ -#define UDISP_OLIVE 0x7BE0 /* 128, 128, 0 */ -#define UDISP_LIGHTGREY 0xC618 /* 192, 192, 192 */ -#define UDISP_DARKGREY 0x7BEF /* 128, 128, 128 */ -#define UDISP_BLUE 0x001F /* 0, 0, 255 */ -#define UDISP_GREEN 0x07E0 /* 0, 255, 0 */ -#define UDISP_CYAN 0x07FF /* 0, 255, 255 */ -#define UDISP_RED 0xF800 /* 255, 0, 0 */ -#define UDISP_MAGENTA 0xF81F /* 255, 0, 255 */ -#define UDISP_YELLOW 0xFFE0 /* 255, 255, 0 */ -#define UDISP_WHITE 0xFFFF /* 255, 255, 255 */ -#define UDISP_ORANGE 0xFD20 /* 255, 165, 0 */ -#define UDISP_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ -#define UDISP_PINK 0xFc18 /* 255, 128, 192 */ - -#ifdef ESP8266 -#define PIN_OUT_SET 0x60000304 -#define PIN_OUT_CLEAR 0x60000308 -#define GPIO_SET(A) WRITE_PERI_REG( PIN_OUT_SET, 1 << A) -#define GPIO_CLR(A) WRITE_PERI_REG( PIN_OUT_CLEAR, 1 << A) -#define GPIO_CLR_SLOW(A) digitalWrite(A, LOW) -#define GPIO_SET_SLOW(A) digitalWrite(A, HIGH) -#else -#undef GPIO_SET -#undef GPIO_CLR -#undef GPIO_SET_SLOW -#undef GPIO_CLR_SLOW - -#if CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32P4 -#define GPIO_CLR(A) GPIO.out_w1tc.val = (1 << A) -#define GPIO_SET(A) GPIO.out_w1ts.val = (1 << A) -#else // plain ESP32 -#define GPIO_CLR(A) GPIO.out_w1tc = (1 << A) -#define GPIO_SET(A) GPIO.out_w1ts = (1 << A) -#endif - - - - -#define GPIO_CLR_SLOW(A) digitalWrite(A, LOW) -#define GPIO_SET_SLOW(A) digitalWrite(A, HIGH) - -#endif - -#define SPI_BEGIN_TRANSACTION if (spi_nr <= 2) beginTransaction(spiSettings); -#define SPI_END_TRANSACTION if (spi_nr <= 2) endTransaction(); - -#define SPI_CS_LOW if (spi_cs >= 0) GPIO_CLR_SLOW(spi_cs); -#define SPI_CS_HIGH if (spi_cs >= 0) GPIO_SET_SLOW(spi_cs); -#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); class uDisplay : public Renderer { public: @@ -164,153 +93,154 @@ class uDisplay : public Renderer { int16_t getPoint_y(); #endif // USE_UNIVERSAL_TOUCH - private: - void beginTransaction(SPISettings s); - void endTransaction(void); - void setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); - void drawPixel(int16_t x, int16_t y, uint16_t color); - void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); - void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); - uint32_t str2c(char **sp, char *vp, uint32_t len); - void i2c_command(uint8_t val); - void ulcd_command_one(uint8_t val); - void ulcd_command(uint8_t val); - void ulcd_data8(uint8_t val); - void ulcd_data16(uint16_t val); - void ulcd_data32(uint32_t val); - void write8(uint8_t val); - void write8_slow(uint8_t val); - void write9(uint8_t val, uint8_t dc); - void write9_slow(uint8_t val, uint8_t dc); - void hw_write9(uint8_t val, uint8_t dc); - void write16(uint16_t val); - void write32(uint32_t val); - void spi_data9(uint8_t d, uint8_t dc); - uint8_t readData(void); - uint8_t readStatus(void); - uint8_t writeReg16(uint8_t reg, uint16_t wval); - void WriteColor(uint16_t color); - void SetLut(const unsigned char* lut); - void SetLuts(void); - void DisplayFrame_29(void); - void Updateframe_EPD(); - //void DisplayFrame_42(const unsigned char* frame_buffer); - 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 SetPartialWindow_42(uint8_t* frame_buffer, int16_t x, int16_t y, int16_t w, int16_t l, int16_t dtm); - void ClearFrameMemory(unsigned char color); - void ClearFrame_42(void); - void DisplayFrame_42(void); - uint8_t strlen_ln(char *str); - int32_t next_val(char **sp); - uint32_t next_hex(char **sp); - void setAddrWindow_int(uint16_t x, uint16_t y, uint16_t w, uint16_t h); - char dname[16]; - int8_t bpp; - uint8_t col_type; - uint8_t interface; - uint8_t i2caddr; - int8_t i2c_scl; - int8_t spec_init; - TwoWire *wire; - int8_t wire_n; - 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[256]; - uint8_t dsp_ncmds; - uint8_t dsp_on; - uint8_t dsp_off; - uint8_t allcmd_mode; - int8_t splash_font; - uint8_t splash_size; - uint16_t splash_xp; - uint16_t splash_yp; - uint16_t fg_col; - uint16_t bg_col; - uint16_t gxs; - uint16_t gys; - int8_t bpmode; - int8_t spi_cs; - int8_t spi_clk; - int8_t spi_mosi; - int8_t spi_dc; - int8_t bpanel; // backbanel GPIO, -1 if none - int8_t spi_miso; - uint8_t dimmer8; // 8 bits resolution, 0..255 - uint16_t dimmer10_gamma; // 10 bits resolution, 0..1023, gamma corrected - SPIClass *uspi; - uint8_t sspi; - SPISettings spiSettings; - uint8_t spi_speed; - uint8_t spi_nr = 1; - uint8_t madctrl; - uint8_t startline; - uint8_t rot[4]; - uint8_t rot_t[4]; - uint16_t x_addr_offs[4]; - uint16_t y_addr_offs[4]; - uint8_t saw_1; - uint8_t saw_2; - uint8_t saw_3; - uint8_t cur_rot; - uint8_t col_mode; - uint8_t inv_on; - uint8_t inv_off; - uint8_t sa_mode; - uint8_t dim_op; - uint8_t lutfsize; - uint8_t lutpsize; - int16_t lutftime; - int8_t busy_pin; - uint16_t lutptime; - uint16_t lut3time; - uint16_t lut_num; - uint8_t ep_mode; - 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 *frame_buffer; +private: + uint8_t *frame_buffer; + uint8_t *lut_full; + uint8_t *lut_partial; + uint8_t *lut_array[MAX_LUTS]; + uint8_t dsp_cmds[256]; + char dname[16]; + SPIClass *uspi; + TwoWire *wire; + SPISettings spiSettings; - uint8_t epcoffs_full; - uint8_t epc_full_cnt; - uint8_t epcoffs_part; - uint8_t epc_part_cnt; + uint16_t x_addr_offs[4]; + uint16_t y_addr_offs[4]; + uint16_t splash_xp; + uint16_t splash_yp; + uint16_t fg_col; + uint16_t bg_col; + uint16_t gxs; + uint16_t gys; + uint16_t dimmer10_gamma; + uint16_t seta_xp1; + uint16_t seta_xp2; + uint16_t seta_yp1; + uint16_t seta_yp2; + uint16_t lutptime; + uint16_t lut3time; + uint16_t lut_num; - 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; - uint16_t seta_yp2; - int16_t rotmap_xmin; - int16_t rotmap_xmax; - 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); - void send_spi_icmds(uint16_t cmd_size); + uint8_t bpp; + uint8_t col_type; + uint8_t interface; + uint8_t i2caddr; + uint8_t i2c_col_start; + uint8_t i2c_col_end; + uint8_t i2c_page_start; + uint8_t i2c_page_end; + uint8_t dsp_ncmds; + uint8_t dsp_on; + uint8_t dsp_off; + uint8_t allcmd_mode; + uint8_t splash_size; + uint8_t dimmer8; + uint8_t spi_speed; + uint8_t spi_nr; + uint8_t rot[4]; + uint8_t rot_t[4]; + uint8_t madctrl; + uint8_t startline; + uint8_t saw_1; + uint8_t saw_2; + uint8_t saw_3; + uint8_t cur_rot; + uint8_t col_mode; + uint8_t inv_on; + uint8_t inv_off; + uint8_t sa_mode; + uint8_t dim_op; + uint8_t lutfsize; + uint8_t lutpsize; + uint8_t lut_siz_full; + 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_cnt[MAX_LUTS]; + uint8_t lut_cmd[MAX_LUTS]; + uint8_t lut_siz[MAX_LUTS]; + uint8_t ep_mode; + uint8_t ep_update_mode; + uint8_t sspi; + + int8_t spec_init; + int8_t wire_n; + int8_t i2c_scl; + int8_t i2c_sda; + int8_t reset; + int8_t splash_font; + int8_t bpmode; + int8_t spi_cs; + int8_t spi_clk; + int8_t spi_mosi; + int8_t spi_dc; + int8_t bpanel; + int8_t spi_miso; + int8_t busy_pin; + + int16_t lutftime; + int16_t rotmap_xmin; + int16_t rotmap_xmax; + int16_t rotmap_ymin; + int16_t rotmap_ymax; + + void beginTransaction(SPISettings s); + void endTransaction(void); + void setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); + void drawPixel(int16_t x, int16_t y, uint16_t color); + void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + uint32_t str2c(char **sp, char *vp, uint32_t len); + void i2c_command(uint8_t val); + void ulcd_command_one(uint8_t val); + void ulcd_command(uint8_t val); + void ulcd_data8(uint8_t val); + void ulcd_data16(uint16_t val); + void ulcd_data32(uint32_t val); + void write8(uint8_t val); + void write8_slow(uint8_t val); + void write9(uint8_t val, uint8_t dc); + void write9_slow(uint8_t val, uint8_t dc); + void hw_write9(uint8_t val, uint8_t dc); + void write16(uint16_t val); + void write32(uint32_t val); + void spi_data9(uint8_t d, uint8_t dc); + uint8_t readData(void); + uint8_t readStatus(void); + uint8_t writeReg16(uint8_t reg, uint16_t wval); + void WriteColor(uint16_t color); + void SetLut(const unsigned char* lut); + void SetLuts(void); + void DisplayFrame_29(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); + void ClearFrame_42(void); + void DisplayFrame_42(void); + uint8_t strlen_ln(char *str); + int32_t next_val(char **sp); + uint32_t next_hex(char **sp); + void setAddrWindow_int(uint16_t x, uint16_t y, uint16_t w, uint16_t h); + 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); + void send_spi_icmds(uint16_t cmd_size); #ifdef USE_ESP32_S3 diff --git a/lib/lib_display/UDisplay/uDisplayColors.cpp b/lib/lib_display/UDisplay/uDisplayColors.cpp new file mode 100644 index 000000000..87c758f08 --- /dev/null +++ b/lib/lib_display/UDisplay/uDisplayColors.cpp @@ -0,0 +1,31 @@ +#include "uDisplay.h" +#include "uDisplay_config.h" + +// Color palette definition +static constexpr uint16_t udisp_colors[] = { + UDISP_BLACK, UDISP_WHITE, UDISP_RED, UDISP_GREEN, UDISP_BLUE, UDISP_CYAN, + UDISP_MAGENTA, UDISP_YELLOW, UDISP_NAVY, UDISP_DARKGREEN, UDISP_DARKCYAN, + UDISP_MAROON, UDISP_PURPLE, UDISP_OLIVE, UDISP_LIGHTGREY, UDISP_DARKGREY, + UDISP_ORANGE, UDISP_GREENYELLOW, UDISP_PINK +}; + +uint16_t uDisplay::GetColorFromIndex(uint8_t index) { + const size_t color_count = sizeof(udisp_colors) / sizeof(udisp_colors[0]); + + if (index >= color_count) { + index = 0; + } + return udisp_colors[index]; +} + +uint16_t uDisplay::fgcol(void) { + return fg_col; +} + +uint16_t uDisplay::bgcol(void) { + return bg_col; +} + +int8_t uDisplay::color_type(void) { + return col_type; +} \ No newline at end of file diff --git a/lib/lib_display/UDisplay/uDisplay_config.h b/lib/lib_display/UDisplay/uDisplay_config.h new file mode 100644 index 000000000..5dd8f7d78 --- /dev/null +++ b/lib/lib_display/UDisplay/uDisplay_config.h @@ -0,0 +1,56 @@ +#ifndef UDISPLAY_CONFIG_H +#define UDISPLAY_CONFIG_H + +// Logging system interface +enum LoggingLevels { + LOG_LEVEL_NONE, + LOG_LEVEL_ERROR, + LOG_LEVEL_INFO, + LOG_LEVEL_DEBUG, + LOG_LEVEL_DEBUG_MORE +}; + +extern void AddLog(uint32_t loglevel, const char* formatP, ...); +extern int32_t ESP_ResetInfoReason(); +extern float CharToFloat(const char *str); +extern SPIClass *SpiBegin(uint32 bus); +extern int Cache_WriteBack_Addr(uint32_t addr, uint32_t size); +extern bool UsePSRAM(void); + +enum uColorType { uCOLOR_BW, uCOLOR_COLOR }; + +// Color definitions +constexpr uint16_t UDISP_BLACK = 0x0000; /* 0, 0, 0 */ +constexpr uint16_t UDISP_NAVY = 0x000F; /* 0, 0, 128 */ +constexpr uint16_t UDISP_DARKGREEN = 0x03E0; /* 0, 128, 0 */ +constexpr uint16_t UDISP_DARKCYAN = 0x03EF; /* 0, 128, 128 */ +constexpr uint16_t UDISP_MAROON = 0x7800; /* 128, 0, 0 */ +constexpr uint16_t UDISP_PURPLE = 0x780F; /* 128, 0, 128 */ +constexpr uint16_t UDISP_OLIVE = 0x7BE0; /* 128, 128, 0 */ +constexpr uint16_t UDISP_LIGHTGREY = 0xC618; /* 192, 192, 192 */ +constexpr uint16_t UDISP_DARKGREY = 0x7BEF; /* 128, 128, 128 */ +constexpr uint16_t UDISP_BLUE = 0x001F; /* 0, 0, 255 */ +constexpr uint16_t UDISP_GREEN = 0x07E0; /* 0, 255, 0 */ +constexpr uint16_t UDISP_CYAN = 0x07FF; /* 0, 255, 255 */ +constexpr uint16_t UDISP_RED = 0xF800; /* 255, 0, 0 */ +constexpr uint16_t UDISP_MAGENTA = 0xF81F; /* 255, 0, 255 */ +constexpr uint16_t UDISP_YELLOW = 0xFFE0; /* 255, 255, 0 */ +constexpr uint16_t UDISP_WHITE = 0xFFFF; /* 255, 255, 255 */ +constexpr uint16_t UDISP_ORANGE = 0xFD20; /* 255, 165, 0 */ +constexpr uint16_t UDISP_GREENYELLOW = 0xAFE5; /* 173, 255, 47 */ +constexpr uint16_t UDISP_PINK = 0xFC18; /* 255, 128, 192 */ + +// epaper pseudo opcodes +constexpr uint8_t EP_RESET = 0x60; +constexpr uint8_t EP_LUT_FULL = 0x61; +constexpr uint8_t EP_LUT_PARTIAL = 0x62; +constexpr uint8_t EP_WAITIDLE = 0x63; +constexpr uint8_t EP_SET_MEM_AREA = 0x64; +constexpr uint8_t EP_SET_MEM_PTR = 0x65; +constexpr uint8_t EP_SEND_DATA = 0x66; +constexpr uint8_t EP_CLR_FRAME = 0x67; +constexpr uint8_t EP_SEND_FRAME = 0x68; +constexpr uint8_t EP_BREAK_RR_EQU = 0x69; +constexpr uint8_t EP_BREAK_RR_NEQ = 0x6a; + +#endif \ No newline at end of file diff --git a/lib/lib_display/UDisplay/uDisplay_control.cpp b/lib/lib_display/UDisplay/uDisplay_control.cpp new file mode 100644 index 000000000..29c1d537b --- /dev/null +++ b/lib/lib_display/UDisplay/uDisplay_control.cpp @@ -0,0 +1,146 @@ +#include "uDisplay.h" +#include "uDisplay_config.h" +#include "uDisplay_spi.h" + +void udisp_bpwr(uint8_t on); + +void udisp_dimm(uint8_t dim); + +// input value is 0..15 +// void uDisplay::dim(uint8_t dim) { +// dim8(((uint32_t)dim * 255) / 15); +// } + +// ===== Power Management ===== + +void uDisplay::DisplayOnff(int8_t on) { + if (ep_mode) { + return; + } + + if (pwr_cbp) { + pwr_cbp(on); + } + +#define AW_PWMRES 1024 + + if (interface == _UDSP_I2C) { + if (on) { + i2c_command(dsp_on); + } else { + i2c_command(dsp_off); + } + } else { + if (on) { + if (dsp_on != 0xff) ulcd_command_one(dsp_on); + if (bpanel >= 0) { +#ifdef ESP32 + if (!bpmode) { + analogWrite(bpanel, dimmer10_gamma); + } else { + analogWrite(bpanel, AW_PWMRES - dimmer10_gamma); + } +#else + if (!bpmode) { + digitalWrite(bpanel, HIGH); + } else { + digitalWrite(bpanel, LOW); + } +#endif + } + } else { + if (dsp_off != 0xff) ulcd_command_one(dsp_off); + if (bpanel >= 0) { +#ifdef ESP32 + if (!bpmode) { + analogWrite(bpanel, 0); + } else { + analogWrite(bpanel, AW_PWMRES - 1); + } +#else + if (!bpmode) { + digitalWrite(bpanel, LOW); + } else { + digitalWrite(bpanel, HIGH); + } +#endif + } + } + } +} + +// ===== Brightness/Dimming Control ===== +// dim is 0..255 +void uDisplay::dim10(uint8_t dim, uint16_t dim_gamma) { + dimmer8 = dim; + dimmer10_gamma = dim_gamma; + + if (ep_mode) { + return; + } + +#ifdef ESP32 + if (bpanel >= 0) { + if (!bpmode) { + analogWrite(bpanel, dimmer10_gamma); + } else { + analogWrite(bpanel, AW_PWMRES - dimmer10_gamma); + } + } else if (dim_cbp) { + dim_cbp(dim); + } +#endif + + if (interface == _UDSP_SPI) { + if (dim_op != 0xff) { + SPI_BEGIN_TRANSACTION + SPI_CS_LOW + ulcd_command(dim_op); + ulcd_data8(dimmer8); + SPI_CS_HIGH + SPI_END_TRANSACTION + } + } +} + +// ===== Display Inversion ===== + +void uDisplay::invertDisplay(boolean i) { + if (ep_mode) { + return; + } + + if (interface == _UDSP_SPI || interface == _UDSP_PAR8 || interface == _UDSP_PAR16) { + if (i) { + ulcd_command_one(inv_on); + } else { + ulcd_command_one(inv_off); + } + } + + if (interface == _UDSP_I2C) { + if (i) { + i2c_command(inv_on); + } else { + i2c_command(inv_off); + } + } +} + +// ===== Splash Screen ===== + +void uDisplay::Splash(void) { + if (splash_font < 0) return; + + if (ep_mode) { + Updateframe(); + delay_sync(lut3time * 10); + } + + setTextFont(splash_font); + setTextSize(splash_size); + DrawStringAt(splash_xp, splash_yp, dname, fg_col, 0); + Updateframe(); + + AddLog(LOG_LEVEL_DEBUG, PSTR("DSP: draw splash")); +} \ No newline at end of file diff --git a/lib/lib_display/UDisplay/uDisplay_dma.cpp b/lib/lib_display/UDisplay/uDisplay_dma.cpp new file mode 100644 index 000000000..b8fa6eb63 --- /dev/null +++ b/lib/lib_display/UDisplay/uDisplay_dma.cpp @@ -0,0 +1,133 @@ +#include "uDisplay.h" +#include "uDisplay_config.h" + +#ifdef ESP32 + +// ===== DMA Initialization and Control ===== +// ESP 32 DMA section , derived from TFT_eSPI + +bool uDisplay::initDMA(int32_t ctrl_cs) { + if (DMA_Enabled) return false; + + esp_err_t ret; + spi_bus_config_t buscfg = { + .mosi_io_num = spi_mosi, + .miso_io_num = -1, + .sclk_io_num = spi_clk, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = width() * height() * 2 + 8, // TFT screen size + .flags = 0, + .intr_flags = 0 + }; + + spi_device_interface_config_t devcfg = { + .command_bits = 0, + .address_bits = 0, + .dummy_bits = 0, + .mode = SPI_MODE3, + .duty_cycle_pos = 0, + .cs_ena_pretrans = 0, + .cs_ena_posttrans = 0, + .clock_speed_hz = spi_speed*1000000, + .input_delay_ns = 0, + .spics_io_num = ctrl_cs, + .flags = SPI_DEVICE_NO_DUMMY, //0, + .queue_size = 1, + .pre_cb = 0, //dc_callback, //Callback to handle D/C line + .post_cb = 0 + }; + + ret = spi_bus_initialize(spi_host, &buscfg, 1); + ESP_ERROR_CHECK(ret); + ret = spi_bus_add_device(spi_host, &devcfg, &dmaHAL); + ESP_ERROR_CHECK(ret); + + DMA_Enabled = true; + spiBusyCheck = 0; + return true; +} + +void uDisplay::deInitDMA(void) { + if (!DMA_Enabled) return; + spi_bus_remove_device(dmaHAL); + spi_bus_free(spi_host); + DMA_Enabled = false; +} + +bool uDisplay::dmaBusy(void) { + if (!DMA_Enabled || !spiBusyCheck) return false; + + spi_transaction_t *rtrans; + esp_err_t ret; + uint8_t checks = spiBusyCheck; + for (int i = 0; i < checks; ++i) { + ret = spi_device_get_trans_result(dmaHAL, &rtrans, 0); + if (ret == ESP_OK) spiBusyCheck--; + } + + //Serial.print("spiBusyCheck=");Serial.println(spiBusyCheck); + if (spiBusyCheck == 0) return false; + return true; +} + +void uDisplay::dmaWait(void) { + if (!DMA_Enabled || !spiBusyCheck) return; + spi_transaction_t *rtrans; + esp_err_t ret; + for (int i = 0; i < spiBusyCheck; ++i) { + ret = spi_device_get_trans_result(dmaHAL, &rtrans, portMAX_DELAY); + assert(ret == ESP_OK); + } + spiBusyCheck = 0; +} + +// ===== DMA Data Transfer Functions ===== + +void uDisplay::pushPixelsDMA(uint16_t* image, uint32_t len) { + if ((len == 0) || (!DMA_Enabled)) return; + + dmaWait(); + + esp_err_t ret; + + memset(&trans, 0, sizeof(spi_transaction_t)); + + trans.user = (void *)1; + trans.tx_buffer = image; //finally send the line data + trans.length = len * 16; //Data length, in bits + trans.flags = 0; //SPI_TRANS_USE_TXDATA flag + + ret = spi_device_queue_trans(dmaHAL, &trans, portMAX_DELAY); + assert(ret == ESP_OK); + + spiBusyCheck++; + if (!lvgl_param.async_dma) { + dmaWait(); + } +} + +void uDisplay::pushPixels3DMA(uint8_t* image, uint32_t len) { + if ((len == 0) || (!DMA_Enabled)) return; + + dmaWait(); + + esp_err_t ret; + + memset(&trans, 0, sizeof(spi_transaction_t)); + + trans.user = (void *)1; + trans.tx_buffer = image; //finally send the line data + trans.length = len * 24; //Data length, in bits + trans.flags = 0; //SPI_TRANS_USE_TXDATA flag + + ret = spi_device_queue_trans(dmaHAL, &trans, portMAX_DELAY); + assert(ret == ESP_OK); + + spiBusyCheck++; + if (!lvgl_param.async_dma) { + dmaWait(); + } +} + +#endif // ESP32 \ No newline at end of file diff --git a/lib/lib_display/UDisplay/uDisplay_epd.cpp b/lib/lib_display/UDisplay/uDisplay_epd.cpp new file mode 100644 index 000000000..e16122934 --- /dev/null +++ b/lib/lib_display/UDisplay/uDisplay_epd.cpp @@ -0,0 +1,362 @@ +#include "uDisplay.h" +#include "uDisplay_config.h" +#include "uDisplay_spi.h" + + +// ===== EPD Command Definitions ===== +static constexpr uint8_t DRIVER_OUTPUT_CONTROL = 0x01; +static constexpr uint8_t BOOSTER_SOFT_START_CONTROL = 0x0C; +static constexpr uint8_t GATE_SCAN_START_POSITION = 0x0F; +static constexpr uint8_t DEEP_SLEEP_MODE = 0x10; +static constexpr uint8_t DATA_ENTRY_MODE_SETTING = 0x11; +static constexpr uint8_t SW_RESET = 0x12; +static constexpr uint8_t TEMPERATURE_SENSOR_CONTROL = 0x1A; +static constexpr uint8_t MASTER_ACTIVATION = 0x20; +static constexpr uint8_t DISPLAY_UPDATE_CONTROL_1 = 0x21; +static constexpr uint8_t DISPLAY_UPDATE_CONTROL_2 = 0x22; +static constexpr uint8_t WRITE_RAM = 0x24; +static constexpr uint8_t WRITE_VCOM_REGISTER = 0x2C; +static constexpr uint8_t WRITE_LUT_REGISTER = 0x32; +static constexpr uint8_t SET_DUMMY_LINE_PERIOD = 0x3A; +static constexpr uint8_t SET_GATE_TIME = 0x3B; +static constexpr uint8_t BORDER_WAVEFORM_CONTROL = 0x3C; +static constexpr uint8_t SET_RAM_X_ADDRESS_START_END_POSITION = 0x44; +static constexpr uint8_t SET_RAM_Y_ADDRESS_START_END_POSITION = 0x45; +static constexpr uint8_t SET_RAM_X_ADDRESS_COUNTER = 0x4E; +static constexpr uint8_t SET_RAM_Y_ADDRESS_COUNTER = 0x4F; +static constexpr uint8_t TERMINATE_FRAME_READ_WRITE = 0xFF; + +// ===== EPD Initialization and Control ===== + +void uDisplay::Init_EPD(int8_t p) { + if (p == DISPLAY_INIT_PARTIAL) { + if (lutpsize) { + AddLog(LOG_LEVEL_DEBUG, PSTR("DSP: init partial epaper mode")); + SetLut(lut_partial); + Updateframe_EPD(); + delay_sync(lutptime * 10); + } + return; + } else if (p == DISPLAY_INIT_FULL) { + AddLog(LOG_LEVEL_DEBUG, PSTR("DSP: init full epaper mode")); + if (lutfsize) { + SetLut(lut_full); + Updateframe_EPD(); + } + if (ep_mode == 2) { + ClearFrame_42(); + DisplayFrame_42(); + } + delay_sync(lutftime * 10); + return; + } + + if (ep_mode == 2) Init_EPD(DISPLAY_INIT_FULL); +} + +void uDisplay::SetLut(const unsigned char* lut) { + spi_command_EPD(lut_cmd[0]); + for (int i = 0; i < lutfsize; i++) { + spi_data8_EPD(lut[i]); + } +} + +void uDisplay::SetLuts(void) { + uint8_t index, count; + 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[index][count]); + } + } +} + +// ===== EPD Frame Memory Management ===== + +void uDisplay::ClearFrameMemory(unsigned char color) { + SetMemoryArea(0, 0, gxs - 1, gys - 1); + SetMemoryPointer(0, 0); + spi_command_EPD(WRITE_RAM); + for (int i = 0; i < gxs / 8 * gys; i++) { + spi_data8_EPD(color); + } +} + +void uDisplay::ClearFrame_42(void) { + spi_command_EPD(saw_1); + 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 < gys; j++) { + for (uint16_t i = 0; i < gxs; i++) { + spi_data8_EPD(0xFF); + } + } + + spi_command_EPD(saw_3); + delay_sync(100); + AddLog(LOG_LEVEL_DEBUG, PSTR("DSP: EPD Clearframe")); +} + +void uDisplay::DisplayFrame_42(void) { + 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(int i = 0; i < gxs / 8 * gys; i++) { + spi_data8_EPD(framebuffer[i]^0xff); + } + delay(2); + + SetLuts(); + + spi_command_EPD(saw_3); + delay_sync(100); + + AddLog(LOG_LEVEL_DEBUG, PSTR("DSP: EPD Displayframe")); +} + +void uDisplay::DisplayFrame_29(void) { + spi_command_EPD(DISPLAY_UPDATE_CONTROL_2); + spi_data8_EPD(0xC4); + spi_command_EPD(MASTER_ACTIVATION); + spi_data8_EPD(TERMINATE_FRAME_READ_WRITE); +} + +// ===== EPD Memory Addressing ===== + +void uDisplay::SetMemoryArea(int x_start, int y_start, int x_end, int y_end) { + int x_start1 = (x_start >> 3) & 0xFF; + int x_end1 = (x_end >> 3) & 0xFF; + int y_start1 = y_start & 0xFF; + int y_start2 = (y_start >> 8) & 0xFF; + int y_end1 = y_end & 0xFF; + int y_end2 = (y_end >> 8) & 0xFF; + + if (ep_mode == 3) { + spi_command_EPD(SET_RAM_X_ADDRESS_START_END_POSITION); + spi_data8_EPD(x_start1); + spi_data8_EPD(x_end1); + spi_command_EPD(SET_RAM_Y_ADDRESS_START_END_POSITION); + spi_data8_EPD(y_end1); + spi_data8_EPD(y_end2); + spi_data8_EPD(y_start1); + spi_data8_EPD(y_start2); + } else { + spi_command_EPD(SET_RAM_X_ADDRESS_START_END_POSITION); + spi_data8_EPD(x_start1); + spi_data8_EPD(x_end1); + spi_command_EPD(SET_RAM_Y_ADDRESS_START_END_POSITION); + spi_data8_EPD(y_start1); + spi_data8_EPD(y_start2); + spi_data8_EPD(y_end1); + spi_data8_EPD(y_end2); + } +} + +void uDisplay::SetMemoryPointer(int x, int y) { + int x1; + int y1; + int y2; + + if (ep_mode == 3) { + x1 = (x >> 3) & 0xFF; + y--; + y1 = y & 0xFF; + y2 = (y >> 8) & 0xFF; + } else { + x1 = (x >> 3) & 0xFF; + y1 = y & 0xFF; + y2 = (y >> 8) & 0xFF; + } + + spi_command_EPD(SET_RAM_X_ADDRESS_COUNTER); + spi_data8_EPD(x1); + spi_command_EPD(SET_RAM_Y_ADDRESS_COUNTER); + spi_data8_EPD(y1); + spi_data8_EPD(y2); +} + +// ===== EPD Frame Updates ===== + +void uDisplay::Updateframe_EPD(void) { + if (ep_mode == 1 || ep_mode == 3) { + 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(); + } +} + +void uDisplay::SetFrameMemory(const unsigned char* image_buffer) { + SetMemoryArea(0, 0, gxs - 1, gys - 1); + SetMemoryPointer(0, 0); + spi_command_EPD(WRITE_RAM); + for (int i = 0; i < gxs / 8 * gys; i++) { + spi_data8_EPD(image_buffer[i] ^ 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 &= 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); + 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); + } + } +} + +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); + } + } +} + +// ===== EPD Drawing Primitives ===== +#define IF_INVERT_COLOR 1 +#define renderer_swap(a, b) { int16_t t = a; a = b; b = t; } + +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) { + framebuffer[(x + y * w) / 8] |= 0x80 >> (x % 8); + } else { + framebuffer[(x + y * w) / 8] &= ~(0x80 >> (x % 8)); + } + } else { + if (color) { + framebuffer[(x + y * w) / 8] &= ~(0x80 >> (x % 8)); + } else { + framebuffer[(x + y * w) / 8] |= 0x80 >> (x % 8); + } + } +} + +void uDisplay::drawPixel_EPD(int16_t x, int16_t y, uint16_t color) { + if (!framebuffer) return; + if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) + return; + + 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; + } + + 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++; + } +} + +// ===== EPD SPI Helpers ===== + +void uDisplay::spi_command_EPD(uint8_t val) { + SPI_BEGIN_TRANSACTION + SPI_CS_LOW + ulcd_command(val); + SPI_CS_HIGH + SPI_END_TRANSACTION +} + +void uDisplay::spi_data8_EPD(uint8_t val) { + SPI_BEGIN_TRANSACTION + SPI_CS_LOW + ulcd_data8(val); + SPI_CS_HIGH + SPI_END_TRANSACTION +} \ No newline at end of file diff --git a/lib/lib_display/UDisplay/uDisplay_esp32s3.cpp b/lib/lib_display/UDisplay/uDisplay_esp32s3.cpp new file mode 100644 index 000000000..4e2409d47 --- /dev/null +++ b/lib/lib_display/UDisplay/uDisplay_esp32s3.cpp @@ -0,0 +1,158 @@ +#include "uDisplay.h" +#include "uDisplay_config.h" + +#ifdef USE_ESP32_S3 +static inline volatile uint32_t* get_gpio_hi_reg(int_fast8_t pin) { return (pin & 32) ? &GPIO.out1_w1ts.val : &GPIO.out_w1ts; } +//static inline volatile uint32_t* get_gpio_hi_reg(int_fast8_t pin) { return (volatile uint32_t*)((pin & 32) ? 0x60004014 : 0x60004008) ; } // workaround Eratta +static inline volatile uint32_t* get_gpio_lo_reg(int_fast8_t pin) { return (pin & 32) ? &GPIO.out1_w1tc.val : &GPIO.out_w1tc; } +//static inline volatile uint32_t* get_gpio_lo_reg(int_fast8_t pin) { return (volatile uint32_t*)((pin & 32) ? 0x60004018 : 0x6000400C) ; } +static inline bool gpio_in(int_fast8_t pin) { return ((pin & 32) ? GPIO.in1.data : GPIO.in) & (1 << (pin & 31)); } +static inline void gpio_hi(int_fast8_t pin) { if (pin >= 0) *get_gpio_hi_reg(pin) = 1 << (pin & 31); } // ESP_LOGI("LGFX", "gpio_hi: %d", pin); } +static inline void gpio_lo(int_fast8_t pin) { if (pin >= 0) *get_gpio_lo_reg(pin) = 1 << (pin & 31); } // ESP_LOGI("LGFX", "gpio_lo: %d", pin); } + +// ===== ESP32-S3 Clock Calculation ===== + +void uDisplay::calcClockDiv(uint32_t* div_a, uint32_t* div_b, uint32_t* div_n, uint32_t* clkcnt, uint32_t baseClock, uint32_t targetFreq) { + uint32_t diff = INT32_MAX; + *div_n = 256; + *div_a = 63; + *div_b = 62; + *clkcnt = 64; + uint32_t start_cnt = std::min(64u, (baseClock / (targetFreq * 2) + 1)); + uint32_t end_cnt = std::max(2u, baseClock / 256u / targetFreq); + if (start_cnt <= 2) { end_cnt = 1; } + for (uint32_t cnt = start_cnt; diff && cnt >= end_cnt; --cnt) + { + float fdiv = (float)baseClock / cnt / targetFreq; + uint32_t n = std::max(2u, (uint32_t)fdiv); + fdiv -= n; + + for (uint32_t a = 63; diff && a > 0; --a) + { + uint32_t b = roundf(fdiv * a); + if (a == b && n == 256) { + break; + } + uint32_t freq = baseClock / ((n * cnt) + (float)(b * cnt) / (float)a); + uint32_t d = abs((int)targetFreq - (int)freq); + if (diff <= d) { continue; } + diff = d; + *clkcnt = cnt; + *div_n = n; + *div_b = b; + *div_a = a; + if (b == 0 || a == b) { + break; + } + } + } + if (*div_a == *div_b) + { + *div_b = 0; + *div_n += 1; + } +} + +// ===== ESP32-S3 DMA Descriptor Management ===== + +void uDisplay::_alloc_dmadesc(size_t len) { + if (_dmadesc) heap_caps_free(_dmadesc); + _dmadesc_size = len; + _dmadesc = (lldesc_t*)heap_caps_malloc(sizeof(lldesc_t) * len, MALLOC_CAP_DMA); +} + +void uDisplay::_setup_dma_desc_links(const uint8_t *data, int32_t len) { + static constexpr size_t MAX_DMA_LEN = (4096-4); +} + +// ===== ESP32-S3 Pin Control ===== + +void uDisplay::cs_control(bool level) { + auto pin = par_cs; + if (pin < 0) return; + if (level) { + gpio_hi(pin); + } + else { + gpio_lo(pin); + } +} + +void uDisplay::_pb_init_pin(bool read) { + if (read) { + if (interface == _UDSP_PAR8) { + for (size_t i = 0; i < 8; ++i) { + gpio_ll_output_disable(&GPIO, (gpio_num_t)par_dbl[i]); + } + } else { + for (size_t i = 0; i < 8; ++i) { + gpio_ll_output_disable(&GPIO, (gpio_num_t)par_dbl[i]); + } + for (size_t i = 0; i < 8; ++i) { + gpio_ll_output_disable(&GPIO, (gpio_num_t)par_dbh[i]); + } + } + } + else { + auto idx_base = LCD_DATA_OUT0_IDX; + if (interface == _UDSP_PAR8) { + for (size_t i = 0; i < 8; ++i) { + gpio_matrix_out(par_dbl[i], idx_base + i, 0, 0); + } + } else { + for (size_t i = 0; i < 8; ++i) { + gpio_matrix_out(par_dbl[i], idx_base + i, 0, 0); + } + for (size_t i = 0; i < 8; ++i) { + gpio_matrix_out(par_dbh[i], idx_base + 8 + i, 0, 0); + } + } + } +} + +// ===== ESP32-S3 Simple Resistive Touch ===== + +uint32_t uDisplay::get_sr_touch(uint32_t _xp, uint32_t _xm, uint32_t _yp, uint32_t _ym) { + uint32_t aval = 0; + uint16_t xp,yp; + if (pb_busy()) return 0; + + _pb_init_pin(true); + gpio_matrix_out(par_rs, 0x100, 0, 0); + + pinMode(_ym, INPUT_PULLUP); // d0 + pinMode(_yp, INPUT_PULLUP); // rs + + pinMode(_xm, OUTPUT); // cs + pinMode(_xp, OUTPUT); // d1 + digitalWrite(_xm, HIGH); // cs + digitalWrite(_xp, LOW); // d1 + + xp = 4096 - analogRead(_ym); // d0 + + pinMode(_xm, INPUT_PULLUP); // cs + pinMode(_xp, INPUT_PULLUP); // d1 + + pinMode(_ym, OUTPUT); // d0 + pinMode(_yp, OUTPUT); // rs + digitalWrite(_ym, HIGH); // d0 + digitalWrite(_yp, LOW); // rs + + yp = 4096 - analogRead(_xp); // d1 + + aval = (xp << 16) | yp; + + pinMode(_yp, OUTPUT); // rs + pinMode(_xm, OUTPUT); // cs + pinMode(_ym, OUTPUT); // d0 + pinMode(_xp, OUTPUT); // d1 + digitalWrite(_yp, HIGH); // rs + digitalWrite(_xm, HIGH); // cs + + _pb_init_pin(false); + gpio_matrix_out(par_rs, LCD_DC_IDX, 0, 0); + + return aval; +} + +#endif // USE_ESP32_S3 \ No newline at end of file diff --git a/lib/lib_display/UDisplay/uDisplay_graphics.cpp b/lib/lib_display/UDisplay/uDisplay_graphics.cpp new file mode 100644 index 000000000..256facc89 --- /dev/null +++ b/lib/lib_display/UDisplay/uDisplay_graphics.cpp @@ -0,0 +1,565 @@ +#include "uDisplay.h" +#include "uDisplay_config.h" +#include "uDisplay_spi.h" + +// ===== Basic Drawing Primitives ===== + +static constexpr uint16_t RGB16_TO_MONO = 0x8410; +static constexpr uint16_t RGB16_SWAP_TO_MONO = 0x1084; +#define renderer_swap(a, b) { int16_t t = a; a = b; b = t; } + +void uDisplay::drawPixel(int16_t x, int16_t y, uint16_t color) { +#ifdef USE_ESP32_S3 + if (interface == _UDSP_RGB) { + drawPixel_RGB(x, y, color); + return; + } +#endif + + if (ep_mode) { + drawPixel_EPD(x, y, color); + return; + } + + if (framebuffer) { + Renderer::drawPixel(x, y, color); + return; + } + + if ((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) return; + + SPI_BEGIN_TRANSACTION + SPI_CS_LOW + setAddrWindow_int(x, y, 1, 1); + WriteColor(color); + SPI_CS_HIGH + SPI_END_TRANSACTION +} + +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 (framebuffer) { + Renderer::drawFastHLine(x, y, w, color); + return; + } + + // Rudimentary clipping + if((x >= _width) || (y >= _height)) return; + if((x + w - 1) >= _width) w = _width - x; + +#ifdef USE_ESP32_S3 + if (interface == _UDSP_RGB) { + if (cur_rot > 0) { + while (w--) { + drawPixel_RGB(x , y , color); + x++; + } + } else { + uint16_t *fb = rgb_fb; + fb += (int32_t)y * _width; + fb += x; + while (w--) { + *fb = color; + Cache_WriteBack_Addr((uint32_t)fb, 2); + fb++; + x++; + } + } + return; + } +#endif + + SPI_BEGIN_TRANSACTION + SPI_CS_LOW + setAddrWindow_int(x, y, w, 1); + + if (col_mode == 18) { + uint8_t r = (color & 0xF800) >> 11; + uint8_t g = (color & 0x07E0) >> 5; + uint8_t b = color & 0x001F; + r = (r * 255) / 31; + g = (g * 255) / 63; + b = (b * 255) / 31; + + while (w--) { + ulcd_data8(r); + ulcd_data8(g); + ulcd_data8(b); + } + } else { + while (w--) { + WriteColor(color); + } + } + + SPI_CS_HIGH + SPI_END_TRANSACTION +} + +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 (framebuffer) { + Renderer::drawFastVLine(x, y, h, color); + return; + } + + // Rudimentary clipping + if ((x >= _width) || (y >= _height)) return; + if ((y + h - 1) >= _height) h = _height - y; + +#ifdef USE_ESP32_S3 + if (interface == _UDSP_RGB) { + if (cur_rot > 0) { + while (h--) { + drawPixel_RGB(x , y , color); + y++; + } + } else { + uint16_t *fb = rgb_fb; + fb += (int32_t)y * _width; + fb += x; + while (h--) { + *fb = color; + Cache_WriteBack_Addr((uint32_t)fb, 2); + fb+=_width; + y++; + } + } + return; + } +#endif + + SPI_BEGIN_TRANSACTION + SPI_CS_LOW + setAddrWindow_int(x, y, 1, h); + + if (col_mode == 18) { + uint8_t r = (color & 0xF800) >> 11; + uint8_t g = (color & 0x07E0) >> 5; + uint8_t b = color & 0x001F; + r = (r * 255) / 31; + g = (g * 255) / 63; + b = (b * 255) / 31; + + while (h--) { + ulcd_data8(r); + ulcd_data8(g); + ulcd_data8(b); + } + } else { + while (h--) { + WriteColor(color); + } + } + + SPI_CS_HIGH + SPI_END_TRANSACTION +} + +void uDisplay::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { +#ifdef USE_ESP32_S3 + if (interface == _UDSP_RGB) { + for (uint32_t yp = y; yp < y + h; yp++) { + drawFastHLine(x, yp, w, color); + } + return; + } +#endif + + if (ep_mode) { + fillRect_EPD(x, y, w, h, color); + return; + } + + if (framebuffer) { + Renderer::fillRect(x, y, w, h, color); + return; + } + + if((x >= _width) || (y >= _height)) return; + if((x + w - 1) >= _width) w = _width - x; + if((y + h - 1) >= _height) h = _height - y; + + SPI_BEGIN_TRANSACTION + SPI_CS_LOW + setAddrWindow_int(x, y, w, h); + + if (col_mode == 18) { + uint8_t r = (color & 0xF800) >> 11; + uint8_t g = (color & 0x07E0) >> 5; + uint8_t b = color & 0x001F; + r = (r * 255) / 31; + g = (g * 255) / 63; + b = (b * 255) / 31; + + for (y = h; y > 0; y--) { + for (x = w; x > 0; x--) { + ulcd_data8(r); + ulcd_data8(g); + ulcd_data8(b); + } + } + } else { + for (y = h; y > 0; y--) { + for (x = w; x > 0; x--) { + WriteColor(color); + } + } + } + + SPI_CS_HIGH + SPI_END_TRANSACTION +} + +void uDisplay::fillScreen(uint16_t color) { + fillRect(0, 0, width(), height(), color); +} + +static inline void lvgl_color_swap(uint16_t *data, uint16_t len) { for (uint32_t i = 0; i < len; i++) (data[i] = data[i] << 8 | data[i] >> 8); } + +void uDisplay::pushColors(uint16_t *data, uint16_t len, boolean not_swapped) { + + if (lvgl_param.swap_color) { + not_swapped = !not_swapped; + } + + //Serial.printf("push %x - %d - %d - %d\n", (uint32_t)data, len, not_swapped, lvgl_param.data); + + // Isolating _UDSP_RGB to increase code sharing + // + // Use ESP-IDF LCD driver to push colors and rely on the following assumptions: + // * bytes swapping is already handled in the driver configuration (see uDisplay::Init()), + // * pushColors() is only called with not_swapped equals true, + // * cache flushing is done by the LCD driver. + if (interface == _UDSP_RGB) { +#ifdef USE_ESP32_S3 + if (!not_swapped) { + // internal error -> write error message but continue (with possibly wrong colors) + AddLog(LOG_LEVEL_ERROR, PSTR("DSP: Unexpected byte-swapping requested in pushColors()")); + } + + // check that bytes count matches the size of area, and remove from inner loop + if ((seta_yp2 - seta_yp1) * (seta_xp2 - seta_xp2) > len) { return; } + + esp_lcd_panel_draw_bitmap(_panel_handle, seta_xp1, seta_yp1, seta_xp2, seta_yp2, (void *)data); +#endif + return; + } + + + if (not_swapped == false) { + // called from LVGL bytes are swapped + if (bpp != 16) { + // lvgl_color_swap(data, len); -- no need to swap anymore, we have inverted the mask + pushColorsMono(data, len, true); + return; + } + + if ( (col_mode != 18) && (spi_dc >= 0) && (spi_nr <= 2) ) { + // special version 8 bit spi I or II +#ifdef ESP8266 + lvgl_color_swap(data, len); + while (len--) { + uspi->write(*data++); + } +#else + if (lvgl_param.use_dma) { + pushPixelsDMA(data, len ); + } else { + uspi->writeBytes((uint8_t*)data, len * 2); + } +#endif + } else { + +#ifdef ESP32 + if ( (col_mode == 18) && (spi_dc >= 0) && (spi_nr <= 2) ) { + uint8_t *line = (uint8_t*)malloc(len * 3); + uint8_t *lp = line; + if (line) { + uint16_t color; + for (uint32_t cnt = 0; cnt < len; cnt++) { + color = *data++; + color = (color << 8) | (color >> 8); + uint8_t r = (color & 0xF800) >> 11; + uint8_t g = (color & 0x07E0) >> 5; + uint8_t b = color & 0x001F; + r = (r * 255) / 31; + g = (g * 255) / 63; + b = (b * 255) / 31; + *lp++ = r; + *lp++ = g; + *lp++ = b; + } + + if (lvgl_param.use_dma) { + pushPixels3DMA(line, len ); + } else { + uspi->writeBytes(line, len * 3); + } + free(line); + } + + } else { + // 9 bit and others + if (interface == _UDSP_PAR8 || interface == _UDSP_PAR16) { + #ifdef USE_ESP32_S3 + pb_pushPixels(data, len, true, false); + #endif // USE_ESP32_S3 + } else { + lvgl_color_swap(data, len); + while (len--) { + WriteColor(*data++); + } + } + } +#endif // ESP32 + +#ifdef ESP8266 + lvgl_color_swap(data, len); + while (len--) { + WriteColor(*data++); + } +#endif + } + } else { + // called from displaytext, no byte swap, currently no dma here + + if (bpp != 16) { + pushColorsMono(data, len); + return; + } + if ( (col_mode != 18) && (spi_dc >= 0) && (spi_nr <= 2) ) { + // special version 8 bit spi I or II + #ifdef ESP8266 + while (len--) { + //uspi->write(*data++); + WriteColor(*data++); + } + #else + uspi->writePixels(data, len * 2); + #endif + } else { + // 9 bit and others + if (interface == _UDSP_PAR8 || interface == _UDSP_PAR16) { +#ifdef USE_ESP32_S3 + pb_pushPixels(data, len, false, false); +#endif // USE_ESP32_S3 + } else { + while (len--) { + WriteColor(*data++); + } + } + } + } +} + +// convert to mono, these are framebuffer based +void uDisplay::pushColorsMono(uint16_t *data, uint16_t len, bool rgb16_swap) { + // pixel is white if at least one of the 3 components is above 50% + // this is tested with a simple mask, swapped if needed + uint16_t rgb16_to_mono_mask = rgb16_swap ? RGB16_SWAP_TO_MONO : RGB16_TO_MONO; + + for (uint32_t y = seta_yp1; y < seta_yp2; y++) { + seta_yp1++; + if (lvgl_param.invert_bw) { + for (uint32_t x = seta_xp1; x < seta_xp2; x++) { + uint16_t color = *data++; + if (bpp == 1) color = (color & rgb16_to_mono_mask) ? 0 : 1; + drawPixel(x, y, color); // todo - inline the method to save speed + len--; + if (!len) return; // failsafe - exist if len (pixel number) is exhausted + } + } else { + for (uint32_t x = seta_xp1; x < seta_xp2; x++) { + uint16_t color = *data++; + if (bpp == 1) color = (color & rgb16_to_mono_mask) ? 1 : 0; + drawPixel(x, y, color); // todo - inline the method to save speed + len--; + if (!len) return; // failsafe - exist if len (pixel number) is exhausted + } + } + } +} + +void uDisplay::setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { + if (bpp != 16 || interface == _UDSP_RGB) { + // Just save params or update frame + if (!x0 && !y0 && !x1 && !y1) { + if (!ep_mode) { + Updateframe(); + } + } else { + seta_xp1 = x0; + seta_xp2 = x1; + seta_yp1 = y0; + seta_yp2 = y1; + } + return; + } + + if (interface == _UDSP_RGB) { + return; + } + + if (!x0 && !y0 && !x1 && !y1) { + SPI_CS_HIGH + SPI_END_TRANSACTION + } else { + SPI_BEGIN_TRANSACTION + SPI_CS_LOW + setAddrWindow_int(x0, y0, x1 - x0, y1 - y0); + } +} + +void uDisplay::setAddrWindow_int(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { + if (interface == _UDSP_RGB) { + return; + } + + x += x_addr_offs[cur_rot]; + y += y_addr_offs[cur_rot]; + + if (sa_mode != 8) { + uint32_t xa = ((uint32_t)x << 16) | (x + w - 1); + uint32_t ya = ((uint32_t)y << 16) | (y + h - 1); + + ulcd_command(saw_1); + ulcd_data32(xa); + + ulcd_command(saw_2); + ulcd_data32(ya); + + if (saw_3 != 0xff) { + ulcd_command(saw_3); // write to RAM + } + } else { + uint16_t x2 = x + w - 1, + y2 = y + h - 1; + + if (cur_rot & 1) { // Vertical address increment mode + renderer_swap(x,y); + renderer_swap(x2,y2); + } + ulcd_command(saw_1); + if (allcmd_mode) { + ulcd_data8(x); + ulcd_data8(x2); + } else { + ulcd_command(x); + ulcd_command(x2); + } + ulcd_command(saw_2); + if (allcmd_mode) { + ulcd_data8(y); + ulcd_data8(y2); + } else { + ulcd_command(y); + ulcd_command(y2); + } + if (saw_3 != 0xff) { + ulcd_command(saw_3); // write to RAM + } + } +} + +void uDisplay::setRotation(uint8_t rotation) { + cur_rot = rotation; + + if (framebuffer) { + Renderer::setRotation(cur_rot); + return; + } + + if (interface == _UDSP_SPI || interface == _UDSP_PAR8 || interface == _UDSP_PAR16) { + if (ep_mode) { + Renderer::setRotation(cur_rot); + return; + } + SPI_BEGIN_TRANSACTION + SPI_CS_LOW + ulcd_command(madctrl); + + if (!allcmd_mode) { + ulcd_data8(rot[cur_rot]); + } else { + ulcd_command(rot[cur_rot]); + } + + if ((sa_mode == 8) && !allcmd_mode) { + ulcd_command(startline); + ulcd_data8((cur_rot < 2) ? height() : 0); + } + + SPI_CS_HIGH + SPI_END_TRANSACTION + } + + switch (rotation) { + case 0: + _width = gxs; + _height = gys; + break; + case 1: + _width = gys; + _height = gxs; + break; + case 2: + _width = gxs; + _height = gys; + break; + case 3: + _width = gys; + _height = gxs; + break; + } + +#ifdef USE_ESP32_S3 + if (interface == _UDSP_RGB) { + // Utilize the ESP-IDF LCD driver's support for display rotation + esp_lcd_panel_mirror(_panel_handle, rotation == 1 || rotation == 2, rotation & 2); + esp_lcd_panel_swap_xy(_panel_handle, rotation & 1); + } +#endif +} + +#ifdef USE_ESP32_S3 +void uDisplay::drawPixel_RGB(int16_t x, int16_t y, uint16_t color) { + int16_t w = _width, h = _height; + + if ((x < 0) || (x >= w) || (y < 0) || (y >= h)) { + return; + } + + // check rotation, move pixel around if necessary + switch (cur_rot) { + case 1: + renderer_swap(w, h); + renderer_swap(x, y); + x = w - x - 1; + break; + case 2: + x = w - x - 1; + y = h - y - 1; + break; + case 3: + renderer_swap(w, h); + renderer_swap(x, y); + y = h - y - 1; + break; + } + + uint16_t *fb = rgb_fb; + fb += (int32_t)y * w; + fb += x; + *fb = color; + Cache_WriteBack_Addr((uint32_t)fb, 2); +} +#endif \ No newline at end of file diff --git a/lib/lib_display/UDisplay/uDisplay_parallel.cpp b/lib/lib_display/UDisplay/uDisplay_parallel.cpp new file mode 100644 index 000000000..d3f39f559 --- /dev/null +++ b/lib/lib_display/UDisplay/uDisplay_parallel.cpp @@ -0,0 +1,142 @@ +#include "uDisplay.h" +#include "uDisplay_config.h" + +#ifdef USE_ESP32_S3 + +#ifdef ESP32 +#include "esp8266toEsp32.h" +#endif + +#define WAIT_LCD_NOT_BUSY while (*reg_lcd_user & LCD_CAM_LCD_START) {} + +// ===== Parallel Bus Control Functions ===== + +void uDisplay::pb_beginTransaction(void) { + auto dev = _dev; + dev->lcd_clock.val = _clock_reg_value; + dev->lcd_misc.val = LCD_CAM_LCD_CD_IDLE_EDGE; + dev->lcd_user.val = LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG; + _cache_flip = _cache[0]; +} + +void uDisplay::pb_endTransaction(void) { + auto dev = _dev; + while (dev->lcd_user.val & LCD_CAM_LCD_START) {} +} + +void uDisplay::pb_wait(void) { + auto dev = _dev; + while (dev->lcd_user.val & LCD_CAM_LCD_START) {} +} + +bool uDisplay::pb_busy(void) { + auto dev = _dev; + return (dev->lcd_user.val & LCD_CAM_LCD_START); +} + +// ===== Parallel Bus Write Functions ===== + +bool uDisplay::pb_writeCommand(uint32_t data, uint_fast8_t bit_length) { + auto dev = _dev; + auto reg_lcd_user = &(dev->lcd_user.val); + dev->lcd_misc.val = LCD_CAM_LCD_CD_IDLE_EDGE | LCD_CAM_LCD_CD_CMD_SET; + + if (interface == _UDSP_PAR8) { + auto bytes = bit_length >> 3; + do { + dev->lcd_cmd_val.lcd_cmd_value = data; + data >>= 8; + WAIT_LCD_NOT_BUSY + *reg_lcd_user = LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; + } while (--bytes); + return true; + } else { + dev->lcd_cmd_val.val = data; + WAIT_LCD_NOT_BUSY + *reg_lcd_user = LCD_CAM_LCD_2BYTE_EN | LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; + return true; + } +} + +void uDisplay::pb_writeData(uint32_t data, uint_fast8_t bit_length) { + auto dev = _dev; + auto reg_lcd_user = &(dev->lcd_user.val); + dev->lcd_misc.val = LCD_CAM_LCD_CD_IDLE_EDGE; + auto bytes = bit_length >> 3; + + if (interface == _UDSP_PAR8) { + uint8_t shift = (bytes - 1) * 8; + for (uint32_t cnt = 0; cnt < bytes; cnt++) { + dev->lcd_cmd_val.lcd_cmd_value = (data >> shift) & 0xff; + shift -= 8; + WAIT_LCD_NOT_BUSY + *reg_lcd_user = LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; + } + return; + } else { + if (bytes == 1 || bytes == 4) { + uint8_t shift = (bytes - 1) * 8; + for (uint32_t cnt = 0; cnt < bytes; cnt++) { + dev->lcd_cmd_val.lcd_cmd_value = (data >> shift) & 0xff; + shift -= 8; + WAIT_LCD_NOT_BUSY + *reg_lcd_user = LCD_CAM_LCD_2BYTE_EN | LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; + } + return; + } + + dev->lcd_cmd_val.val = data; + WAIT_LCD_NOT_BUSY + *reg_lcd_user = LCD_CAM_LCD_2BYTE_EN | LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; + return; + } +} + +void uDisplay::pb_pushPixels(uint16_t* data, uint32_t length, bool swap_bytes, bool use_dma) { + auto dev = _dev; + auto reg_lcd_user = &(dev->lcd_user.val); + dev->lcd_misc.val = LCD_CAM_LCD_CD_IDLE_EDGE; + + if (interface == _UDSP_PAR8) { + if (swap_bytes) { + for (uint32_t cnt = 0; cnt < length; cnt++) { + dev->lcd_cmd_val.lcd_cmd_value = *data; + while (*reg_lcd_user & LCD_CAM_LCD_START) {} + *reg_lcd_user = LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; + dev->lcd_cmd_val.lcd_cmd_value = *data >> 8; + WAIT_LCD_NOT_BUSY + *reg_lcd_user = LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; + data++; + } + } else { + for (uint32_t cnt = 0; cnt < length; cnt++) { + dev->lcd_cmd_val.lcd_cmd_value = *data >> 8; + while (*reg_lcd_user & LCD_CAM_LCD_START) {} + *reg_lcd_user = LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; + dev->lcd_cmd_val.lcd_cmd_value = *data; + WAIT_LCD_NOT_BUSY + *reg_lcd_user = LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; + data++; + } + } + } else { + if (swap_bytes) { + uint16_t iob; + for (uint32_t cnt = 0; cnt < length; cnt++) { + iob = *data++; + iob = (iob << 8) | (iob >> 8); + dev->lcd_cmd_val.lcd_cmd_value = iob; + WAIT_LCD_NOT_BUSY + *reg_lcd_user = LCD_CAM_LCD_2BYTE_EN | LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; + } + } else { + for (uint32_t cnt = 0; cnt < length; cnt++) { + dev->lcd_cmd_val.lcd_cmd_value = *data++; + WAIT_LCD_NOT_BUSY + *reg_lcd_user = LCD_CAM_LCD_2BYTE_EN | LCD_CAM_LCD_CMD | LCD_CAM_LCD_UPDATE_REG | LCD_CAM_LCD_START; + } + } + } +} + +#endif \ No newline at end of file diff --git a/lib/lib_display/UDisplay/uDisplay_spi.cpp b/lib/lib_display/UDisplay/uDisplay_spi.cpp new file mode 100644 index 000000000..2a11abe94 --- /dev/null +++ b/lib/lib_display/UDisplay/uDisplay_spi.cpp @@ -0,0 +1,154 @@ +#include "uDisplay.h" +#include "uDisplay_config.h" +#include "uDisplay_spi.h" +#ifndef ESP32 +#include "spi_register.h" +#endif + +// ===== Low-Level SPI Write Functions ===== +// ===== RA8876 Controller Commands ===== +static constexpr uint8_t RA8876_DATA_WRITE = 0x80; +static constexpr uint8_t RA8876_DATA_READ = 0xC0; +static constexpr uint8_t RA8876_CMD_WRITE = 0x00; +static constexpr uint8_t RA8876_STATUS_READ = 0x40; + +#ifdef ESP32 +void uDisplay::hw_write9(uint8_t val, uint8_t dc) { + if (spi_dc < -1) { + // RA8876 mode + if (!dc) { + uspi->write(RA8876_CMD_WRITE); + uspi->write(val); + } else { + uspi->write(RA8876_DATA_WRITE); + uspi->write(val); + } + } else { + uint32_t regvalue = val >> 1; + if (dc) regvalue |= 0x80; + else regvalue &= 0x7f; + if (val & 1) regvalue |= 0x8000; + + REG_SET_BIT(SPI_USER_REG(3), SPI_USR_MOSI); + REG_WRITE(SPI_MOSI_DLEN_REG(3), 9 - 1); + uint32_t *dp = (uint32_t*)SPI_W0_REG(3); + *dp = regvalue; + REG_SET_BIT(SPI_CMD_REG(3), SPI_USR); + while (REG_GET_FIELD(SPI_CMD_REG(3), SPI_USR)); + } +} +#else +void uDisplay::hw_write9(uint8_t val, uint8_t dc) { + if (spi_dc < -1) { + // RA8876 mode + if (!dc) { + uspi->write(RA8876_CMD_WRITE); + uspi->write(val); + } else { + uspi->write(RA8876_DATA_WRITE); + uspi->write(val); + } + } else { + uint32_t regvalue; + uint8_t bytetemp; + if (!dc) { + bytetemp = (val>> 1) & 0x7f; + } else { + bytetemp = (val >> 1) | 0x80; + } + regvalue = ((8 & SPI_USR_COMMAND_BITLEN) << SPI_USR_COMMAND_BITLEN_S) | ((uint32)bytetemp); + if (val & 0x01) regvalue |= BIT15; + while (READ_PERI_REG(SPI_CMD(1)) & SPI_USR); + WRITE_PERI_REG(SPI_USER2(1), regvalue); + SET_PERI_REG_MASK(SPI_CMD(1), SPI_USR); + } +} +#endif + +// ===== Software SPI (Slow) Functions ===== + +void uDisplay::write8(uint8_t val) { + for (uint8_t bit = 0x80; bit; bit >>= 1) { + GPIO_CLR(spi_clk); + if (val & bit) GPIO_SET(spi_mosi); + else GPIO_CLR(spi_mosi); + GPIO_SET(spi_clk); + } +} + +void uDisplay::write8_slow(uint8_t val) { + for (uint8_t bit = 0x80; bit; bit >>= 1) { + GPIO_CLR_SLOW(spi_clk); + if (val & bit) GPIO_SET_SLOW(spi_mosi); + else GPIO_CLR_SLOW(spi_mosi); + GPIO_SET_SLOW(spi_clk); + } +} + +void uDisplay::write9(uint8_t val, uint8_t dc) { + GPIO_CLR(spi_clk); + if (dc) GPIO_SET(spi_mosi); + else GPIO_CLR(spi_mosi); + GPIO_SET(spi_clk); + + for (uint8_t bit = 0x80; bit; bit >>= 1) { + GPIO_CLR(spi_clk); + if (val & bit) GPIO_SET(spi_mosi); + else GPIO_CLR(spi_mosi); + GPIO_SET(spi_clk); + } +} + +void uDisplay::write9_slow(uint8_t val, uint8_t dc) { + GPIO_CLR_SLOW(spi_clk); + if (dc) GPIO_SET_SLOW(spi_mosi); + else GPIO_CLR_SLOW(spi_mosi); + GPIO_SET_SLOW(spi_clk); + + for (uint8_t bit = 0x80; bit; bit >>= 1) { + GPIO_CLR_SLOW(spi_clk); + if (val & bit) GPIO_SET_SLOW(spi_mosi); + else GPIO_CLR_SLOW(spi_mosi); + GPIO_SET_SLOW(spi_clk); + } +} + +void uDisplay::write16(uint16_t val) { + for (uint16_t bit = 0x8000; bit; bit >>= 1) { + GPIO_CLR(spi_clk); + if (val & bit) GPIO_SET(spi_mosi); + else GPIO_CLR(spi_mosi); + GPIO_SET(spi_clk); + } +} + +void uDisplay::write32(uint32_t val) { + for (uint32_t bit = 0x80000000; bit; bit >>= 1) { + GPIO_CLR(spi_clk); + if (val & bit) GPIO_SET(spi_mosi); + else GPIO_CLR(spi_mosi); + GPIO_SET(spi_clk); + } +} + +// ===== RA8876 Specific Functions ===== + +uint8_t uDisplay::writeReg16(uint8_t reg, uint16_t wval) { + hw_write9(reg, 0); + hw_write9(wval, 1); + hw_write9(reg + 1, 0); + hw_write9(wval >> 8, 1); + return 0; +} + +uint8_t uDisplay::readData(void) { + uspi->write(RA8876_DATA_READ); + uint8_t val = uspi->transfer(0); + return val; +} + +uint8_t uDisplay::readStatus(void) { + uspi->write(RA8876_STATUS_READ); + uint8_t val = uspi->transfer(0); + return val; +} \ No newline at end of file diff --git a/lib/lib_display/UDisplay/uDisplay_spi.h b/lib/lib_display/UDisplay/uDisplay_spi.h new file mode 100644 index 000000000..113832a15 --- /dev/null +++ b/lib/lib_display/UDisplay/uDisplay_spi.h @@ -0,0 +1,71 @@ +#ifndef UDISPLAY_SPI_LOWLEVEL_H +#define UDISPLAY_SPI_LOWLEVEL_H + +#include "uDisplay_config.h" + +// ===== SPI Platform-Specific Includes ===== +#ifdef ESP32 +#include "soc/spi_reg.h" +#include "soc/spi_struct.h" +#include "esp32-hal-spi.h" +#include "esp32-hal.h" +#include "soc/spi_struct.h" +#endif + +// ===== GPIO Control Macros ===== + +#ifdef ESP8266 +#define PIN_OUT_SET 0x60000304 +#define PIN_OUT_CLEAR 0x60000308 +#define GPIO_SET(A) WRITE_PERI_REG( PIN_OUT_SET, 1 << A) +#define GPIO_CLR(A) WRITE_PERI_REG( PIN_OUT_CLEAR, 1 << A) +#define GPIO_CLR_SLOW(A) digitalWrite(A, LOW) +#define GPIO_SET_SLOW(A) digitalWrite(A, HIGH) +#else // ESP32 +#undef GPIO_SET +#undef GPIO_CLR +#undef GPIO_SET_SLOW +#undef GPIO_CLR_SLOW + +#if CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32P4 +#define GPIO_CLR(A) GPIO.out_w1tc.val = (1 << A) +#define GPIO_SET(A) GPIO.out_w1ts.val = (1 << A) +#else // plain ESP32 +#define GPIO_CLR(A) GPIO.out_w1tc = (1 << A) +#define GPIO_SET(A) GPIO.out_w1ts = (1 << A) +#endif + +#define GPIO_CLR_SLOW(A) digitalWrite(A, LOW) +#define GPIO_SET_SLOW(A) digitalWrite(A, HIGH) +#endif + +// ===== SPI Transaction Control Macros ===== +#define SPI_BEGIN_TRANSACTION if (spi_nr <= 2) beginTransaction(spiSettings); +#define SPI_END_TRANSACTION if (spi_nr <= 2) endTransaction(); + +#define SPI_CS_LOW if (spi_cs >= 0) GPIO_CLR_SLOW(spi_cs); +#define SPI_CS_HIGH if (spi_cs >= 0) GPIO_SET_SLOW(spi_cs); +#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); + +// ===== Function Declarations ===== +// These would typically be in the class declaration in uDisplay.h +// but we list them here for reference: + +/* +// Low-Level SPI Write Functions +void hw_write9(uint8_t val, uint8_t dc); +void write8(uint8_t val); +void write8_slow(uint8_t val); +void write9(uint8_t val, uint8_t dc); +void write9_slow(uint8_t val, uint8_t dc); +void write16(uint16_t val); +void write32(uint32_t val); + +// RA8876 Specific Functions +uint8_t writeReg16(uint8_t reg, uint16_t wval); +uint8_t readData(void); +uint8_t readStatus(void); +*/ + +#endif // UDISPLAY_SPI_LOWLEVEL_H \ No newline at end of file diff --git a/lib/lib_display/UDisplay/uDisplay_spi_comm.cpp b/lib/lib_display/UDisplay/uDisplay_spi_comm.cpp new file mode 100644 index 000000000..d04197bcf --- /dev/null +++ b/lib/lib_display/UDisplay/uDisplay_spi_comm.cpp @@ -0,0 +1,158 @@ +#include "uDisplay.h" +#include "uDisplay_config.h" +#include "uDisplay_spi.h" // Your SPI header + +// ===== High-Level SPI Communication Functions ===== + +void uDisplay::ulcd_command(uint8_t val) { + if (interface == _UDSP_SPI) { + if (spi_dc < 0) { + if (spi_nr > 2) { + if (spi_nr == 3) { + write9(val, 0); + } else { + write9_slow(val, 0); + } + } else { + hw_write9(val, 0); + } + } else { + SPI_DC_LOW + if (spi_nr > 2) { + if (spi_nr == 3) { + write8(val); + } else { + write8_slow(val); + } + } else { + uspi->write(val); + } + SPI_DC_HIGH + } + return; + } + +#ifdef USE_ESP32_S3 + if (interface == _UDSP_PAR8 || interface == _UDSP_PAR16) { + pb_writeCommand(val, 8); + } +#endif +} + +void uDisplay::ulcd_data8(uint8_t val) { + if (interface == _UDSP_SPI) { + if (spi_dc < 0) { + if (spi_nr > 2) { + if (spi_nr == 3) { + write9(val, 1); + } else { + write9_slow(val, 1); + } + } else { + hw_write9(val, 1); + } + } else { + if (spi_nr > 2) { + if (spi_nr == 3) { + write8(val); + } else { + write8_slow(val); + } + } else { + uspi->write(val); + } + } + return; + } + +#ifdef USE_ESP32_S3 + if (interface == _UDSP_PAR8 || interface == _UDSP_PAR16) { + pb_writeData(val, 8); + } +#endif +} + +void uDisplay::ulcd_data16(uint16_t val) { + if (interface == _UDSP_SPI) { + if (spi_dc < 0) { + if (spi_nr > 2) { + write9(val >> 8, 1); + write9(val, 1); + } else { + hw_write9(val >> 8, 1); + hw_write9(val, 1); + } + } else { + if (spi_nr > 2) { + write16(val); + } else { + uspi->write16(val); + } + } + return; + } + +#ifdef USE_ESP32_S3 + if (interface == _UDSP_PAR8 || interface == _UDSP_PAR16) { + pb_writeData(val, 16); + } +#endif +} + +void uDisplay::ulcd_data32(uint32_t val) { + if (interface == _UDSP_SPI) { + if (spi_dc < 0) { + if (spi_nr > 2) { + write9(val >> 24, 1); + write9(val >> 16, 1); + write9(val >> 8, 1); + write9(val, 1); + } else { + hw_write9(val >> 24, 1); + hw_write9(val >> 16, 1); + hw_write9(val >> 8, 1); + hw_write9(val, 1); + } + } else { + if (spi_nr > 2) { + write32(val); + } else { + uspi->write32(val); + } + } + return; + } + +#ifdef USE_ESP32_S3 + if (interface == _UDSP_PAR8 || interface == _UDSP_PAR16) { + pb_writeData(val, 32); + } +#endif +} + +void uDisplay::ulcd_command_one(uint8_t val) { + if (interface == _UDSP_SPI) { + SPI_BEGIN_TRANSACTION + SPI_CS_LOW + ulcd_command(val); + SPI_CS_HIGH + SPI_END_TRANSACTION + } +} + +void uDisplay::WriteColor(uint16_t color) { + if (col_mode == 18) { + uint8_t r = (color & 0xF800) >> 11; + uint8_t g = (color & 0x07E0) >> 5; + uint8_t b = color & 0x001F; + r = (r * 255) / 31; + g = (g * 255) / 63; + b = (b * 255) / 31; + + ulcd_data8(r); + ulcd_data8(g); + ulcd_data8(b); + } else { + ulcd_data16(color); + } +} \ No newline at end of file diff --git a/lib/lib_display/UDisplay/uDisplay_timing.cpp b/lib/lib_display/UDisplay/uDisplay_timing.cpp new file mode 100644 index 000000000..f63170326 --- /dev/null +++ b/lib/lib_display/UDisplay/uDisplay_timing.cpp @@ -0,0 +1,60 @@ +#include "uDisplay.h" +#include "uDisplay_config.h" + +// ===== Timing and Delay Functions ===== + +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); + AddLog(LOG_LEVEL_DEBUG, PSTR("DSP: delay %d ms"), delay_ms); + } +} + +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 + +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); + } +} + +// ===== SPI Transaction Control ===== + +void uDisplay::beginTransaction(SPISettings s) { +#ifdef ESP32 + if (lvgl_param.use_dma) { + dmaWait(); + } +#endif + uspi->beginTransaction(s); +} + +void uDisplay::endTransaction(void) { + uspi->endTransaction(); +} \ No newline at end of file diff --git a/lib/lib_display/UDisplay/uDisplay_touch.cpp b/lib/lib_display/UDisplay/uDisplay_touch.cpp new file mode 100644 index 000000000..6afa12637 --- /dev/null +++ b/lib/lib_display/UDisplay/uDisplay_touch.cpp @@ -0,0 +1,568 @@ +#include "uDisplay.h" +#include "uDisplay_config.h" + +#ifdef USE_UNIVERSAL_TOUCH + +// ===== Touch IRQ Handler ===== + +uint8_t ut_irq_flg; + +void IRAM_ATTR ut_touch_irq(void) { + ut_irq_flg = 1; +} + +// ===== Touch Initialization ===== + +bool uDisplay::utouch_Init(char **name) { + *name = ut_name; + + if (ut_init_code) { + if (ut_reset >= 0) { + pinMode(ut_reset, OUTPUT); + digitalWrite(ut_reset, HIGH); + delay(10); + digitalWrite(ut_reset, LOW); + delay(5); + digitalWrite(ut_reset, HIGH); + delay(10); + } + + if (ut_irq >= 0) { + pinMode(ut_irq, INPUT); + attachInterrupt(ut_irq, ut_touch_irq, FALLING); + } + + if (ut_spi_nr == spi_nr) { + // same as display + ut_spi = uspi; + } else { +#ifdef ESP32 + ut_spi = SpiBegin(ut_spi_nr); +#endif + } + + return ut_execute(ut_init_code); + } + + return false; +} + +// ===== Touch Detection ===== + +uint16_t uDisplay::touched(void) { + if (ut_irq >= 0) { + if (!ut_irq_flg) { + return false; + } + ut_irq_flg = 0; + } + + if (ut_touch_code) { + return ut_execute(ut_touch_code); + } + + return 0; +} + +// ===== Touch Coordinate Reading ===== + +int16_t uDisplay::getPoint_x(void) { + if (ut_getx_code) { + return ut_execute(ut_getx_code); + } + return 0; +} + +int16_t uDisplay::getPoint_y(void) { + if (ut_gety_code) { + return ut_execute(ut_gety_code); + } + return 0; +} + +// ===== Touch Command Execution ===== + +// ===== Touch Code Translation ===== + +void uDisplay::ut_trans(char **sp, uint8_t **code) { + char *cp = *sp; + uint16_t wval; + uint8_t tmp_code[64]; + uint8_t *ut_code = tmp_code; + + while (*cp) { + if (*cp == ':' || *cp == '#') { + break; + } + if (*cp == ';') { + // skip comment line + while (*cp) { + if (*cp == '\n') { + cp++; + break; + } + cp++; + } + } + + if (!strncmp(cp, "RDWM", 4)) { + // read word many + *ut_code++ = UT_RDWM; + wval = ut_par(&cp, 0); + *ut_code++ = wval>>8; + *ut_code++ = wval; + wval = ut_par(&cp, 1); + if (wval > sizeof(ut_array)) { + wval = sizeof(ut_array); + } + *ut_code++ = wval; + } else if (!strncmp(cp, "RDW", 3)) { + // read word one + *ut_code++ = UT_RDW; + wval = ut_par(&cp, 0); + *ut_code++ = wval>>8; + *ut_code++ = wval; + } else if (!strncmp(cp, "RDM", 3)) { + // read many + *ut_code++ = UT_RDM; + *ut_code++ = ut_par(&cp, 0); + wval = ut_par(&cp, 1); + if (wval > sizeof(ut_array)) { + wval = sizeof(ut_array); + } + *ut_code++ = wval; + } else if (!strncmp(cp, "RD", 2)) { + // read one + *ut_code++ = UT_RD; + *ut_code++ = ut_par(&cp, 0); + } else if (!strncmp(cp, "CPR", 3)) { + // cmp and set + *ut_code++ = UT_CPR; + *ut_code++ = ut_par(&cp, 0); + } else if (!strncmp(cp, "CPM", 3)) { + // cmp multiple and set + *ut_code++ = UT_CPM; + uint8_t num = ut_par(&cp, 0); + *ut_code++ = num; + for (uint32_t cnt = 0; cnt < num; cnt++) { + *ut_code++ = ut_par(&cp, 0); + } + } else if (!strncmp(cp, "CP", 2)) { + // cmp and set + *ut_code++ = UT_CP; + *ut_code++ = ut_par(&cp, 0); + } else if (!strncmp(cp, "RTF", 3)) { + // return when false + *ut_code++ = UT_RTF; + } else if (!strncmp(cp, "RTT", 3)) { + // return when true + *ut_code++ = UT_RTT; + } else if (!strncmp(cp, "MVB", 3)) { + // move + *ut_code++ = UT_MVB; + *ut_code++ = ut_par(&cp, 1); + *ut_code++ = ut_par(&cp, 1); + } else if (!strncmp(cp, "MV", 2)) { + // move + *ut_code++ = UT_MV; + *ut_code++ = ut_par(&cp, 1); + *ut_code++ = ut_par(&cp, 1); + } else if (!strncmp(cp, "RT", 2)) { + // return status + *ut_code++ = UT_RT; + } else if (!strncmp(cp, "WRW", 3)) { + *ut_code++ = UT_WRW; + wval = ut_par(&cp, 0); + *ut_code++ = wval>>8; + *ut_code++ = wval; + wval = ut_par(&cp, 0); + *ut_code++ = wval; + } else if (!strncmp(cp, "WR", 2)) { + *ut_code++ = UT_WR; + wval = ut_par(&cp, 0); + *ut_code++ = wval; + wval = ut_par(&cp, 0); + *ut_code++ = wval; + } else if (!strncmp(cp, "AND", 3)) { + *ut_code++ = UT_AND; + wval = ut_par(&cp, 0); + *ut_code++ = wval >> 8; + *ut_code++ = wval; + } else if (!strncmp(cp, "SCL", 3)) { + *ut_code++ = UT_SCALE; + wval = ut_par(&cp, 1); + *ut_code++ = wval >> 8; + *ut_code++ = wval; + uint32_t lval = ut_par(&cp, 2); + *ut_code++ = lval >> 24; + *ut_code++ = lval >> 16; + *ut_code++ = lval >> 8; + *ut_code++ = lval; + } else if (!strncmp(cp, "LIM", 3)) { + *ut_code++ = UT_LIM; + wval = ut_par(&cp, 1); + *ut_code++ = wval >> 8; + *ut_code++ = wval; + } else if (!strncmp(cp, "GSRT", 4)) { + *ut_code++ = UT_GSRT; + wval = ut_par(&cp, 1); + *ut_code++ = wval >> 8; + *ut_code++ = wval; + } else if (!strncmp(cp, "XPT", 3)) { + *ut_code++ = UT_XPT; + wval = ut_par(&cp, 1); + *ut_code++ = wval >> 8; + *ut_code++ = wval; + } else if (!strncmp(cp, "DBG", 3)) { + *ut_code++ = UT_DBG; + wval = ut_par(&cp, 1); + *ut_code++ = wval; + } + cp++; + } + + *ut_code++ = UT_END; + *sp = cp - 1; + uint16_t memsize = (uint32_t)ut_code - (uint32_t)tmp_code; + + // allocate memory + uint8_t *mp = (uint8_t*)malloc(memsize + 2); + if (mp) { + memmove(mp, tmp_code, memsize); + *code = mp; + } +} + +// ===== Touch Parameter Parsing ===== + +uint32_t uDisplay::ut_par(char **lp, uint32_t mode) { + char *cp = *lp; + while (*cp != ' ') { + if (!cp) break; + cp++; + } + cp++; + uint32_t result; + + if (!mode) { + // hex + result = strtol(cp, &cp, 16); + } else if (mode == 1) { + // word + result = strtol(cp, &cp, 10); + } else { + // float as 32bit integer + float fval = CharToFloat(cp); + result = *(uint32_t*)&fval; + while (*cp) { + if (*cp == ' ' || *cp =='\n') { + break; + } + cp++; + } + } + + *lp = cp; + return result; +} + +int16_t uDisplay::ut_execute(uint8_t *ut_code) { + int16_t result = 0; + uint8_t iob, len; + uint16_t wval; + + while (*ut_code != UT_END) { + iob = *ut_code++; + switch (iob) { + case UT_RD: + // read 1 byte + ut_code = ut_rd(ut_code, 1, 1); + break; + + case UT_RDM: + // read multiple bytes + ut_code = ut_rd(ut_code, 2, 1); + break; + + case UT_RDW: + // read 1 byte + ut_code = ut_rd(ut_code, 1, 2); + break; + + case UT_RDWM: + // read multiple bytes + ut_code = ut_rd(ut_code, 2, 2); + break; + + case UT_WR: + ut_code = ut_wr(ut_code, 1); + break; + + case UT_WRW: + ut_code = ut_wr(ut_code, 2); + break; + + case UT_CP: + // compare + iob = *ut_code++; + result = (iob == ut_array[0]); + break; + + case UT_CPM: + // compare multiple + len = *ut_code++; + result = 0; + for (uint32_t cnt = 0; cnt < len; cnt++) { + iob = *ut_code++; + result |= (iob == ut_array[0]); + } + break; + + case UT_CPR: + // compare + iob = *ut_code++; + result = (iob == result); + break; + + case UT_RTF: + // return when false + if (result == 0) { + return false; + } + break; + + case UT_RTT: + // return when true + if (result > 0) { + return false; + } + break; + + case UT_MVB: + // move byte from index to high or low result + wval = *ut_code++; + iob = *ut_code++; + if (wval == 0) { + result &= 0xff00; + result |= ut_array[iob]; + } else { + result &= 0x00ff; + result |= (ut_array[iob] << 8); + } + break; + + case UT_MV: + // move + // source + result = *ut_code++; + iob = *ut_code++; + if (iob == 1) { + result = ut_array[result]; + } else if (iob == 2) { + iob = result; + result = ut_array[iob] << 8; + result |= ut_array[iob + 1]; + } else { + iob = result; + result = ut_array[iob + 1] << 8; + result |= ut_array[iob]; + } + result &= 0xfff; + break; + + case UT_AND: + // and + wval = *ut_code++ << 8; + wval |= *ut_code++; + result &= wval; + break; + + case UT_SCALE: + { + wval = *ut_code++ << 8; + wval |= *ut_code++; + result -= wval; + uint32_t lval = (uint32_t)*ut_code++ << 24; + lval |= (uint32_t)*ut_code++ << 16; + lval |= (uint32_t)*ut_code++ << 8; + lval |= (uint32_t)*ut_code++; + float fval = *(float*)&lval; + fval *= (float)result; + result = fval; + } + break; + + case UT_LIM: + wval = *ut_code++ << 8; + wval |= *ut_code++; + if (result > wval) { + result = wval; + } + break; + + case UT_RT: + // result + return result; + break; + + case UT_GSRT: +#ifdef USE_ESP32_S3 + { + uint32_t val = get_sr_touch(SIMPLERS_XP, SIMPLERS_XM, SIMPLERS_YP, SIMPLERS_YM); + if (val == 0) { + return false; + } + uint16_t xp = val >> 16; + uint16_t yp = val; + + wval = *ut_code++ << 8; + wval |= *ut_code++; + if (xp > wval && yp > wval) { + ut_array[0] = val >> 24; + ut_array[1] = val >> 16; + ut_array[2] = val >> 8; + ut_array[3] = val; + return true; + } + return false; + } +#endif // USE_ESP32_S3 + break; + + case UT_XPT: + wval = *ut_code++ << 8; + wval |= *ut_code++; + result = ut_XPT2046(wval); + break; + + case UT_DBG: + // debug show result + wval = *ut_code++; + AddLog(LOG_LEVEL_INFO, PSTR("UTDBG %d: %02x : %02x,%02x,%02x,%02x"), wval, result, ut_array[0], ut_array[1], ut_array[2], ut_array[3]); + break; + + case UT_END: + break; + } + } + + return result; +} + +// ===== Low-Level Touch Communication ===== +uint8_t *uDisplay::ut_rd(uint8_t *iop, uint32_t len, uint32_t amode) { + if (ut_wire) { + // i2c mode + ut_wire->beginTransmission(ut_i2caddr); + ut_wire->write(*iop++); + if (amode == 2) { + ut_wire->write(*iop++); + } + ut_wire->endTransmission(false); + if (len > 1) { + len = *iop++; + } + ut_wire->requestFrom(ut_i2caddr, (size_t)len); + uint8_t index = 0; + while (ut_wire->available()) { + ut_array[index++] = ut_wire->read(); + } + } else { + // spi mode + if (amode == 1) { + uint16_t val = *iop++; + uint16_t len = *iop++; + if (ut_spi) { + digitalWrite(ut_spi_cs, LOW); + ut_spi->beginTransaction(ut_spiSettings); + ut_spi->transfer(val); + val = ut_spi->transfer16(0); + ut_spi->endTransaction(); + ut_array[len] = val << 8; + ut_array[len + 1] = val; + digitalWrite(ut_spi_cs, HIGH); + } + } + } + + return iop; +} + +uint8_t *uDisplay::ut_wr(uint8_t *iop, uint32_t amode) { + if (ut_wire) { + // i2c mode + ut_wire->beginTransmission(ut_i2caddr); + ut_wire->write(*iop++); + if (amode == 2) { + ut_wire->write(*iop++); + } + ut_wire->write(*iop++); + ut_wire->endTransmission(true); + } else { + // spi mode + } + + return iop; +} + +// ===== XPT2046 Touch Controller ===== + +uint16_t uDisplay::ut_XPT2046(uint16_t z_th) { + uint16_t result = 0; + + if (ut_spi) { + int16_t data[6]; + ut_spi->beginTransaction(ut_spiSettings); + digitalWrite(ut_spi_cs, LOW); + ut_spi->transfer(0xB1 /* Z1 */); + int16_t z1 = ut_spi->transfer16(0xC1 /* Z2 */) >> 3; + int16_t z = z1 + 4095; + int16_t z2 = ut_spi->transfer16(0x91 /* X */) >> 3; + z -= z2; + + if (z >= z_th) { + ut_spi->transfer16(0x91 /* X */); // dummy X measure, 1st is always noisy + data[0] = ut_spi->transfer16(0xD1 /* Y */) >> 3; + data[1] = ut_spi->transfer16(0x91 /* X */) >> 3; // make 3 x-y measurements + data[2] = ut_spi->transfer16(0xD1 /* Y */) >> 3; + data[3] = ut_spi->transfer16(0x91 /* X */) >> 3; + result = 1; + } else { + data[0] = data[1] = data[2] = data[3] = 0; + } + + data[4] = ut_spi->transfer16(0xD0 /* Y */) >> 3; // Last Y touch power down + data[5] = ut_spi->transfer16(0) >> 3; + digitalWrite(ut_spi_cs, HIGH); + ut_spi->endTransaction(); + + uint16_t x = besttwoavg(data[0], data[2], data[4]); + uint16_t y = besttwoavg(data[1], data[3], data[5]); + + ut_array[0] = x >> 8; + ut_array[1] = x; + ut_array[2] = y >> 8; + ut_array[3] = y; + } + + return result; +} + +// ===== Touch Data Processing ===== + +int16_t uDisplay::besttwoavg(int16_t x, int16_t y, int16_t z) { + int16_t da, db, dc; + int16_t reta = 0; + + if (x > y) da = x - y; else da = y - x; + if (x > z) db = x - z; else db = z - x; + if (z > y) dc = z - y; else dc = y - z; + + if (da <= db && da <= dc) reta = (x + y) >> 1; + else if (db <= da && db <= dc) reta = (x + z) >> 1; + else reta = (y + z) >> 1; + + return (reta); +} + +#endif // USE_UNIVERSAL_TOUCH \ No newline at end of file diff --git a/lib/lib_display/UDisplay/uDisplay_utils.cpp b/lib/lib_display/UDisplay/uDisplay_utils.cpp new file mode 100644 index 000000000..fff89e886 --- /dev/null +++ b/lib/lib_display/UDisplay/uDisplay_utils.cpp @@ -0,0 +1,111 @@ +#include "uDisplay.h" +#include "uDisplay_config.h" + +// ===== String and Parsing Utilities ===== + +uint8_t uDisplay::strlen_ln(char *str) { + for (uint32_t cnt = 0; cnt < 256; cnt++) { + if (!str[cnt] || str[cnt] == '\n' || str[cnt] == ' ') return cnt; + } + return 0; +} + +char *uDisplay::devname(void) { + return dname; +} + +uint32_t uDisplay::str2c(char **sp, char *vp, uint32_t len) { + char *lp = *sp; + if (len) len--; + char *cp = strchr(lp, ','); + if (cp) { + while (1) { + if (*lp == ',') { + *vp = 0; + *sp = lp + 1; + return 0; + } + if (len) { + *vp++ = *lp++; + len--; + } else { + lp++; + } + } + } else { + uint16_t slen = strlen(lp); + if (slen) { + strlcpy(vp, *sp, len); + *sp = lp + slen; + return 0; + } + } + return 1; +} + +int32_t uDisplay::next_val(char **sp) { + char ibuff[16]; + if (!str2c(sp, ibuff, sizeof(ibuff))) { + return atoi(ibuff); + } + return 0xff; +} + +uint32_t uDisplay::next_hex(char **sp) { + char ibuff[16]; + if (!str2c(sp, ibuff, sizeof(ibuff))) { + return strtol(ibuff, 0, 16); + } + return 0xff; +} + +// ===== Touch Coordinate Conversion ===== +// the cases are PSEUDO_OPCODES from MODULE_DESCRIPTOR +// and may be expanded with more opcodes +void uDisplay::TS_RotConvert(int16_t *x, int16_t *y) { + int16_t temp; + + if (rot_t[cur_rot] & 0x80) { + temp = *y; + *y = *x; + *x = temp; + } + + if (rotmap_xmin >= 0) { + *y = map(*y, rotmap_ymin, rotmap_ymax, 0, gys); + *x = map(*x, rotmap_xmin, rotmap_xmax, 0, gxs); + *x = constrain(*x, 0, gxs); + *y = constrain(*y, 0, gys); + } + + switch (rot_t[cur_rot] & 0xf) { + case 0: + break; + case 1: + temp = *y; + *y = height() - *x; + *x = temp; + break; + case 2: + *x = width() - *x; + *y = height() - *y; + break; + case 3: + temp = *y; + *y = *x; + *x = width() - temp; + break; + case 4: + *x = width() - *x; + break; + case 5: + *y = height() - *y; + break; + } +} + +// ===== Color Conversion Helper ===== + +static inline void lvgl_color_swap(uint16_t *data, uint16_t len) { + for (uint32_t i = 0; i < len; i++) (data[i] = data[i] << 8 | data[i] >> 8); +} \ No newline at end of file