components/tft/tftspi.c

changeset 0
b74b0e4902c3
child 34
5c92103c5e72
--- /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 <string.h>
+#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<len; n++) {
+				color[n] = color2gs(color[n]);
+			}
+	    }
+
+	    _dma_send((uint8_t *)color, len*3);
+	}
+	else {
+		// ==== Repeat color, more than 512 bits total ====
+
+		color_t _color;
+		uint32_t buf_colors;
+		int buf_bytes, to_send;
+
+		/*
+		to_send = len;
+		while (to_send > 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<buf_colors; i++) {
+			trans_cline[i] = _color;
+		}
+
+		// Send 'len' colors
+		to_send = len;
+		while (to_send > 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
+}
+
+

mercurial