/* * 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 "freertos/task.h" #include "soc/spi_reg.h" #include "driver/gpio.h" // ==================================================== // ==== Global variables, default values ============== // Converts colors to grayscale if set to 1 uint8_t tft_gray_scale = 0; // Spi clock for reading data from display memory in Hz uint32_t tft_max_rdclock = 8000000; // Default display dimensions int tft_width = DEFAULT_TFT_DISPLAY_WIDTH; int tft_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 tft_disp_spi = NULL; spi_lobo_device_handle_t tft_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 (tft_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 (tft_disp_spi->host->dma_chan) spi_lobo_dmaworkaround_idle(tft_disp_spi->host->dma_chan); // Reset DMA tft_disp_spi->host->hw->dma_conf.val |= SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST; tft_disp_spi->host->hw->dma_out_link.start=0; tft_disp_spi->host->hw->dma_in_link.start=0; tft_disp_spi->host->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); tft_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(tft_disp_spi, 0); } //--------------------------------- esp_err_t IRAM_ATTR disp_deselect() { wait_trans_finish(1); return spi_lobo_device_deselect(tft_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 (tft_disp_spi->host->hw->cmd.usr); // Set DC to 0 (command mode); gpio_set_level(PIN_NUM_DC, 0); tft_disp_spi->host->hw->data_buf[0] = (uint32_t)cmd; _spi_transfer_start(tft_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 (tft_disp_spi->host->hw->cmd.usr); // Set DC to 0 (command mode); gpio_set_level(PIN_NUM_DC, 0); tft_disp_spi->host->hw->data_buf[0] = (uint32_t)cmd; _spi_transfer_start(tft_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) { tft_disp_spi->host->hw->data_buf[idx] = wd; break; } if (bidx == 32) { tft_disp_spi->host->hw->data_buf[idx] = wd; idx++; bidx = 0; wd = 0; } if (idx == 16) { // SPI buffer full, send data _spi_transfer_start(tft_disp_spi, bits, 0); bits = 0; idx = 0; bidx = 0; } } if (bits > 0) _spi_transfer_start(tft_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 (tft_disp_spi->host->hw->cmd.usr); gpio_set_level(PIN_NUM_DC, 0); tft_disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_CASET; tft_disp_spi->host->hw->user.usr_mosi_highpart = 0; tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; tft_disp_spi->host->hw->user.usr_mosi = 1; tft_disp_spi->host->hw->miso_dlen.usr_miso_dbitlen = 0; tft_disp_spi->host->hw->user.usr_miso = 0; tft_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 (tft_disp_spi->host->hw->cmd.usr); // wait transfer end gpio_set_level(PIN_NUM_DC, 1); tft_disp_spi->host->hw->data_buf[0] = wd; tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 31; tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer while (tft_disp_spi->host->hw->cmd.usr); gpio_set_level(PIN_NUM_DC, 0); tft_disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_PASET; tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; tft_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 (tft_disp_spi->host->hw->cmd.usr); gpio_set_level(PIN_NUM_DC, 1); tft_disp_spi->host->hw->data_buf[0] = wd; tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 31; tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer while (tft_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 (!(tft_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 (tft_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); tft_disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_RAMWR; tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer while (tft_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); tft_disp_spi->host->hw->data_buf[0] = wd; tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 23; tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer while (tft_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(tft_disp_spi->host->dma_chan); //mark channel as active spi_lobo_setup_dma_desc_links(tft_disp_spi->host->dmadesc_tx, size, data, false); tft_disp_spi->host->hw->user.usr_mosi_highpart=0; tft_disp_spi->host->hw->dma_out_link.addr=(int)(&tft_disp_spi->host->dmadesc_tx[0]) & 0xFFFFF; tft_disp_spi->host->hw->dma_out_link.start=1; tft_disp_spi->host->hw->user.usr_mosi_highpart=0; tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = (size * 8) - 1; _dma_sending = 1; // Start transfer tft_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) && (tft_gray_scale)) _color = color2gs(color[0]); while (len) { // ** Get color data from color buffer ** if (rep == 0) { if (tft_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; tft_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; tft_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; tft_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 (tft_disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = bits-1; // set number of bits to be sent tft_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 (!(tft_disp_spi->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX)) return; // Send RAM WRITE command gpio_set_level(PIN_NUM_DC, 0); tft_disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_RAMWR; tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer while (tft_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 (tft_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 > (tft_width*2)) ? (tft_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 (tft_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(tft_disp_spi); if (tft_max_rdclock < current_clock) spi_lobo_set_speed(tft_disp_spi, tft_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(tft_disp_spi, &t); // Receive using direct mode disp_deselect(); if (set_sp) { // Restore spi clock if needed if (tft_max_rdclock < current_clock) spi_lobo_set_speed(tft_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(tft_ts_spi, &t); // Transmit using direct mode if (ret != ESP_OK) return -1; return (((int)(rxdata[0] << 8) | (int)(rxdata[1])) >> 4); */ spi_lobo_device_select(tft_ts_spi, 0); tft_ts_spi->host->hw->data_buf[0] = type; _spi_transfer_start(tft_ts_spi, 24, 24); uint16_t res = (uint16_t)(tft_ts_spi->host->hw->data_buf[0] >> 8); spi_lobo_device_deselect(tft_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(tft_ts_spi, 0); tft_ts_spi->host->hw->data_buf[0] = (val << 8) | reg; _spi_transfer_start(tft_ts_spi, 16, 0); spi_lobo_device_deselect(tft_ts_spi); } //----------------------------------------------- static uint8_t IRAM_ATTR stmpe610_read_byte(uint8_t reg) { spi_lobo_device_select(tft_ts_spi, 0); tft_ts_spi->host->hw->data_buf[0] = (reg << 8) | (reg | 0x80); _spi_transfer_start(tft_ts_spi, 16, 16); uint8_t res = tft_ts_spi->host->hw->data_buf[0] >> 8; spi_lobo_device_deselect(tft_ts_spi); return res; } //----------------------------------------- static uint16_t IRAM_ATTR stmpe610_read_word(uint8_t reg) { spi_lobo_device_select(tft_ts_spi, 0); tft_ts_spi->host->hw->data_buf[0] = ((((reg+1) << 8) | ((reg+1) | 0x80)) << 16) | (reg << 8) | (reg | 0x80); _spi_transfer_start(tft_ts_spi, 32, 32); uint16_t res = (uint16_t)(tft_ts_spi->host->hw->data_buf[0] & 0xFF00); res |= (uint16_t)(tft_ts_spi->host->hw->data_buf[0] >> 24); spi_lobo_device_deselect(tft_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 = tft_gray_scale; tft_gray_scale = 0; cur_speed = spi_lobo_get_speed(tft_disp_spi); color_line = malloc(tft_width*3); if (color_line == NULL) goto exit; line_rdbuf = malloc((tft_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 height if (tft_width < tft_height) { tmp = tft_width; tft_width = tft_height; tft_height = tmp; } } else { // in portrait modes must be width < height if (tft_width > tft_height) { tmp = tft_width; tft_width = tft_height; tft_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(tft_disp_spi, ILI9341_init); } else if (tft_disp_type == DISP_TYPE_ILI9488) { commandList(tft_disp_spi, ILI9488_init); } else if (tft_disp_type == DISP_TYPE_ST7789V) { commandList(tft_disp_spi, ST7789V_init); } else if (tft_disp_type == DISP_TYPE_ST7735) { commandList(tft_disp_spi, STP7735_init); } else if (tft_disp_type == DISP_TYPE_ST7735R) { commandList(tft_disp_spi, STP7735R_init); commandList(tft_disp_spi, Rcmd2green); commandList(tft_disp_spi, Rcmd3); } else if (tft_disp_type == DISP_TYPE_ST7735B) { commandList(tft_disp_spi, STP7735R_init); commandList(tft_disp_spi, Rcmd2red); commandList(tft_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(TFT_STATIC_WIDTH_OFFSET, TFT_STATIC_HEIGHT_OFFSET, tft_width + TFT_STATIC_WIDTH_OFFSET -1, tft_height + TFT_STATIC_HEIGHT_OFFSET -1, (color_t){0,0,0}, (uint32_t)(tft_height*tft_width)); ///Enable backlight #if PIN_NUM_BCKL gpio_set_level(PIN_NUM_BCKL, PIN_BCKL_ON); #endif }