diff -r 000000000000 -r b74b0e4902c3 components/tft/tftspi.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/tft/tftspi.c Sat Oct 20 13:23:15 2018 +0200 @@ -0,0 +1,951 @@ +/* + * Author: LoBo (loboris@gmail.com, loboris.github) + * + * Module supporting SPI TFT displays based on ILI9341 & ILI9488 controllers + * + * HIGH SPEED LOW LEVEL DISPLAY FUNCTIONS + * USING DIRECT or DMA SPI TRANSFER MODEs + * +*/ + +#include +#include "tftspi.h" +#include "esp_system.h" +#include "freertos/task.h" +#include "esp_heap_caps.h" +#include "soc/spi_reg.h" + +// ==================================================== +// ==== Global variables, default values ============== + +// Converts colors to grayscale if set to 1 +uint8_t gray_scale = 0; +// Spi clock for reading data from display memory in Hz +uint32_t max_rdclock = 8000000; + +// Default display dimensions +int _width = DEFAULT_TFT_DISPLAY_WIDTH; +int _height = DEFAULT_TFT_DISPLAY_HEIGHT; + +// Display type, DISP_TYPE_ILI9488 or DISP_TYPE_ILI9341 +uint8_t tft_disp_type = DEFAULT_DISP_TYPE; + +// Spi device handles for display and touch screen +spi_lobo_device_handle_t disp_spi = NULL; +spi_lobo_device_handle_t ts_spi = NULL; + +// ==================================================== + + +static color_t *trans_cline = NULL; +static uint8_t _dma_sending = 0; + +// RGB to GRAYSCALE constants +// 0.2989 0.5870 0.1140 +#define GS_FACT_R 0.2989 +#define GS_FACT_G 0.4870 +#define GS_FACT_B 0.2140 + + + +// ==== Functions ===================== + +//------------------------------------------------------ +esp_err_t IRAM_ATTR wait_trans_finish(uint8_t free_line) +{ + // Wait for SPI bus ready + while (disp_spi->host->hw->cmd.usr); + if ((free_line) && (trans_cline)) { + free(trans_cline); + trans_cline = NULL; + } + if (_dma_sending) { + //Tell common code DMA workaround that our DMA channel is idle. If needed, the code will do a DMA reset. + if (disp_spi->host->dma_chan) spi_lobo_dmaworkaround_idle(disp_spi->host->dma_chan); + + // Reset DMA + disp_spi->host->hw->dma_conf.val |= SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST; + disp_spi->host->hw->dma_out_link.start=0; + disp_spi->host->hw->dma_in_link.start=0; + disp_spi->host->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); + disp_spi->host->hw->dma_conf.out_data_burst_en=1; + _dma_sending = 0; + } + return ESP_OK; +} + +//------------------------------- +esp_err_t IRAM_ATTR disp_select() +{ + wait_trans_finish(1); + return spi_lobo_device_select(disp_spi, 0); +} + +//--------------------------------- +esp_err_t IRAM_ATTR disp_deselect() +{ + wait_trans_finish(1); + return spi_lobo_device_deselect(disp_spi); +} + +//--------------------------------------------------------------------------------------------------- +static void IRAM_ATTR _spi_transfer_start(spi_lobo_device_handle_t spi_dev, int wrbits, int rdbits) { + // Load send buffer + spi_dev->host->hw->user.usr_mosi_highpart = 0; + spi_dev->host->hw->mosi_dlen.usr_mosi_dbitlen = wrbits-1; + spi_dev->host->hw->user.usr_mosi = 1; + if (rdbits) { + spi_dev->host->hw->miso_dlen.usr_miso_dbitlen = rdbits; + spi_dev->host->hw->user.usr_miso = 1; + } + else { + spi_dev->host->hw->miso_dlen.usr_miso_dbitlen = 0; + spi_dev->host->hw->user.usr_miso = 0; + } + // Start transfer + spi_dev->host->hw->cmd.usr = 1; + // Wait for SPI bus ready + while (spi_dev->host->hw->cmd.usr); +} + +// Send 1 byte display command, display must be selected +//------------------------------------------------ +void IRAM_ATTR disp_spi_transfer_cmd(int8_t cmd) { + // Wait for SPI bus ready + while (disp_spi->host->hw->cmd.usr); + + // Set DC to 0 (command mode); + gpio_set_level(PIN_NUM_DC, 0); + + disp_spi->host->hw->data_buf[0] = (uint32_t)cmd; + _spi_transfer_start(disp_spi, 8, 0); +} + +// Send command with data to display, display must be selected +//---------------------------------------------------------------------------------- +void IRAM_ATTR disp_spi_transfer_cmd_data(int8_t cmd, uint8_t *data, uint32_t len) { + // Wait for SPI bus ready + while (disp_spi->host->hw->cmd.usr); + + // Set DC to 0 (command mode); + gpio_set_level(PIN_NUM_DC, 0); + + disp_spi->host->hw->data_buf[0] = (uint32_t)cmd; + _spi_transfer_start(disp_spi, 8, 0); + + if ((len == 0) || (data == NULL)) return; + + // Set DC to 1 (data mode); + gpio_set_level(PIN_NUM_DC, 1); + + uint8_t idx=0, bidx=0; + uint32_t bits=0; + uint32_t count=0; + uint32_t wd = 0; + while (count < len) { + // get data byte from buffer + wd |= (uint32_t)data[count] << bidx; + count++; + bits += 8; + bidx += 8; + if (count == len) { + disp_spi->host->hw->data_buf[idx] = wd; + break; + } + if (bidx == 32) { + disp_spi->host->hw->data_buf[idx] = wd; + idx++; + bidx = 0; + wd = 0; + } + if (idx == 16) { + // SPI buffer full, send data + _spi_transfer_start(disp_spi, bits, 0); + + bits = 0; + idx = 0; + bidx = 0; + } + } + if (bits > 0) _spi_transfer_start(disp_spi, bits, 0); +} + +// Set the address window for display write & read commands, display must be selected +//--------------------------------------------------------------------------------------------------- +static void IRAM_ATTR disp_spi_transfer_addrwin(uint16_t x1, uint16_t x2, uint16_t y1, uint16_t y2) { + uint32_t wd; + + taskDISABLE_INTERRUPTS(); + // Wait for SPI bus ready + while (disp_spi->host->hw->cmd.usr); + gpio_set_level(PIN_NUM_DC, 0); + + disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_CASET; + disp_spi->host->hw->user.usr_mosi_highpart = 0; + disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; + disp_spi->host->hw->user.usr_mosi = 1; + disp_spi->host->hw->miso_dlen.usr_miso_dbitlen = 0; + disp_spi->host->hw->user.usr_miso = 0; + + disp_spi->host->hw->cmd.usr = 1; // Start transfer + + wd = (uint32_t)(x1>>8); + wd |= (uint32_t)(x1&0xff) << 8; + wd |= (uint32_t)(x2>>8) << 16; + wd |= (uint32_t)(x2&0xff) << 24; + + while (disp_spi->host->hw->cmd.usr); // wait transfer end + gpio_set_level(PIN_NUM_DC, 1); + disp_spi->host->hw->data_buf[0] = wd; + disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 31; + disp_spi->host->hw->cmd.usr = 1; // Start transfer + + while (disp_spi->host->hw->cmd.usr); + gpio_set_level(PIN_NUM_DC, 0); + disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_PASET; + disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; + disp_spi->host->hw->cmd.usr = 1; // Start transfer + + wd = (uint32_t)(y1>>8); + wd |= (uint32_t)(y1&0xff) << 8; + wd |= (uint32_t)(y2>>8) << 16; + wd |= (uint32_t)(y2&0xff) << 24; + + while (disp_spi->host->hw->cmd.usr); + gpio_set_level(PIN_NUM_DC, 1); + + disp_spi->host->hw->data_buf[0] = wd; + disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 31; + disp_spi->host->hw->cmd.usr = 1; // Start transfer + while (disp_spi->host->hw->cmd.usr); + taskENABLE_INTERRUPTS(); +} + +// Convert color to gray scale +//---------------------------------------------- +static color_t IRAM_ATTR color2gs(color_t color) +{ + color_t _color; + float gs_clr = GS_FACT_R * color.r + GS_FACT_G * color.g + GS_FACT_B * color.b; + if (gs_clr > 255) gs_clr = 255; + + _color.r = (uint8_t)gs_clr; + _color.g = (uint8_t)gs_clr; + _color.b = (uint8_t)gs_clr; + + return _color; +} + +// Set display pixel at given coordinates to given color +//------------------------------------------------------------------------ +void IRAM_ATTR drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) +{ + if (!(disp_spi->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX)) return; + + if (sel) { + if (disp_select()) return; + } + else wait_trans_finish(1); + + uint32_t wd = 0; + color_t _color = color; + if (gray_scale) _color = color2gs(color); + + taskDISABLE_INTERRUPTS(); + disp_spi_transfer_addrwin(x, x+1, y, y+1); + + // Send RAM WRITE command + gpio_set_level(PIN_NUM_DC, 0); + disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_RAMWR; + disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; + disp_spi->host->hw->cmd.usr = 1; // Start transfer + while (disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready + + wd = (uint32_t)_color.r; + wd |= (uint32_t)_color.g << 8; + wd |= (uint32_t)_color.b << 16; + + // Set DC to 1 (data mode); + gpio_set_level(PIN_NUM_DC, 1); + + disp_spi->host->hw->data_buf[0] = wd; + disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 23; + disp_spi->host->hw->cmd.usr = 1; // Start transfer + while (disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready + + taskENABLE_INTERRUPTS(); + if (sel) disp_deselect(); +} + +//----------------------------------------------------------- +static void IRAM_ATTR _dma_send(uint8_t *data, uint32_t size) +{ + //Fill DMA descriptors + spi_lobo_dmaworkaround_transfer_active(disp_spi->host->dma_chan); //mark channel as active + spi_lobo_setup_dma_desc_links(disp_spi->host->dmadesc_tx, size, data, false); + disp_spi->host->hw->user.usr_mosi_highpart=0; + disp_spi->host->hw->dma_out_link.addr=(int)(&disp_spi->host->dmadesc_tx[0]) & 0xFFFFF; + disp_spi->host->hw->dma_out_link.start=1; + disp_spi->host->hw->user.usr_mosi_highpart=0; + + disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = (size * 8) - 1; + + _dma_sending = 1; + // Start transfer + disp_spi->host->hw->cmd.usr = 1; +} + +//--------------------------------------------------------------------------- +static void IRAM_ATTR _direct_send(color_t *color, uint32_t len, uint8_t rep) +{ + uint32_t cidx = 0; // color buffer index + uint32_t wd = 0; + int idx = 0; + int bits = 0; + int wbits = 0; + + taskDISABLE_INTERRUPTS(); + color_t _color = color[0]; + if ((rep) && (gray_scale)) _color = color2gs(color[0]); + + while (len) { + // ** Get color data from color buffer ** + if (rep == 0) { + if (gray_scale) _color = color2gs(color[cidx]); + else _color = color[cidx]; + } + + wd |= (uint32_t)_color.r << wbits; + wbits += 8; + if (wbits == 32) { + bits += wbits; + wbits = 0; + disp_spi->host->hw->data_buf[idx++] = wd; + wd = 0; + } + wd |= (uint32_t)_color.g << wbits; + wbits += 8; + if (wbits == 32) { + bits += wbits; + wbits = 0; + disp_spi->host->hw->data_buf[idx++] = wd; + wd = 0; + } + wd |= (uint32_t)_color.b << wbits; + wbits += 8; + if (wbits == 32) { + bits += wbits; + wbits = 0; + disp_spi->host->hw->data_buf[idx++] = wd; + wd = 0; + } + len--; // Decrement colors counter + if (rep == 0) cidx++; // if not repeating color, increment color buffer index + } + if (bits) { + while (disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready + disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = bits-1; // set number of bits to be sent + disp_spi->host->hw->cmd.usr = 1; // Start transfer + } + taskENABLE_INTERRUPTS(); +} + +// ================================================================ +// === Main function to send data to display ====================== +// If rep==true: repeat sending color data to display 'len' times +// If rep==false: send 'len' color data from color buffer to display +// ** Device must already be selected and address window set ** +// ================================================================ +//---------------------------------------------------------------------------------------------- +static void IRAM_ATTR _TFT_pushColorRep(color_t *color, uint32_t len, uint8_t rep, uint8_t wait) +{ + if (len == 0) return; + if (!(disp_spi->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX)) return; + + // Send RAM WRITE command + gpio_set_level(PIN_NUM_DC, 0); + disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_RAMWR; + disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; + disp_spi->host->hw->cmd.usr = 1; // Start transfer + while (disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready + + gpio_set_level(PIN_NUM_DC, 1); // Set DC to 1 (data mode); + + if ((len*24) <= 512) { + + _direct_send(color, len, rep); + + } + else if (rep == 0) { + // ==== use DMA transfer ==== + // ** Prepare data + if (gray_scale) { + for (int n=0; n 0) { + wait_trans_finish(0); + _direct_send(color, ((to_send > 21) ? 21 : to_send), rep); + to_send -= 21; + } + */ + + buf_colors = ((len > (_width*2)) ? (_width*2) : len); + buf_bytes = buf_colors * 3; + + // Prepare color buffer of maximum 2 color lines + trans_cline = heap_caps_malloc(buf_bytes, MALLOC_CAP_DMA); + if (trans_cline == NULL) return; + + // Prepare fill color + if (gray_scale) _color = color2gs(color[0]); + else _color = color[0]; + + // Fill color buffer with fill color + for (uint32_t i=0; i 0) { + wait_trans_finish(0); + _dma_send((uint8_t *)trans_cline, ((to_send > buf_colors) ? buf_bytes : (to_send*3))); + to_send -= buf_colors; + } + } + + if (wait) wait_trans_finish(1); +} + +// Write 'len' color data to TFT 'window' (x1,y2),(x2,y2) +//------------------------------------------------------------------------------------------- +void IRAM_ATTR TFT_pushColorRep(int x1, int y1, int x2, int y2, color_t color, uint32_t len) +{ + if (disp_select() != ESP_OK) return; + + // ** Send address window ** + disp_spi_transfer_addrwin(x1, x2, y1, y2); + + _TFT_pushColorRep(&color, len, 1, 1); + + disp_deselect(); +} + +// Write 'len' color data to TFT 'window' (x1,y2),(x2,y2) from given buffer +// ** Device must already be selected ** +//----------------------------------------------------------------------------------- +void IRAM_ATTR send_data(int x1, int y1, int x2, int y2, uint32_t len, color_t *buf) +{ + // ** Send address window ** + disp_spi_transfer_addrwin(x1, x2, y1, y2); + _TFT_pushColorRep(buf, len, 0, 0); +} + +// Reads 'len' pixels/colors from the TFT's GRAM 'window' +// 'buf' is an array of bytes with 1st byte reserved for reading 1 dummy byte +// and the rest is actually an array of color_t values +//-------------------------------------------------------------------------------------------- +int IRAM_ATTR read_data(int x1, int y1, int x2, int y2, int len, uint8_t *buf, uint8_t set_sp) +{ + spi_lobo_transaction_t t; + uint32_t current_clock = 0; + + memset(&t, 0, sizeof(t)); //Zero out the transaction + memset(buf, 0, len*sizeof(color_t)); + + if (set_sp) { + if (disp_deselect() != ESP_OK) return -1; + // Change spi clock if needed + current_clock = spi_lobo_get_speed(disp_spi); + if (max_rdclock < current_clock) spi_lobo_set_speed(disp_spi, max_rdclock); + } + + if (disp_select() != ESP_OK) return -2; + + // ** Send address window ** + disp_spi_transfer_addrwin(x1, x2, y1, y2); + + // ** GET pixels/colors ** + disp_spi_transfer_cmd(TFT_RAMRD); + + t.length=0; //Send nothing + t.tx_buffer=NULL; + t.rxlength=8*((len*3)+1); //Receive size in bits + t.rx_buffer=buf; + //t.user = (void*)1; + + esp_err_t res = spi_lobo_transfer_data(disp_spi, &t); // Receive using direct mode + + disp_deselect(); + + if (set_sp) { + // Restore spi clock if needed + if (max_rdclock < current_clock) spi_lobo_set_speed(disp_spi, current_clock); + } + + return res; +} + +// Reads one pixel/color from the TFT's GRAM at position (x,y) +//----------------------------------------------- +color_t IRAM_ATTR readPixel(int16_t x, int16_t y) +{ + uint8_t color_buf[sizeof(color_t)+1] = {0}; + + read_data(x, y, x+1, y+1, 1, color_buf, 1); + + color_t color; + color.r = color_buf[1]; + color.g = color_buf[2]; + color.b = color_buf[3]; + return color; +} + +// get 16-bit data from touch controller for specified type +// ** Touch device must already be selected ** +//---------------------------------------- +int IRAM_ATTR touch_get_data(uint8_t type) +{ + /* + esp_err_t ret; + spi_lobo_transaction_t t; + memset(&t, 0, sizeof(t)); //Zero out the transaction + uint8_t rxdata[2] = {0}; + + // send command byte & receive 2 byte response + t.rxlength=8*2; + t.rx_buffer=&rxdata; + t.command = type; + + ret = spi_lobo_transfer_data(ts_spi, &t); // Transmit using direct mode + + if (ret != ESP_OK) res = -1; + res = (((int)(rxdata[0] << 8) | (int)(rxdata[1])) >> 4); +*/ +// spi_lobo_device_select(ts_spi, 0); + + ts_spi->host->hw->data_buf[0] = type; + _spi_transfer_start(ts_spi, 24, 24); + +// printf("touch_get_data(%02X) %06x %02x %02x %02x ", type, ts_spi->host->hw->data_buf[0], +// (ts_spi->host->hw->data_buf[0] & 0x0ff), +// (ts_spi->host->hw->data_buf[0] >> 11 & 0x0ff), +// (ts_spi->host->hw->data_buf[0] >> 19 & 0x0ff) ); + uint16_t res = (uint16_t)(((ts_spi->host->hw->data_buf[0] >> 11 & 0x0ff) << 8) | (ts_spi->host->hw->data_buf[0] >> 19 & 0x0ff)); +// printf("res=%d %04x\n", res, res); +// spi_lobo_device_deselect(ts_spi); + return res; +} + +// ==== STMPE610 =============================================================== + + +// ----- STMPE610 -------------------------------------------------------------------------- + +// Send 1 byte display command, display must be selected +//--------------------------------------------------------- +static void IRAM_ATTR stmpe610_write_reg(uint8_t reg, uint8_t val) { + + spi_lobo_device_select(ts_spi, 0); + + ts_spi->host->hw->data_buf[0] = (val << 8) | reg; + _spi_transfer_start(ts_spi, 16, 0); + + spi_lobo_device_deselect(ts_spi); +} + +//----------------------------------------------- +static uint8_t IRAM_ATTR stmpe610_read_byte(uint8_t reg) { + spi_lobo_device_select(ts_spi, 0); + + ts_spi->host->hw->data_buf[0] = (reg << 8) | (reg | 0x80); + _spi_transfer_start(ts_spi, 16, 16); + uint8_t res = ts_spi->host->hw->data_buf[0] >> 8; + + spi_lobo_device_deselect(ts_spi); + return res; +} + +//----------------------------------------- +static uint16_t IRAM_ATTR stmpe610_read_word(uint8_t reg) { + spi_lobo_device_select(ts_spi, 0); + + ts_spi->host->hw->data_buf[0] = ((((reg+1) << 8) | ((reg+1) | 0x80)) << 16) | (reg << 8) | (reg | 0x80); + _spi_transfer_start(ts_spi, 32, 32); + uint16_t res = (uint16_t)(ts_spi->host->hw->data_buf[0] & 0xFF00); + res |= (uint16_t)(ts_spi->host->hw->data_buf[0] >> 24); + + spi_lobo_device_deselect(ts_spi); + return res; +} + +//----------------------- +uint32_t stmpe610_getID() +{ + uint16_t tid = stmpe610_read_word(0); + uint8_t tver = stmpe610_read_byte(2); + return (tid << 8) | tver; +} + +//================== +void stmpe610_Init() +{ + stmpe610_write_reg(STMPE610_REG_SYS_CTRL1, 0x02); // Software chip reset + vTaskDelay(10 / portTICK_RATE_MS); + + stmpe610_write_reg(STMPE610_REG_SYS_CTRL2, 0x04); // Temperature sensor clock off, GPIO clock off, touch clock on, ADC clock on + + stmpe610_write_reg(STMPE610_REG_INT_EN, 0x00); // Don't Interrupt on INT pin + + stmpe610_write_reg(STMPE610_REG_ADC_CTRL1, 0x48); // ADC conversion time = 80 clock ticks, 12-bit ADC, internal voltage refernce + vTaskDelay(2 / portTICK_RATE_MS); + stmpe610_write_reg(STMPE610_REG_ADC_CTRL2, 0x01); // ADC speed 3.25MHz + stmpe610_write_reg(STMPE610_REG_GPIO_AF, 0x00); // GPIO alternate function - OFF + stmpe610_write_reg(STMPE610_REG_TSC_CFG, 0xE3); // Averaging 8, touch detect delay 1ms, panel driver settling time 1ms + stmpe610_write_reg(STMPE610_REG_FIFO_TH, 0x01); // FIFO threshold = 1 + stmpe610_write_reg(STMPE610_REG_FIFO_STA, 0x01); // FIFO reset enable + stmpe610_write_reg(STMPE610_REG_FIFO_STA, 0x00); // FIFO reset disable + stmpe610_write_reg(STMPE610_REG_TSC_FRACT_XYZ, 0x07); // Z axis data format + stmpe610_write_reg(STMPE610_REG_TSC_I_DRIVE, 0x01); // max 50mA touchscreen line current + stmpe610_write_reg(STMPE610_REG_TSC_CTRL, 0x30); // X&Y&Z, 16 reading window + stmpe610_write_reg(STMPE610_REG_TSC_CTRL, 0x31); // X&Y&Z, 16 reading window, TSC enable + stmpe610_write_reg(STMPE610_REG_INT_STA, 0xFF); // Clear all interrupts + stmpe610_write_reg(STMPE610_REG_INT_CTRL, 0x00); // Level interrupt, disable interrupts +} + +//=========================================================== +int stmpe610_get_touch(uint16_t *x, uint16_t *y, uint16_t *z) +{ + if (!(stmpe610_read_byte(STMPE610_REG_TSC_CTRL) & 0x80)) return 0; + + // Get touch data + uint8_t fifo_size = stmpe610_read_byte(STMPE610_REG_FIFO_SIZE); + while (fifo_size < 2) { + if (!(stmpe610_read_byte(STMPE610_REG_TSC_CTRL) & 0x80)) return 0; + fifo_size = stmpe610_read_byte(STMPE610_REG_FIFO_SIZE); + } + while (fifo_size > 120) { + if (!(stmpe610_read_byte(STMPE610_REG_TSC_CTRL) & 0x80)) return 0; + *x = stmpe610_read_word(STMPE610_REG_TSC_DATA_X); + *y = stmpe610_read_word(STMPE610_REG_TSC_DATA_Y); + *z = stmpe610_read_byte(STMPE610_REG_TSC_DATA_Z); + fifo_size = stmpe610_read_byte(STMPE610_REG_FIFO_SIZE); + } + for (uint8_t i=0; i < (fifo_size-1); i++) { + *x = stmpe610_read_word(STMPE610_REG_TSC_DATA_X); + *y = stmpe610_read_word(STMPE610_REG_TSC_DATA_Y); + *z = stmpe610_read_byte(STMPE610_REG_TSC_DATA_Z); + } + + *x = 4096 - *x; + /* + // Clear the rest of the fifo + { + stmpe610_write_reg(STMPE610_REG_FIFO_STA, 0x01); // FIFO reset enable + stmpe610_write_reg(STMPE610_REG_FIFO_STA, 0x00); // FIFO reset disable + } + */ + return 1; +} + +// ==== STMPE610 =========================================================================== + + +// Find maximum spi clock for successful read from display RAM +// ** Must be used AFTER the display is initialized ** +//====================== +uint32_t find_rd_speed() +{ + esp_err_t ret; + color_t color; + uint32_t max_speed = 1000000; + uint32_t change_speed, cur_speed; + int line_check; + color_t *color_line = NULL; + uint8_t *line_rdbuf = NULL; + uint8_t gs = gray_scale; + + gray_scale = 0; + cur_speed = spi_lobo_get_speed(disp_spi); + + color_line = malloc(_width*3); + if (color_line == NULL) goto exit; + + line_rdbuf = malloc((_width*3)+1); + if (line_rdbuf == NULL) goto exit; + + color_t *rdline = (color_t *)(line_rdbuf+1); + + // Fill test line with colors + color = (color_t){0xEC,0xA8,0x74}; + for (int x=0; x<_width; x++) { + color_line[x] = color; + } + + // Find maximum read spi clock + for (uint32_t speed=2000000; speed<=cur_speed; speed += 1000000) { + change_speed = spi_lobo_set_speed(disp_spi, speed); + if (change_speed == 0) goto exit; + + memset(line_rdbuf, 0, _width*sizeof(color_t)+1); + + if (disp_select()) goto exit; + // Write color line + send_data(0, _height/2, _width-1, _height/2, _width, color_line); + if (disp_deselect()) goto exit; + + // Read color line + ret = read_data(0, _height/2, _width-1, _height/2, _width, line_rdbuf, 0); + + // Compare + line_check = 0; + if (ret == ESP_OK) { + for (int y=0; y<_width; y++) { + if ((color_line[y].r & 0xFC) != (rdline[y].r & 0xFC)) line_check = 1; + else if ((color_line[y].g & 0xFC) != (rdline[y].g & 0xFC)) line_check = 1; + else if ((color_line[y].b & 0xFC) != (rdline[y].b & 0xFC)) line_check = 1; + if (line_check) break; + } + } + else line_check = ret; + + if (line_check) break; + max_speed = speed; + } + +exit: + gray_scale = gs; + if (line_rdbuf) free(line_rdbuf); + if (color_line) free(color_line); + + // restore spi clk + change_speed = spi_lobo_set_speed(disp_spi, cur_speed); + + return max_speed; +} + +//--------------------------------------------------------------------------- +// Companion code to the initialization table. +// Reads and issues a series of LCD commands stored in byte array +//--------------------------------------------------------------------------- +static void commandList(spi_lobo_device_handle_t spi, const uint8_t *addr) { + uint8_t numCommands, numArgs, cmd; + uint16_t ms; + + numCommands = *addr++; // Number of commands to follow + while(numCommands--) { // For each command... + cmd = *addr++; // save command + numArgs = *addr++; // Number of args to follow + ms = numArgs & TFT_CMD_DELAY; // If high bit set, delay follows args + numArgs &= ~TFT_CMD_DELAY; // Mask out delay bit + + disp_spi_transfer_cmd_data(cmd, (uint8_t *)addr, numArgs); + + addr += numArgs; + + if(ms) { + ms = *addr++; // Read post-command delay time (ms) + if(ms == 255) ms = 500; // If 255, delay for 500 ms + vTaskDelay(ms / portTICK_RATE_MS); + } + } +} + +//================================== +void _tft_setRotation(uint8_t rot) { + uint8_t rotation = rot & 3; // can't be higher than 3 + uint8_t send = 1; + uint8_t madctl = 0; + uint16_t tmp; + + if ((rotation & 1)) { + // in landscape modes must be width > height + if (_width < _height) { + tmp = _width; + _width = _height; + _height = tmp; + } + } + else { + // in portrait modes must be width < height + if (_width > _height) { + tmp = _width; + _width = _height; + _height = tmp; + } + } + #if TFT_INVERT_ROTATION + switch (rotation) { + case PORTRAIT: + madctl = (MADCTL_MV | TFT_RGB_BGR); + break; + case LANDSCAPE: + madctl = (MADCTL_MX | TFT_RGB_BGR); + break; + case PORTRAIT_FLIP: + madctl = (MADCTL_MV | TFT_RGB_BGR); + break; + case LANDSCAPE_FLIP: + madctl = (MADCTL_MY | TFT_RGB_BGR); + break; + } + #elif TFT_INVERT_ROTATION1 + switch (rotation) { + case PORTRAIT: + madctl = (MADCTL_MY | MADCTL_MX | TFT_RGB_BGR); + break; + case LANDSCAPE: + madctl = (MADCTL_MY | MADCTL_MV | TFT_RGB_BGR); + break; + case PORTRAIT_FLIP: + madctl = (TFT_RGB_BGR); + break; + case LANDSCAPE_FLIP: + madctl = (MADCTL_MX | MADCTL_MV | TFT_RGB_BGR); + break; + } + #elif TFT_INVERT_ROTATION2 + switch (rotation) { + case PORTRAIT: + madctl = (MADCTL_MX | MADCTL_MV | TFT_RGB_BGR); + break; + case LANDSCAPE: + madctl = (TFT_RGB_BGR); + break; + case PORTRAIT_FLIP: + madctl = (MADCTL_MY | MADCTL_MV | TFT_RGB_BGR); + break; + case LANDSCAPE_FLIP: + madctl = (MADCTL_MY | MADCTL_MX | TFT_RGB_BGR); + break; + } + #else + switch (rotation) { + case PORTRAIT: + madctl = (MADCTL_MX | TFT_RGB_BGR); + break; + case LANDSCAPE: + madctl = (MADCTL_MV | TFT_RGB_BGR); + break; + case PORTRAIT_FLIP: + madctl = (MADCTL_MY | TFT_RGB_BGR); + break; + case LANDSCAPE_FLIP: + madctl = (MADCTL_MX | MADCTL_MY | MADCTL_MV | TFT_RGB_BGR); + break; + } + #endif + if (send) { + if (disp_select() == ESP_OK) { + disp_spi_transfer_cmd_data(TFT_MADCTL, &madctl, 1); + disp_deselect(); + } + } + +} + +//================= +void TFT_PinsInit() +{ + // Route all used pins to GPIO control + gpio_pad_select_gpio(PIN_NUM_CS); + gpio_pad_select_gpio(PIN_NUM_MISO); + gpio_pad_select_gpio(PIN_NUM_MOSI); + gpio_pad_select_gpio(PIN_NUM_CLK); + gpio_pad_select_gpio(PIN_NUM_DC); + + gpio_set_direction(PIN_NUM_MISO, GPIO_MODE_INPUT); + gpio_set_pull_mode(PIN_NUM_MISO, GPIO_PULLUP_ONLY); + gpio_set_direction(PIN_NUM_CS, GPIO_MODE_OUTPUT); + gpio_set_direction(PIN_NUM_MOSI, GPIO_MODE_OUTPUT); + gpio_set_direction(PIN_NUM_CLK, GPIO_MODE_OUTPUT); + gpio_set_direction(PIN_NUM_DC, GPIO_MODE_OUTPUT); + gpio_set_level(PIN_NUM_DC, 0); +#if USE_TOUCH + gpio_pad_select_gpio(PIN_NUM_TCS); + gpio_set_direction(PIN_NUM_TCS, GPIO_MODE_OUTPUT); +#endif +#if PIN_NUM_BCKL + gpio_pad_select_gpio(PIN_NUM_BCKL); + gpio_set_direction(PIN_NUM_BCKL, GPIO_MODE_OUTPUT); + gpio_set_level(PIN_NUM_BCKL, PIN_BCKL_OFF); +#endif + +#if PIN_NUM_RST + gpio_pad_select_gpio(PIN_NUM_RST); + gpio_set_direction(PIN_NUM_RST, GPIO_MODE_OUTPUT); + gpio_set_level(PIN_NUM_RST, 0); +#endif +} + +// Initialize the display +// ==================== +void TFT_display_init() +{ + esp_err_t ret; + +#if PIN_NUM_RST + //Reset the display + gpio_set_level(PIN_NUM_RST, 0); + vTaskDelay(20 / portTICK_RATE_MS); + gpio_set_level(PIN_NUM_RST, 1); + vTaskDelay(150 / portTICK_RATE_MS); +#endif + + ret = disp_select(); + assert(ret==ESP_OK); + //Send all the initialization commands + if (tft_disp_type == DISP_TYPE_ILI9341) { + commandList(disp_spi, ILI9341_init); + } + else if (tft_disp_type == DISP_TYPE_ILI9488) { + commandList(disp_spi, ILI9488_init); + } + else if (tft_disp_type == DISP_TYPE_ST7789V) { + commandList(disp_spi, ST7789V_init); + } + else if (tft_disp_type == DISP_TYPE_ST7735) { + commandList(disp_spi, STP7735_init); + } + else if (tft_disp_type == DISP_TYPE_ST7735R) { + commandList(disp_spi, STP7735R_init); + commandList(disp_spi, Rcmd2green); + commandList(disp_spi, Rcmd3); + } + else if (tft_disp_type == DISP_TYPE_ST7735B) { + commandList(disp_spi, STP7735R_init); + commandList(disp_spi, Rcmd2red); + commandList(disp_spi, Rcmd3); + uint8_t dt = 0xC0; + disp_spi_transfer_cmd_data(TFT_MADCTL, &dt, 1); + } + else assert(0); + + ret = disp_deselect(); + assert(ret==ESP_OK); + + // Clear screen + _tft_setRotation(PORTRAIT); + TFT_pushColorRep(0, 0, _width-1, _height-1, (color_t){0,0,0}, (uint32_t)(_height*_width)); + + ///Enable backlight +#if PIN_NUM_BCKL + gpio_set_level(PIN_NUM_BCKL, PIN_BCKL_ON); +#endif +} + +