1459 lines
41 KiB
C++
1459 lines
41 KiB
C++
/*
|
|
uDisplay.cpp - universal display driver support for Tasmota
|
|
|
|
Copyright (C) 2021 Gerhard Mutz and Theo Arends
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <Arduino.h>
|
|
#include "uDisplay.h"
|
|
#include "uDisplay_config.h"
|
|
#include "uDisplay_spi.h"
|
|
|
|
#ifdef ESP32
|
|
#include "esp8266toEsp32.h"
|
|
#endif
|
|
#include "tasmota_options.h"
|
|
|
|
|
|
//#define UDSP_DEBUG
|
|
|
|
#ifndef UDSP_LBSIZE
|
|
#define UDSP_LBSIZE 256
|
|
#endif
|
|
|
|
uDisplay::~uDisplay(void) {
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: dealloc");
|
|
#endif
|
|
if (frame_buffer) {
|
|
free(frame_buffer);
|
|
}
|
|
|
|
if (lut_full) {
|
|
free(lut_full);
|
|
}
|
|
|
|
if (lut_partial) {
|
|
free(lut_partial);
|
|
}
|
|
|
|
for (uint16_t cnt = 0; cnt < MAX_LUTS; cnt++ ) {
|
|
if (lut_array[cnt]) {
|
|
free(lut_array[cnt]);
|
|
}
|
|
}
|
|
|
|
#ifdef USE_UNIVERSAL_TOUCH
|
|
if (ut_init_code) {
|
|
free(ut_init_code);
|
|
}
|
|
if (ut_touch_code) {
|
|
free(ut_touch_code);
|
|
}
|
|
if (ut_getx_code) {
|
|
free(ut_getx_code);
|
|
}
|
|
if (ut_gety_code) {
|
|
free(ut_gety_code);
|
|
}
|
|
#endif // USE_UNIVERSAL_TOUCH
|
|
|
|
#ifdef USE_ESP32_S3
|
|
if (_dmadesc) {
|
|
heap_caps_free(_dmadesc);
|
|
_dmadesc = nullptr;
|
|
_dmadesc_size = 0;
|
|
}
|
|
if (_i80_bus) {
|
|
esp_lcd_del_i80_bus(_i80_bus);
|
|
}
|
|
#endif // USE_ESP32_S3
|
|
}
|
|
|
|
uDisplay::uDisplay(char *lp) : Renderer(800, 600) {
|
|
// analyse decriptor
|
|
pwr_cbp = 0;
|
|
dim_cbp = 0;
|
|
framebuffer = 0;
|
|
col_mode = 16;
|
|
sa_mode = 16;
|
|
saw_3 = 0xff;
|
|
dim_op = 0xff;
|
|
bpmode = 0;
|
|
dsp_off = 0xff;
|
|
dsp_on = 0xff;
|
|
lutpsize = 0;
|
|
lutfsize = 0;
|
|
lutptime = 35;
|
|
lutftime = 350;
|
|
lut3time = 10;
|
|
busy_pin = -1;
|
|
spec_init = -1;
|
|
ep_mode = 0;
|
|
fg_col = 1;
|
|
bg_col = 0;
|
|
splash_font = -1;
|
|
rotmap_xmin = -1;
|
|
bpanel = -1;
|
|
allcmd_mode = 0;
|
|
startline = 0xA1;
|
|
uint8_t section = 0;
|
|
dsp_ncmds = 0;
|
|
epc_part_cnt = 0;
|
|
epc_full_cnt = 0;
|
|
lut_num = 0;
|
|
lvgl_param.data = 0;
|
|
lvgl_param.flushlines = 40;
|
|
rot_t[0] = 0;
|
|
rot_t[1] = 1;
|
|
rot_t[2] = 2;
|
|
rot_t[3] = 3;
|
|
epcoffs_full = 0;
|
|
epcoffs_part = 0;
|
|
interface = 0;
|
|
|
|
for (uint32_t cnt = 0; cnt < MAX_LUTS; cnt++) {
|
|
lut_cnt[cnt] = 0;
|
|
lut_cmd[cnt] = 0xff;
|
|
lut_array[cnt] = 0;
|
|
}
|
|
|
|
lut_partial = 0;
|
|
lut_full = 0;
|
|
char linebuff[UDSP_LBSIZE];
|
|
while (*lp) {
|
|
|
|
uint16_t llen = strlen_ln(lp);
|
|
strncpy(linebuff, lp, llen);
|
|
linebuff[llen] = 0;
|
|
lp += llen;
|
|
char *lp1 = linebuff;
|
|
|
|
if (*lp1 == '#') break;
|
|
if (*lp1 == '\n') lp1++;
|
|
while (*lp1 == ' ') lp1++;
|
|
//AddLog(LOG_LEVEL_DEBUG, ">> %s\n",lp1);
|
|
if (*lp1 != ';') {
|
|
// check ids:
|
|
if (*lp1 == ':') {
|
|
// id line
|
|
lp1++;
|
|
section = *lp1++;
|
|
if (section == 'I') {
|
|
|
|
if (*lp1 == 'C') {
|
|
allcmd_mode = 1;
|
|
lp1++;
|
|
}
|
|
|
|
if (*lp1 == 'S') {
|
|
// pecial case RGB with software SPI init clk,mosi,cs,reset
|
|
lp1++;
|
|
if (interface == _UDSP_RGB) {
|
|
// collect line and send directly
|
|
lp1++;
|
|
spi_nr = 4;
|
|
spec_init = _UDSP_SPI;
|
|
spi_dc = -1;
|
|
spi_miso = -1;
|
|
spi_clk = next_val(&lp1);
|
|
spi_mosi = next_val(&lp1);
|
|
spi_cs = next_val(&lp1);
|
|
reset = next_val(&lp1);
|
|
|
|
pinMode(spi_cs, OUTPUT);
|
|
digitalWrite(spi_cs, HIGH);
|
|
|
|
pinMode(spi_clk, OUTPUT);
|
|
digitalWrite(spi_clk, LOW);
|
|
|
|
pinMode(spi_mosi, OUTPUT);
|
|
digitalWrite(spi_mosi, LOW);
|
|
|
|
if (reset >= 0) {
|
|
pinMode(reset, OUTPUT);
|
|
digitalWrite(reset, HIGH);
|
|
delay(50);
|
|
reset_pin(50, 200);
|
|
}
|
|
#ifdef UDSP_DEBUG
|
|
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') {
|
|
// pecial case RGB with i2c init, bus nr, i2c addr
|
|
lp1++;
|
|
if (interface == _UDSP_RGB) {
|
|
// collect line and send directly
|
|
lp1++;
|
|
wire_n = next_val(&lp1);
|
|
i2caddr = next_hex(&lp1);
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: I2C_INIT bus:%d addr:%02x", wire_n, i2caddr);
|
|
#endif
|
|
if (wire_n == 1) {
|
|
wire = &Wire;
|
|
} else {
|
|
#if SOC_HP_I2C_NUM > 1
|
|
wire = &Wire1;
|
|
#else
|
|
wire = &Wire;
|
|
#endif
|
|
}
|
|
spec_init = _UDSP_I2C;
|
|
}
|
|
}
|
|
} else if (section == 'L') {
|
|
if (*lp1 >= '1' && *lp1 <= '5') {
|
|
lut_num = (*lp1 & 0x07);
|
|
lp1 += 2;
|
|
lut_siz[lut_num - 1] = next_val(&lp1);
|
|
lut_array[lut_num - 1] = (uint8_t*)malloc(lut_siz[lut_num - 1]);
|
|
lut_cmd[lut_num - 1] = next_hex(&lp1);
|
|
} else {
|
|
lut_num = 0;
|
|
lp1++;
|
|
lut_siz_full = next_val(&lp1);
|
|
lut_full = (uint8_t*)malloc(lut_siz_full);
|
|
lut_cmd[0] = next_hex(&lp1);
|
|
}
|
|
} else if (section == 'l') {
|
|
lp1++;
|
|
lut_siz_partial = next_val(&lp1);
|
|
lut_partial = (uint8_t*)malloc(lut_siz_partial);
|
|
lut_cmd[0] = next_hex(&lp1);
|
|
}
|
|
if (*lp1 == ',') lp1++;
|
|
|
|
}
|
|
if (*lp1 && *lp1 != ':' && *lp1 != '\n' && *lp1 != ' ') { // Add space char
|
|
switch (section) {
|
|
case 'H':
|
|
// header line
|
|
// SD1306,128,64,1,I2C,5a,*,*,*
|
|
str2c(&lp1, dname, sizeof(dname));
|
|
char ibuff[16];
|
|
gxs = next_val(&lp1);
|
|
setwidth(gxs);
|
|
gys = next_val(&lp1);
|
|
setheight(gys);
|
|
disp_bpp = next_val(&lp1);
|
|
bpp = abs(disp_bpp);
|
|
if (bpp == 1) {
|
|
col_type = uCOLOR_BW;
|
|
} else {
|
|
col_type = uCOLOR_COLOR;
|
|
if (bpp == 16) {
|
|
fg_col = GetColorFromIndex(fg_col);
|
|
bg_col = GetColorFromIndex(bg_col);
|
|
}
|
|
}
|
|
str2c(&lp1, ibuff, sizeof(ibuff));
|
|
if (!strncmp(ibuff, "I2C", 3)) {
|
|
interface = _UDSP_I2C;
|
|
wire_n = 0;
|
|
if (!strncmp(ibuff, "I2C2", 4)) {
|
|
wire_n = 1;
|
|
}
|
|
i2caddr = next_hex(&lp1);
|
|
i2c_scl = next_val(&lp1);
|
|
i2c_sda = next_val(&lp1);
|
|
reset = next_val(&lp1);
|
|
section = 0;
|
|
} else if (!strncmp(ibuff, "SPI", 3)) {
|
|
interface = _UDSP_SPI;
|
|
spi_nr = next_val(&lp1);
|
|
spi_cs = next_val(&lp1);
|
|
spi_clk = next_val(&lp1);
|
|
spi_mosi = next_val(&lp1);
|
|
spi_dc = next_val(&lp1);
|
|
bpanel = next_val(&lp1);
|
|
reset = next_val(&lp1);
|
|
spi_miso = next_val(&lp1);
|
|
spi_speed = next_val(&lp1);
|
|
section = 0;
|
|
} else if (!strncmp(ibuff, "PAR", 3)) {
|
|
#ifdef USE_ESP32_S3
|
|
uint8_t bus = next_val(&lp1);
|
|
if (bus == 8) {
|
|
interface = _UDSP_PAR8;
|
|
} else {
|
|
interface = _UDSP_PAR16;
|
|
}
|
|
reset = next_val(&lp1);
|
|
par_cs = next_val(&lp1);
|
|
par_rs = next_val(&lp1);
|
|
par_wr = next_val(&lp1);
|
|
par_rd = next_val(&lp1);
|
|
bpanel = next_val(&lp1);
|
|
|
|
for (uint32_t cnt = 0; cnt < 8; cnt ++) {
|
|
par_dbl[cnt] = next_val(&lp1);
|
|
}
|
|
|
|
if (interface == _UDSP_PAR16) {
|
|
for (uint32_t cnt = 0; cnt < 8; cnt ++) {
|
|
par_dbh[cnt] = next_val(&lp1);
|
|
}
|
|
}
|
|
spi_speed = next_val(&lp1);
|
|
#endif // USE_ESP32_S3
|
|
section = 0;
|
|
} else if (!strncmp(ibuff, "RGB", 3)) {
|
|
#ifdef USE_ESP32_S3
|
|
interface = _UDSP_RGB;
|
|
|
|
de = next_val(&lp1);
|
|
vsync = next_val(&lp1);
|
|
hsync = next_val(&lp1);
|
|
pclk = next_val(&lp1);
|
|
bpanel = next_val(&lp1);
|
|
|
|
for (uint32_t cnt = 0; cnt < 8; cnt ++) {
|
|
par_dbl[cnt] = next_val(&lp1);
|
|
}
|
|
|
|
for (uint32_t cnt = 0; cnt < 8; cnt ++) {
|
|
par_dbh[cnt] = next_val(&lp1);
|
|
}
|
|
spi_speed = next_val(&lp1);
|
|
#endif // USE_ESP32_S3
|
|
section = 0;
|
|
}
|
|
break;
|
|
case 'S':
|
|
splash_font = next_val(&lp1);
|
|
splash_size = next_val(&lp1);
|
|
fg_col = next_val(&lp1);
|
|
bg_col = next_val(&lp1);
|
|
if (bpp == 16) {
|
|
fg_col = GetColorFromIndex(fg_col);
|
|
bg_col = GetColorFromIndex(bg_col);
|
|
}
|
|
splash_xp = next_val(&lp1);
|
|
splash_yp = next_val(&lp1);
|
|
break;
|
|
case 'I':
|
|
// init data
|
|
if (interface == _UDSP_RGB && spec_init > 0) {
|
|
// special case RGB with SPI or I2C init
|
|
// collect line and send directly
|
|
dsp_ncmds = 0;
|
|
while (1) {
|
|
if (dsp_ncmds >= sizeof(dsp_cmds)) break;
|
|
if (!str2c(&lp1, ibuff, sizeof(ibuff))) {
|
|
dsp_cmds[dsp_ncmds++] = strtol(ibuff, 0, 16);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (spec_init == _UDSP_SPI) {
|
|
interface = spec_init;
|
|
send_spi_icmds(dsp_ncmds);
|
|
} else {
|
|
if (dsp_ncmds == 2) {
|
|
wire->beginTransmission(i2caddr);
|
|
wire->write(dsp_cmds[0]);
|
|
wire->write(dsp_cmds[1]);
|
|
wire->endTransmission();
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: reg=%02x val=%02x", dsp_cmds[0], dsp_cmds[1]);
|
|
#endif
|
|
} else {
|
|
delay(dsp_cmds[0]);
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: delay=%d ms", dsp_cmds[0]);
|
|
#endif
|
|
}
|
|
}
|
|
interface = _UDSP_RGB;
|
|
} else {
|
|
if (interface == _UDSP_I2C) {
|
|
dsp_cmds[dsp_ncmds++] = next_hex(&lp1);
|
|
if (!str2c(&lp1, ibuff, sizeof(ibuff))) {
|
|
dsp_cmds[dsp_ncmds++] = strtol(ibuff, 0, 16);
|
|
}
|
|
} else {
|
|
while (1) {
|
|
if (dsp_ncmds >= sizeof(dsp_cmds)) break;
|
|
if (!str2c(&lp1, ibuff, sizeof(ibuff))) {
|
|
dsp_cmds[dsp_ncmds++] = strtol(ibuff, 0, 16);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'f':
|
|
// epaper full update cmds
|
|
if (!epcoffs_full) {
|
|
epcoffs_full = dsp_ncmds;
|
|
epc_full_cnt = 0;
|
|
}
|
|
while (1) {
|
|
if (epc_full_cnt >= sizeof(dsp_cmds)) break;
|
|
if (!str2c(&lp1, ibuff, sizeof(ibuff))) {
|
|
dsp_cmds[epcoffs_full + epc_full_cnt++] = strtol(ibuff, 0, 16);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 'p':
|
|
// epaper partial update cmds
|
|
if (!epcoffs_part) {
|
|
epcoffs_part = dsp_ncmds + epc_full_cnt;
|
|
epc_part_cnt = 0;
|
|
}
|
|
while (1) {
|
|
if (epc_part_cnt >= sizeof(dsp_cmds)) break;
|
|
if (!str2c(&lp1, ibuff, sizeof(ibuff))) {
|
|
dsp_cmds[epcoffs_part + epc_part_cnt++] = strtol(ibuff, 0, 16);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
#ifdef USE_ESP32_S3
|
|
case 'V':
|
|
hsync_polarity = next_val(&lp1);
|
|
hsync_front_porch = next_val(&lp1);
|
|
hsync_pulse_width = next_val(&lp1);
|
|
hsync_back_porch = next_val(&lp1);
|
|
vsync_polarity = next_val(&lp1);
|
|
vsync_front_porch = next_val(&lp1);
|
|
vsync_pulse_width = next_val(&lp1);
|
|
vsync_back_porch = next_val(&lp1);
|
|
pclk_active_neg = next_val(&lp1);
|
|
break;
|
|
#endif // USE_ESP32_S3
|
|
case 'o':
|
|
dsp_off = next_hex(&lp1);
|
|
break;
|
|
|
|
case 'O':
|
|
dsp_on = next_hex(&lp1);
|
|
break;
|
|
case 'R':
|
|
madctrl = next_hex(&lp1);
|
|
startline = next_hex(&lp1);
|
|
break;
|
|
case '0':
|
|
if (interface != _UDSP_RGB) {
|
|
rot[0] = next_hex(&lp1);
|
|
x_addr_offs[0] = next_hex(&lp1);
|
|
y_addr_offs[0] = next_hex(&lp1);
|
|
}
|
|
rot_t[0] = next_hex(&lp1);
|
|
break;
|
|
case '1':
|
|
if (interface != _UDSP_RGB) {
|
|
rot[1] = next_hex(&lp1);
|
|
x_addr_offs[1] = next_hex(&lp1);
|
|
y_addr_offs[1] = next_hex(&lp1);
|
|
}
|
|
rot_t[1] = next_hex(&lp1);
|
|
break;
|
|
case '2':
|
|
if (interface != _UDSP_RGB) {
|
|
rot[2] = next_hex(&lp1);
|
|
x_addr_offs[2] = next_hex(&lp1);
|
|
y_addr_offs[2] = next_hex(&lp1);
|
|
}
|
|
rot_t[2] = next_hex(&lp1);
|
|
break;
|
|
case '3':
|
|
if (interface != _UDSP_RGB) {
|
|
rot[3] = next_hex(&lp1);
|
|
x_addr_offs[3] = next_hex(&lp1);
|
|
y_addr_offs[3] = next_hex(&lp1);
|
|
}
|
|
rot_t[3] = next_hex(&lp1);
|
|
break;
|
|
case 'A':
|
|
if (interface == _UDSP_I2C || bpp == 1) {
|
|
saw_1 = next_hex(&lp1);
|
|
i2c_page_start = next_hex(&lp1);
|
|
i2c_page_end = next_hex(&lp1);
|
|
saw_2 = next_hex(&lp1);
|
|
i2c_col_start = next_hex(&lp1);
|
|
i2c_col_end = next_hex(&lp1);
|
|
saw_3 = next_hex(&lp1);
|
|
} else {
|
|
saw_1 = next_hex(&lp1);
|
|
saw_2 = next_hex(&lp1);
|
|
saw_3 = next_hex(&lp1);
|
|
sa_mode = next_val(&lp1);
|
|
}
|
|
break;
|
|
case 'a':
|
|
saw_1 = next_hex(&lp1);
|
|
saw_2 = next_hex(&lp1);
|
|
saw_3 = next_hex(&lp1);
|
|
break;
|
|
case 'P':
|
|
col_mode = next_val(&lp1);
|
|
break;
|
|
case 'i':
|
|
inv_off = next_hex(&lp1);
|
|
inv_on = next_hex(&lp1);
|
|
break;
|
|
case 'D':
|
|
dim_op = next_hex(&lp1);
|
|
break;
|
|
case 'L':
|
|
if (!lut_num) {
|
|
if (!lut_full) {
|
|
break;
|
|
}
|
|
while (1) {
|
|
if (!str2c(&lp1, ibuff, sizeof(ibuff))) {
|
|
lut_full[lutfsize++] = strtol(ibuff, 0, 16);
|
|
} else {
|
|
break;
|
|
}
|
|
if (lutfsize >= lut_siz_full) break;
|
|
}
|
|
} else {
|
|
uint8_t index = lut_num - 1;
|
|
if (!lut_array[index]) {
|
|
break;
|
|
}
|
|
while (1) {
|
|
if (!str2c(&lp1, ibuff, sizeof(ibuff))) {
|
|
lut_array[index][lut_cnt[index]++] = strtol(ibuff, 0, 16);
|
|
} else {
|
|
break;
|
|
}
|
|
if (lut_cnt[index] >= lut_siz[index]) break;
|
|
}
|
|
}
|
|
break;
|
|
case 'l':
|
|
if (!lut_partial) {
|
|
break;
|
|
}
|
|
while (1) {
|
|
if (!str2c(&lp1, ibuff, sizeof(ibuff))) {
|
|
lut_partial[lutpsize++] = strtol(ibuff, 0, 16);
|
|
} else {
|
|
break;
|
|
}
|
|
if (lutpsize >= lut_siz_partial) break;
|
|
}
|
|
break;
|
|
case 'T':
|
|
lutftime = next_val(&lp1);
|
|
lutptime = next_val(&lp1);
|
|
lut3time = next_val(&lp1);
|
|
break;
|
|
case 'B':
|
|
lvgl_param.flushlines = next_val(&lp1);
|
|
lvgl_param.data = next_val(&lp1);
|
|
#ifdef ESP32
|
|
lvgl_param.use_dma = false; // temporary fix to disable DMA due to a problem in esp-idf 5.3
|
|
#endif
|
|
break;
|
|
case 'M':
|
|
rotmap_xmin = next_val(&lp1);
|
|
rotmap_xmax = next_val(&lp1);
|
|
rotmap_ymin = next_val(&lp1);
|
|
rotmap_ymax = next_val(&lp1);
|
|
break;
|
|
case 'b':
|
|
bpmode = next_val(&lp1);
|
|
break;
|
|
#ifdef USE_UNIVERSAL_TOUCH
|
|
case 'U':
|
|
if (!strncmp(lp1, "TI", 2)) {
|
|
// init
|
|
ut_wire = 0;
|
|
ut_reset = -1;
|
|
ut_irq = -1;
|
|
lp1 += 3;
|
|
str2c(&lp1, ut_name, sizeof(ut_name));
|
|
if (*lp1 == 'I') {
|
|
// i2c mode
|
|
lp1++;
|
|
uint8_t ut_mode = *lp1 & 0xf;
|
|
lp1 += 2;
|
|
ut_i2caddr = next_hex(&lp1);
|
|
ut_reset = next_val(&lp1);
|
|
ut_irq = next_val(&lp1);
|
|
|
|
if (ut_mode == 1) {
|
|
ut_wire = &Wire;
|
|
} else {
|
|
#if SOC_HP_I2C_NUM > 1
|
|
ut_wire = &Wire1;
|
|
#else
|
|
ut_wire = &Wire;
|
|
#endif
|
|
}
|
|
} else if (*lp1 == 'S') {
|
|
// spi mode
|
|
lp1++;
|
|
ut_spi_nr = *lp1 & 0xf;
|
|
lp1 += 2;
|
|
ut_spi_cs = next_val(&lp1);
|
|
ut_reset = next_val(&lp1);
|
|
ut_irq = next_val(&lp1);
|
|
pinMode(ut_spi_cs, OUTPUT);
|
|
digitalWrite(ut_spi_cs, HIGH);
|
|
ut_spiSettings = SPISettings(2000000, MSBFIRST, SPI_MODE0);
|
|
} else {
|
|
// simple resistive touch
|
|
lp1++;
|
|
}
|
|
ut_trans(&lp, &ut_init_code);
|
|
} else if (!strncmp(lp1, "TT", 2)) {
|
|
lp1 += 2;
|
|
// touch
|
|
ut_trans(&lp, &ut_touch_code);
|
|
} else if (!strncmp(lp1, "TX", 2)) {
|
|
lp1 += 2;
|
|
// get x
|
|
ut_trans(&lp, &ut_getx_code);
|
|
} else if (!strncmp(lp1, "TY", 2)) {
|
|
lp1 += 2;
|
|
// get y
|
|
ut_trans(&lp, &ut_gety_code);
|
|
}
|
|
break;
|
|
#endif // USE_UNIVERSAL_TOUCH
|
|
}
|
|
}
|
|
}
|
|
nextline:
|
|
if (*lp == '\n' || *lp == ' ') { // Add space char
|
|
lp++;
|
|
} else {
|
|
char *lp1;
|
|
lp1 = strchr(lp, '\n');
|
|
if (!lp1) {
|
|
lp1 = strchr(lp, ' ');
|
|
if (!lp1) {
|
|
break;
|
|
}
|
|
}
|
|
lp = lp1 + 1;
|
|
}
|
|
}
|
|
|
|
if (lutfsize && lutpsize) {
|
|
// 2 table mode
|
|
ep_mode = 1;
|
|
}
|
|
|
|
if (lut_cnt[0] > 0 && lut_cnt[1] == lut_cnt[2] && lut_cnt[1] == lut_cnt[3] && lut_cnt[1] == lut_cnt[4]) {
|
|
// 5 table mode
|
|
ep_mode = 2;
|
|
}
|
|
|
|
|
|
#ifdef USE_ESP32_S3
|
|
void UfsCheckSDCardInit(void);
|
|
|
|
if (spec_init == _UDSP_SPI) {
|
|
// special case, assuming sd card and display on same spi bus
|
|
// end spi in case it was running
|
|
SPI.end();
|
|
// reininit SD card
|
|
UfsCheckSDCardInit();
|
|
}
|
|
#endif
|
|
|
|
if ((epcoffs_full || epcoffs_part) && !(lutfsize || lutpsize)) {
|
|
// no lutfsize or lutpsize, but epcoffs_full or epcoffs_part
|
|
ep_mode = 3;
|
|
}
|
|
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: Device:%s xs:%d ys:%d bpp:%d", dname, gxs, gys, bpp);
|
|
|
|
if (interface == _UDSP_SPI) {
|
|
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) {
|
|
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) {
|
|
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) {
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: Addr:%02x SCL:%d SDA:%d", i2caddr, i2c_scl, i2c_sda);
|
|
|
|
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
|
|
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 ++) {
|
|
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 ++) {
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: par d%d:%d", cnt + 8, par_dbh[cnt]);
|
|
}
|
|
}
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: par freq:%d", spi_speed);
|
|
#endif // USE_ESP32_S3
|
|
|
|
}
|
|
if (interface == _UDSP_RGB) {
|
|
#ifdef USE_ESP32_S3
|
|
|
|
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 ++) {
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: rgb d%d:%d", cnt, par_dbl[cnt]);
|
|
}
|
|
for (uint32_t cnt = 0; cnt < 8; cnt ++) {
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: rgb d%d:%d", cnt + 8, par_dbh[cnt]);
|
|
}
|
|
|
|
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
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: Dsp class init complete");
|
|
#endif
|
|
}
|
|
|
|
// special init for GC displays
|
|
void uDisplay::send_spi_icmds(uint16_t cmd_size) {
|
|
uint16_t index = 0;
|
|
uint16_t cmd_offset = 0;
|
|
|
|
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: start send icmd table");
|
|
#endif
|
|
while (1) {
|
|
uint8_t iob;
|
|
SPI_CS_LOW
|
|
iob = dsp_cmds[cmd_offset++];
|
|
index++;
|
|
ulcd_command(iob);
|
|
uint8_t args = dsp_cmds[cmd_offset++];
|
|
index++;
|
|
#ifdef UDSP_DEBUG
|
|
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
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: %02x", iob);
|
|
#endif
|
|
ulcd_data8(iob);
|
|
}
|
|
SPI_CS_HIGH
|
|
if (args & 0x80) { // delay after the command
|
|
delay_arg(args);
|
|
}
|
|
if (index >= cmd_size) break;
|
|
}
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: end send icmd table");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
|
|
void uDisplay::send_spi_cmds(uint16_t cmd_offset, uint16_t cmd_size) {
|
|
uint16_t index = 0;
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: start send cmd table");
|
|
#endif
|
|
while (1) {
|
|
uint8_t iob;
|
|
SPI_CS_LOW
|
|
iob = dsp_cmds[cmd_offset++];
|
|
index++;
|
|
if ((ep_mode == 1 || ep_mode == 3) && iob >= EP_RESET) {
|
|
// epaper pseudo opcodes
|
|
uint8_t args = dsp_cmds[cmd_offset++];
|
|
index++;
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: cmd, args %02x, %d", iob, args & 0x1f);
|
|
#endif
|
|
switch (iob) {
|
|
case EP_RESET:
|
|
if (args & 1) {
|
|
iob = dsp_cmds[cmd_offset++];
|
|
index++;
|
|
}
|
|
reset_pin(iob, iob);
|
|
break;
|
|
case EP_LUT_FULL:
|
|
SetLut(lut_full);
|
|
ep_update_mode = DISPLAY_INIT_FULL;
|
|
break;
|
|
case EP_LUT_PARTIAL:
|
|
SetLut(lut_partial);
|
|
ep_update_mode = DISPLAY_INIT_PARTIAL;
|
|
break;
|
|
case EP_WAITIDLE:
|
|
if (args & 1) {
|
|
iob = dsp_cmds[cmd_offset++];
|
|
index++;
|
|
}
|
|
//delay(iob * 10);
|
|
delay_sync(iob * 10);
|
|
break;
|
|
case EP_SET_MEM_AREA:
|
|
SetMemoryArea(0, 0, gxs - 1, gys - 1);
|
|
break;
|
|
case EP_SET_MEM_PTR:
|
|
SetMemoryPointer(0, 0);
|
|
break;
|
|
case EP_SEND_DATA:
|
|
Send_EP_Data();
|
|
break;
|
|
case EP_CLR_FRAME:
|
|
ClearFrameMemory(0xFF);
|
|
break;
|
|
case EP_SEND_FRAME:
|
|
SetFrameMemory(framebuffer);
|
|
break;
|
|
case EP_BREAK_RR_EQU:
|
|
if (args & 1) {
|
|
iob = dsp_cmds[cmd_offset++];
|
|
index++;
|
|
if (iob == ESP_ResetInfoReason()) {
|
|
ep_update_mode = DISPLAY_INIT_PARTIAL;
|
|
goto exit;
|
|
}
|
|
}
|
|
break;
|
|
case EP_BREAK_RR_NEQ:
|
|
if (args & 1) {
|
|
iob = dsp_cmds[cmd_offset++];
|
|
index++;
|
|
if (iob != ESP_ResetInfoReason()) {
|
|
ep_update_mode = DISPLAY_INIT_PARTIAL;
|
|
goto exit;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
#ifdef UDSP_DEBUG
|
|
if (args & 1) {
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: %02x", iob);
|
|
}
|
|
#endif
|
|
if (args & 0x80) { // delay after the command
|
|
delay_arg(args);
|
|
}
|
|
} else {
|
|
if (spi_dc == -2) {
|
|
// pseudo opcodes
|
|
switch (iob) {
|
|
case UDSP_WRITE_16:
|
|
break;
|
|
case UDSP_READ_DATA:
|
|
break;
|
|
case UDSP_READ_STATUS:
|
|
break;
|
|
}
|
|
}
|
|
ulcd_command(iob);
|
|
uint8_t args = dsp_cmds[cmd_offset++];
|
|
index++;
|
|
#ifdef UDSP_DEBUG
|
|
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
|
|
AddLog(LOG_LEVEL_DEBUG, "%02x ", iob );
|
|
#endif
|
|
if (!allcmd_mode) {
|
|
ulcd_data8(iob);
|
|
} else {
|
|
ulcd_command(iob);
|
|
}
|
|
}
|
|
SPI_CS_HIGH
|
|
if (args & 0x80) { // delay after the command
|
|
delay_arg(args);
|
|
}
|
|
}
|
|
if (index >= cmd_size) break;
|
|
}
|
|
|
|
exit:
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: end send cmd table");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
Renderer *uDisplay::Init(void) {
|
|
if (!interface) { // no valid configuration, abort
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_INFO, "UDisplay: Dsp Init no valid configuration");
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef UDSP_DEBUG
|
|
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
|
|
if (ep_mode || bpp < 16) {
|
|
if (framebuffer) free(framebuffer);
|
|
#ifdef ESP8266
|
|
framebuffer = (uint8_t*)calloc((gxs * gys * bpp) / 8, 1);
|
|
#else
|
|
if (UsePSRAM()) {
|
|
framebuffer = (uint8_t*)heap_caps_malloc((gxs * gys * bpp) / 8, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
|
} else {
|
|
framebuffer = (uint8_t*)calloc((gxs * gys * bpp) / 8, 1);
|
|
}
|
|
#endif // ESP8266
|
|
}
|
|
frame_buffer = framebuffer;
|
|
|
|
if (interface == _UDSP_I2C) {
|
|
if (wire_n == 0) {
|
|
wire = &Wire;
|
|
}
|
|
#if SOC_HP_I2C_NUM > 1
|
|
if (wire_n == 1) {
|
|
wire = &Wire1;
|
|
}
|
|
#endif // ESP32
|
|
/*
|
|
if (i2c_sda != i2c_scl) {
|
|
wire->begin(i2c_sda, i2c_scl); // TODO: aren't I2C buses already initialized? Shouldn't this be moved to display driver?
|
|
}
|
|
*/
|
|
#ifdef UDSP_DEBUG
|
|
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
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: cmd=%x", dsp_cmds[cnt]);
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
if (interface == _UDSP_SPI) {
|
|
|
|
if (bpanel >= 0) {
|
|
#ifdef ESP32
|
|
analogWrite(bpanel, 32);
|
|
#else
|
|
pinMode(bpanel, OUTPUT);
|
|
digitalWrite(bpanel, HIGH);
|
|
#endif // ESP32
|
|
}
|
|
if (spi_dc >= 0) {
|
|
pinMode(spi_dc, OUTPUT);
|
|
digitalWrite(spi_dc, HIGH);
|
|
}
|
|
if (spi_cs >= 0) {
|
|
pinMode(spi_cs, OUTPUT);
|
|
digitalWrite(spi_cs, HIGH);
|
|
}
|
|
|
|
#ifdef ESP8266
|
|
if (spi_nr <= 1) {
|
|
SPI.begin();
|
|
uspi = &SPI;
|
|
} else {
|
|
pinMode(spi_clk, OUTPUT);
|
|
digitalWrite(spi_clk, LOW);
|
|
pinMode(spi_mosi, OUTPUT);
|
|
digitalWrite(spi_mosi, LOW);
|
|
if (spi_miso >= 0) {
|
|
pinMode(spi_miso, INPUT_PULLUP);
|
|
busy_pin = spi_miso;
|
|
}
|
|
}
|
|
#endif // ESP8266
|
|
|
|
#ifdef ESP32
|
|
if (spi_nr == 1) {
|
|
uspi = &SPI;
|
|
uspi->begin(spi_clk, spi_miso, spi_mosi, -1);
|
|
if (lvgl_param.use_dma) {
|
|
spi_host = VSPI_HOST;
|
|
initDMA(lvgl_param.async_dma ? spi_cs : -1); // disable DMA CS if sync, we control it directly
|
|
}
|
|
|
|
} else if (spi_nr == 2) {
|
|
uspi = new SPIClass(HSPI);
|
|
uspi->begin(spi_clk, spi_miso, spi_mosi, -1);
|
|
if (lvgl_param.use_dma) {
|
|
spi_host = HSPI_HOST;
|
|
initDMA(lvgl_param.async_dma ? spi_cs : -1); // disable DMA CS if sync, we control it directly
|
|
}
|
|
} else {
|
|
pinMode(spi_clk, OUTPUT);
|
|
digitalWrite(spi_clk, LOW);
|
|
pinMode(spi_mosi, OUTPUT);
|
|
digitalWrite(spi_mosi, LOW);
|
|
if (spi_miso >= 0) {
|
|
busy_pin = spi_miso;
|
|
pinMode(spi_miso, INPUT_PULLUP);
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: Dsp busy pin:%d", busy_pin);
|
|
#endif
|
|
}
|
|
}
|
|
#endif // ESP32
|
|
|
|
|
|
spiSettings = SPISettings((uint32_t)spi_speed*1000000, MSBFIRST, SPI_MODE3);
|
|
SPI_BEGIN_TRANSACTION
|
|
|
|
if (reset >= 0) {
|
|
pinMode(reset, OUTPUT);
|
|
digitalWrite(reset, HIGH);
|
|
delay(50);
|
|
reset_pin(50, 200);
|
|
}
|
|
|
|
send_spi_cmds(0, dsp_ncmds);
|
|
|
|
SPI_END_TRANSACTION
|
|
|
|
}
|
|
|
|
if (interface == _UDSP_RGB) {
|
|
#ifdef USE_ESP32_S3
|
|
if (!UsePSRAM()) { // RGB is not supported on S3 without PSRAM
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_INFO, "UDisplay: Dsp RGB requires PSRAM, abort");
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
if (bpanel >= 0) {
|
|
analogWrite(bpanel, 32);
|
|
}
|
|
esp_lcd_rgb_panel_config_t *_panel_config = (esp_lcd_rgb_panel_config_t *)heap_caps_calloc(1, sizeof(esp_lcd_rgb_panel_config_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
|
|
|
|
_panel_config->clk_src = LCD_CLK_SRC_PLL160M;
|
|
|
|
//if (spi_speed > 14) {
|
|
//spi_speed = 14;
|
|
//}
|
|
_panel_config->timings.pclk_hz = spi_speed*1000000;
|
|
_panel_config->timings.h_res = gxs;
|
|
_panel_config->timings.v_res = gys;
|
|
|
|
_panel_config->timings.hsync_pulse_width = hsync_pulse_width;
|
|
_panel_config->timings.hsync_back_porch = hsync_back_porch;
|
|
_panel_config->timings.hsync_front_porch = hsync_front_porch;
|
|
_panel_config->timings.vsync_pulse_width = vsync_pulse_width;
|
|
_panel_config->timings.vsync_back_porch = vsync_back_porch;
|
|
_panel_config->timings.vsync_front_porch = vsync_front_porch;
|
|
_panel_config->timings.flags.hsync_idle_low = (hsync_polarity == 0) ? 1 : 0;
|
|
_panel_config->timings.flags.vsync_idle_low = (vsync_polarity == 0) ? 1 : 0;
|
|
_panel_config->timings.flags.de_idle_high = 0;
|
|
_panel_config->timings.flags.pclk_active_neg = pclk_active_neg;
|
|
_panel_config->timings.flags.pclk_idle_high = 0;
|
|
|
|
_panel_config->data_width = 16; // RGB565 in parallel mode, thus 16bit in width
|
|
_panel_config->sram_trans_align = 8;
|
|
_panel_config->psram_trans_align = 64;
|
|
_panel_config->hsync_gpio_num = hsync;
|
|
_panel_config->vsync_gpio_num = vsync;
|
|
_panel_config->de_gpio_num = de;
|
|
_panel_config->pclk_gpio_num = pclk;
|
|
|
|
// assume that byte swapping of 16-bit color is done only upon request
|
|
// via display.ini and not by callers of pushColor()
|
|
// -> swap bytes by swapping GPIO numbers
|
|
int8_t *par_db8 = lvgl_param.swap_color ? par_dbl : par_dbh;
|
|
for (uint32_t cnt = 0; cnt < 8; cnt ++) {
|
|
_panel_config->data_gpio_nums[cnt] = par_db8[cnt];
|
|
}
|
|
par_db8 = lvgl_param.swap_color ? par_dbh : par_dbl;
|
|
for (uint32_t cnt = 0; cnt < 8; cnt ++) {
|
|
_panel_config->data_gpio_nums[cnt + 8] = par_db8[cnt];
|
|
}
|
|
lvgl_param.swap_color = 0;
|
|
|
|
_panel_config->disp_gpio_num = GPIO_NUM_NC;
|
|
|
|
_panel_config->flags.disp_active_low = 0;
|
|
_panel_config->flags.refresh_on_demand = 0;
|
|
_panel_config->flags.fb_in_psram = 1; // allocate frame buffer in PSRAM
|
|
|
|
ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(_panel_config, &_panel_handle));
|
|
ESP_ERROR_CHECK(esp_lcd_panel_reset(_panel_handle));
|
|
ESP_ERROR_CHECK(esp_lcd_panel_init(_panel_handle));
|
|
|
|
uint16_t color = random(0xffff);
|
|
ESP_ERROR_CHECK(_panel_handle->draw_bitmap(_panel_handle, 0, 0, 1, 1, &color));
|
|
|
|
void * buf = NULL;
|
|
esp_lcd_rgb_panel_get_frame_buffer(_panel_handle, 1, &buf);
|
|
rgb_fb = (uint16_t *)buf;
|
|
|
|
#endif // USE_ESP32_S3
|
|
}
|
|
|
|
if (interface == _UDSP_PAR8 || interface == _UDSP_PAR16) {
|
|
|
|
#ifdef USE_ESP32_S3
|
|
|
|
if (bpanel >= 0) {
|
|
analogWrite(bpanel, 32);
|
|
}
|
|
|
|
pinMode(par_cs, OUTPUT);
|
|
digitalWrite(par_cs, HIGH);
|
|
|
|
pinMode(par_rs, OUTPUT);
|
|
digitalWrite(par_rs, HIGH);
|
|
|
|
pinMode(par_wr, OUTPUT);
|
|
digitalWrite(par_wr, HIGH);
|
|
|
|
if (par_rd >= 0) {
|
|
pinMode(par_rd, OUTPUT);
|
|
digitalWrite(par_rd, HIGH);
|
|
}
|
|
|
|
for (uint32_t cnt = 0; cnt < 8; cnt ++) {
|
|
pinMode(par_dbl[cnt], OUTPUT);
|
|
}
|
|
|
|
uint8_t bus_width = 8;
|
|
|
|
if (interface == _UDSP_PAR16) {
|
|
for (uint32_t cnt = 0; cnt < 8; cnt ++) {
|
|
pinMode(par_dbh[cnt], OUTPUT);
|
|
}
|
|
bus_width = 16;
|
|
}
|
|
|
|
if (reset >= 0) {
|
|
pinMode(reset, OUTPUT);
|
|
digitalWrite(reset, HIGH);
|
|
delay(50);
|
|
reset_pin(50, 200);
|
|
}
|
|
|
|
esp_lcd_i80_bus_config_t bus_config = {
|
|
.dc_gpio_num = par_rs,
|
|
.wr_gpio_num = par_wr,
|
|
.clk_src = LCD_CLK_SRC_DEFAULT,
|
|
.bus_width = bus_width,
|
|
.max_transfer_bytes = 32768
|
|
};
|
|
|
|
if (interface == _UDSP_PAR8) {
|
|
for (uint32_t cnt = 0; cnt < 8; cnt ++) {
|
|
bus_config.data_gpio_nums[cnt] = par_dbl[cnt];
|
|
}
|
|
} else {
|
|
for (uint32_t cnt = 0; cnt < 8; cnt ++) {
|
|
bus_config.data_gpio_nums[cnt] = par_dbl[cnt];
|
|
}
|
|
for (uint32_t cnt = 0; cnt < 8; cnt ++) {
|
|
bus_config.data_gpio_nums[cnt + 8] = par_dbh[cnt];
|
|
}
|
|
}
|
|
|
|
// to disable SPI TRANSACTION
|
|
spi_nr = 3;
|
|
spi_cs = par_cs;
|
|
|
|
_i80_bus = nullptr;
|
|
|
|
esp_lcd_new_i80_bus(&bus_config, &_i80_bus);
|
|
|
|
uint32_t div_a, div_b, div_n, clkcnt;
|
|
calcClockDiv(&div_a, &div_b, &div_n, &clkcnt, 240*1000*1000, spi_speed*1000000);
|
|
lcd_cam_lcd_clock_reg_t lcd_clock;
|
|
lcd_clock.lcd_clkcnt_n = std::max((uint32_t)1u, clkcnt - 1); // ESP_IDF_VERSION_MAJOR >= 5
|
|
lcd_clock.lcd_clk_equ_sysclk = (clkcnt == 1);
|
|
lcd_clock.lcd_ck_idle_edge = true;
|
|
lcd_clock.lcd_ck_out_edge = false;
|
|
lcd_clock.lcd_clkm_div_num = div_n;
|
|
lcd_clock.lcd_clkm_div_b = div_b;
|
|
lcd_clock.lcd_clkm_div_a = div_a;
|
|
lcd_clock.lcd_clk_sel = 2; // clock_select: 1=XTAL CLOCK / 2=240MHz / 3=160MHz
|
|
lcd_clock.clk_en = true;
|
|
_clock_reg_value = lcd_clock.val;
|
|
|
|
_alloc_dmadesc(1);
|
|
|
|
_dev = &LCD_CAM;
|
|
|
|
pb_beginTransaction();
|
|
uint16_t index = 0;
|
|
while (1) {
|
|
uint8_t iob;
|
|
cs_control(0);
|
|
|
|
iob = dsp_cmds[index++];
|
|
pb_writeCommand(iob, 8);
|
|
|
|
uint8_t args = dsp_cmds[index++];
|
|
#ifdef UDSP_DEBUG
|
|
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
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: %02x", iob);
|
|
#endif
|
|
pb_writeData(iob, 8);
|
|
}
|
|
cs_control(1);
|
|
if (args & 0x80) { // delay after the command
|
|
uint32_t delay_ms = 0;
|
|
switch (args & 0xE0) {
|
|
case 0x80: delay_ms = 150; break;
|
|
case 0xA0: delay_ms = 10; break;
|
|
case 0xE0: delay_ms = 500; break;
|
|
}
|
|
if (delay_ms > 0) {
|
|
delay(delay_ms);
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: delay %d ms", delay_ms);
|
|
#endif
|
|
}
|
|
|
|
}
|
|
if (index >= dsp_ncmds) break;
|
|
}
|
|
|
|
pb_endTransaction();
|
|
|
|
|
|
#endif // USE_ESP32_S3
|
|
|
|
}
|
|
|
|
// must init luts on epaper
|
|
if (ep_mode) {
|
|
if (ep_mode == 2) Init_EPD(DISPLAY_INIT_FULL);
|
|
//if (ep_mode == 1) Init_EPD(DISPLAY_INIT_PARTIAL);
|
|
}
|
|
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, "UDisplay: Dsp Init 1 complete");
|
|
#endif
|
|
return this;
|
|
}
|
|
|
|
void uDisplay::DisplayInit(int8_t p, int8_t size, int8_t rot, int8_t font) {
|
|
if (p != DISPLAY_INIT_MODE && ep_mode) {
|
|
ep_update_mode = p;
|
|
if (p == DISPLAY_INIT_PARTIAL) {
|
|
if (lutpsize) {
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, "init partial epaper mode");
|
|
#endif
|
|
SetLut(lut_partial);
|
|
Updateframe_EPD();
|
|
delay_sync(lutptime * 10);
|
|
}
|
|
return;
|
|
} else if (p == DISPLAY_INIT_FULL) {
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, "init full epaper mode");
|
|
#endif
|
|
if (lutfsize) {
|
|
SetLut(lut_full);
|
|
Updateframe_EPD();
|
|
}
|
|
if (ep_mode == 2) {
|
|
ClearFrame_42();
|
|
DisplayFrame_42();
|
|
}
|
|
delay_sync(lutftime * 10);
|
|
return;
|
|
}
|
|
} else {
|
|
setRotation(rot);
|
|
invertDisplay(false);
|
|
setTextWrap(false);
|
|
cp437(true);
|
|
setTextFont(font);
|
|
setTextSize(size);
|
|
setTextColor(fg_col, bg_col);
|
|
setCursor(0,0);
|
|
if (splash_font >= 0) {
|
|
fillScreen(bg_col);
|
|
Updateframe();
|
|
}
|
|
|
|
#ifdef UDSP_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, "Dsp Init 2 complete");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void uDisplay::i2c_command(uint8_t val) {
|
|
//AddLog(LOG_LEVEL_DEBUG, "%02x\n",val );
|
|
wire->beginTransmission(i2caddr);
|
|
wire->write(0);
|
|
wire->write(val);
|
|
wire->endTransmission();
|
|
}
|
|
|
|
|
|
#define WIRE_MAX 32
|
|
|
|
void uDisplay::Updateframe(void) {
|
|
|
|
if (interface == _UDSP_RGB) {
|
|
return;
|
|
}
|
|
|
|
if (ep_mode) {
|
|
Updateframe_EPD();
|
|
return;
|
|
}
|
|
|
|
if (interface == _UDSP_I2C) {
|
|
|
|
#if 0
|
|
i2c_command(saw_1);
|
|
i2c_command(i2c_page_start);
|
|
i2c_command(i2c_page_end);
|
|
i2c_command(saw_2);
|
|
i2c_command(i2c_col_start);
|
|
i2c_command(i2c_col_end);
|
|
|
|
uint16_t count = gxs * ((gys + 7) / 8);
|
|
uint8_t *ptr = framebuffer;
|
|
wire->beginTransmission(i2caddr);
|
|
i2c_command(saw_3);
|
|
uint8_t bytesOut = 1;
|
|
while (count--) {
|
|
if (bytesOut >= WIRE_MAX) {
|
|
wire->endTransmission();
|
|
wire->beginTransmission(i2caddr);
|
|
i2c_command(saw_3);
|
|
bytesOut = 1;
|
|
}
|
|
i2c_command(*ptr++);
|
|
bytesOut++;
|
|
}
|
|
wire->endTransmission();
|
|
#else
|
|
|
|
i2c_command(saw_1 | 0x0); // set low col = 0, 0x00
|
|
i2c_command(i2c_page_start | 0x0); // set hi col = 0, 0x10
|
|
i2c_command(i2c_page_end | 0x0); // set startline line #0, 0x40
|
|
|
|
uint8_t ys = gys >> 3;
|
|
uint8_t xs = gxs >> 3;
|
|
//uint8_t xs = 132 >> 3;
|
|
uint8_t m_row = saw_2;
|
|
uint8_t m_col = i2c_col_start;
|
|
|
|
uint16_t p = 0;
|
|
|
|
uint8_t i, j, k = 0;
|
|
|
|
for ( i = 0; i < ys; i++) {
|
|
// send a bunch of data in one xmission
|
|
i2c_command(0xB0 + i + m_row); //set page address
|
|
i2c_command(m_col & 0xf); //set lower column address
|
|
i2c_command(0x10 | (m_col >> 4)); //set higher column address
|
|
|
|
for ( j = 0; j < 8; j++) {
|
|
wire->beginTransmission(i2caddr);
|
|
wire->write(0x40);
|
|
for ( k = 0; k < xs; k++, p++) {
|
|
wire->write(framebuffer[p]);
|
|
}
|
|
wire->endTransmission();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
if (interface == _UDSP_SPI) {
|
|
if (framebuffer == nullptr) { return; }
|
|
|
|
SPI_BEGIN_TRANSACTION
|
|
SPI_CS_LOW
|
|
|
|
// below commands are not needed for SH1107
|
|
// ulcd_command(saw_1 | 0x0); // set low col = 0, 0x00
|
|
// ulcd_command(i2c_page_start | 0x0); // set hi col = 0, 0x10
|
|
// ulcd_command(i2c_page_end | 0x0); // set startline line #0, 0x40
|
|
|
|
uint8_t ys = gys >> 3;
|
|
uint8_t xs = gxs >> 3;
|
|
//uint8_t xs = 132 >> 3;
|
|
uint8_t m_row = saw_2;
|
|
uint8_t m_col = i2c_col_start;
|
|
// 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;
|
|
|
|
uint8_t i, j, k = 0;
|
|
for ( i = 0; i < ys; i++) { // i = line from 0 to ys
|
|
// send a bunch of data in one xmission
|
|
ulcd_command(0xB0 + i + m_row); //set page address
|
|
ulcd_command(m_col & 0xf); //set lower column address
|
|
ulcd_command(0x10 | (m_col >> 4)); //set higher column address
|
|
|
|
for ( j = 0; j < 8; j++) {
|
|
for ( k = 0; k < xs; k++, p++) {
|
|
ulcd_data8(framebuffer[p]);
|
|
}
|
|
}
|
|
}
|
|
|
|
SPI_CS_HIGH
|
|
SPI_END_TRANSACTION
|
|
|
|
}
|
|
|
|
}
|