Files
Boris Lovosevic 0de63ef395 Changed the method of passing parameters from tasks/events/interrupts to MicroPython callbacks
Updated MicroPython scheduler functions
  Updated all affected modules

esp-idf sdspi_host driver refactored
  Using SDCard in SPI mode and display at the same time now works
  Tested on M5Stack & Adafruit 2.4" TFT Featherwing

Display module refactored
  uses (modified) esp-idf spi-master driver
  16-bit color mode added
  low level display functions added
  get TP calibration constants function added
  Backlight on/off function added
  M5Stack & GENERIC display types added
  display initialization sequence for unknown display types can now be handled from MicroPython
  tpcalib frozen module updated

I2C module refactored
  SLAVE mode added
  Low level I2C functions added
  esp-idf i2c driver modified

SPI module updated

UART module updated

network module updated

machine module: added method for reading internal ESP32 temperature sensor

_thread module: status of the system tasks is now available in _thread.list()

os module: SDCard mode can now be configured from MicroPython

time.ticks_xx() functions now returns correct tick count after time update
rtc module updated

Added MPU9250 frozen module (available on M5Stack)

Experimental Bluetooth support, not yet finished
Experimental support for eve display modules, not yet finished

License file added
License information in many source files updated/added
2018-02-28 14:46:02 +01:00

1247 lines
39 KiB
C

/*
* This file is part of the MicroPython ESP32 project, https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo
*
* The MIT License (MIT)
*
* Copyright (c) 2018 LoBo (https://github.com/loboris)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* 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 "esp_attr.h"
#include "soc/spi_reg.h"
#include "esp_log.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 = 4000000;
// 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;
uint8_t tft_touch_type = TOUCH_TYPE_NONE;
// Spi device handles for display and touch screen
exspi_device_handle_t *disp_spi = NULL;
exspi_device_handle_t *ts_spi = NULL;
uint8_t bits_per_color = 16;
uint8_t TFT_RGB_BGR = 0;
uint8_t gamma_curve = 0;
uint32_t spi_speed = 10000000;
// ====================================================
static color_t *trans_cline = NULL;
static const char TAG[] = "[TFTSPI]";
static uint8_t invertrot = 0;
uint8_t spibus_is_init = 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 disp_select()
{
//wait_trans_finish(1);
return spi_device_select(disp_spi, 0);
}
//---------------------------------
esp_err_t IRAM_ATTR disp_deselect()
{
//wait_trans_finish(1);
return spi_device_deselect(disp_spi);
}
// 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)
{
while (disp_spi->handle->host->hw->cmd.usr); // Wait for SPI bus ready
// Set DC to 0 (command mode);
gpio_set_level(disp_spi->dc, 0);
disp_spi->handle->host->hw->data_buf[0] = (uint32_t)cmd;
_spi_transfer_start(disp_spi, 8, 0);
if ((len == 0) || (data == NULL)) return;
while (disp_spi->handle->host->hw->cmd.usr); // Wait for SPI bus ready
// Set DC to 1 (data mode);
gpio_set_level(disp_spi->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, fill spi buffer
wd |= (uint32_t)data[count] << bidx;
count++;
bits += 8;
bidx += 8;
if (count == len) {
disp_spi->handle->host->hw->data_buf[idx] = wd;
break;
}
if (bidx == 32) {
disp_spi->handle->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);
}
// Send 1 byte display command, display must be selected
//----------------------------------------------
void IRAM_ATTR disp_spi_transfer_cmd(int8_t cmd)
{
while (disp_spi->handle->host->hw->cmd.usr); // Wait for SPI bus ready
// Set DC to 0 (command mode);
gpio_set_level(disp_spi->dc, 0);
disp_spi->handle->host->hw->data_buf[0] = (uint32_t)cmd;
_spi_transfer_start(disp_spi, 8, 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;
disp_spi_transfer_cmd(TFT_CASET);
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->handle->host->hw->cmd.usr); // wait transfer end
gpio_set_level(disp_spi->dc, 1);
disp_spi->handle->host->hw->data_buf[0] = wd;
_spi_transfer_start(disp_spi, 32, 0);
disp_spi_transfer_cmd(TFT_PASET);
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->handle->host->hw->cmd.usr); // wait transfer end
gpio_set_level(disp_spi->dc, 1);
disp_spi->handle->host->hw->data_buf[0] = wd;
_spi_transfer_start(disp_spi, 32, 0);
}
// 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;
}
// Convert color to 16-bit 565 RGB value
//----------------------------------------------
static uint16_t IRAM_ATTR color16(color_t color)
{
uint16_t _color;
_color = (uint16_t)(color.r & 0xF8) << 8;
_color |= (uint16_t)(color.g & 0xFC) << 3;
_color |= (uint16_t)(color.b & 0xF8) >> 3;
return _color;
}
//======================================================
esp_err_t IRAM_ATTR wait_trans_finish(uint8_t free_line)
{
_wait_trans_finish(disp_spi);
if ((free_line) && (trans_cline)) {
free(trans_cline);
trans_cline = NULL;
}
return ESP_OK;
}
// 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 ((sel) && (disp_select() != ESP_OK)) return;
uint32_t wd = 0;
color_t _color = color;
if (gray_scale) _color = color2gs(color);
wait_trans_finish(1);
disp_spi_transfer_addrwin(x, x+1, y, y+1);
// Send RAM WRITE command
disp_spi_transfer_cmd(TFT_RAMWR);
if (bits_per_color == 16) {
uint16_t _color16 = color16(_color);
wd = (uint32_t)(_color16 >> 8);
wd |= (uint32_t)(_color16 & 0xFF) << 8;
}
else {
wd = (uint32_t)_color.r;
wd |= (uint32_t)_color.g << 8;
wd |= (uint32_t)_color.b << 16;
}
while (disp_spi->handle->host->hw->cmd.usr); // Wait for SPI bus ready
// Set DC to 1 (data mode);
gpio_set_level(disp_spi->dc, 1);
disp_spi->handle->host->hw->data_buf[0] = wd;
_spi_transfer_start(disp_spi, bits_per_color, 0);
if (sel) disp_deselect();
}
// Send colors from color buffer directly, maximum of 512 bits
//---------------------------------------------------------------------------
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;
uint16_t _color16 = 0;
int idx = 0;
int bits = 0;
int wbits = 0;
// Get first color data from color buffer (repeat color)
color_t _color = color[0];
if ((rep) && (gray_scale)) _color = color2gs(color[0]);
if (bits_per_color == 16) _color16 = color16(_color);
while (len) {
if (rep == 0) {
// Get color data from color buffer
if (gray_scale) _color = color2gs(color[cidx]);
else _color = color[cidx];
if (bits_per_color == 16) _color16 = color16(_color);
}
if (bits_per_color == 16) {
wd |= (uint32_t)(_color16 >> 8) << wbits;
wbits += 8;
wd |= (uint32_t)(_color16 & 0xFF) << wbits;
wbits += 8;
if (wbits == 32) {
bits += wbits;
wbits = 0;
disp_spi->handle->host->hw->data_buf[idx++] = wd;
wd = 0;
}
}
else {
wd |= (uint32_t)_color.r << wbits;
wbits += 8;
if (wbits == 32) {
bits += wbits;
wbits = 0;
disp_spi->handle->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->handle->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->handle->host->hw->data_buf[idx++] = wd;
wd = 0;
}
}
len--; // Decrement colors counter
cidx++; // increment color buffer index (used only if not repeating color)
}
if (bits) _spi_transfer_start(disp_spi, bits, 0);
}
// ==================================================================
// === 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;
// Send RAM WRITE command
disp_spi_transfer_cmd(TFT_RAMWR);
while (disp_spi->handle->host->hw->cmd.usr); // Wait for SPI bus ready
gpio_set_level(disp_spi->dc, 1); // Set DC to 1 (data mode);
if ((len * bits_per_color) <= 512) {
// --- up to 512 bits, send directly ---
_direct_send(color, len, rep);
}
else if (rep == 0) {
// --- more than 512 bits, no repeat: use DMA transfer ---
if (trans_cline) return;
int buflen = ((len * bits_per_color) + 31) / 8;
trans_cline = heap_caps_malloc(buflen, MALLOC_CAP_DMA);
if (trans_cline == NULL) return;
// ** Prepare data
if ((!gray_scale) && (bits_per_color != 16)) {
memcpy(trans_cline, color, len*(bits_per_color/8));
}
else {
color_t _color;
uint16_t _color16;
uint8_t *trans_cline16 = (uint8_t *)trans_cline;
for (int n=0; n<len; n++) {
if (gray_scale) _color = color2gs(color[n]);
else _color = color[n];
if (bits_per_color == 16) {
_color16 = color16(_color);
trans_cline16[n*2] = (uint8_t)(_color16 >> 8);
trans_cline16[(n*2)+1] = (uint8_t)(_color16 & 0xFF);
}
else trans_cline[n] = _color;
}
}
_dma_send(disp_spi, (uint8_t *)trans_cline, len * (bits_per_color/8));
}
else {
// --- more than 512 bits, repeat color ---
if (trans_cline) return;
color_t _color;
uint16_t _color16 = 0;
uint8_t *trans_cline16 = NULL;
uint32_t buf_colors;
int buf_bytes, to_send;
// Prepare color buffer of maximum 2 lines
buf_colors = ((len > (_width*2)) ? (_width*2) : len);
buf_bytes = buf_colors * (bits_per_color / 8);
int buflen = ((buf_bytes*8) + 31) / 8;
trans_cline = heap_caps_malloc(buflen, MALLOC_CAP_DMA);
if (trans_cline == NULL) return;
// Prepare fill color
if (gray_scale) _color = color2gs(color[0]);
else _color = color[0];
if (bits_per_color == 16) {
_color16 = color16(_color);
trans_cline16 = (uint8_t *)trans_cline;
}
// Fill color buffer with fill color
for (uint32_t i=0; i<buf_colors; i++) {
if (bits_per_color == 16) {
trans_cline16[i*2] = (uint8_t)(_color16 >> 8);
trans_cline16[(i*2)+1] = (uint8_t)(_color16 & 0xFF);
}
else trans_cline[i] = _color;
}
// Send 'len' colors
to_send = len;
while (to_send > 0) {
wait_trans_finish(0);
_dma_send(disp_spi, (uint8_t *)trans_cline, ((to_send > buf_colors) ? buf_bytes : (to_send * (bits_per_color/8))));
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)
{
wait_trans_finish(1);
if (disp_select() != ESP_OK) return;
// ** Send address window **
disp_spi_transfer_addrwin(x1, x2, y1, y2);
// Send repeated color and wait for all data sent
_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, uint8_t wait)
{
wait_trans_finish(1);
// ** Send address window **
disp_spi_transfer_addrwin(x1, x2, y1, y2);
// Send color buffer
_TFT_pushColorRep(buf, len, 0, wait);
}
//===================================================
uint32_t IRAM_ATTR read_cmd(uint8_t cmd, uint8_t len)
{
if (disp_select() != ESP_OK) return -2;
// Send command command
disp_spi_transfer_cmd(cmd);
while (disp_spi->handle->host->hw->cmd.usr); // Wait for SPI bus ready
// Read data;
gpio_set_level(disp_spi->dc, 1);
disp_spi->handle->host->hw->data_buf[0] = 0xFFFFFFFF;
_spi_transfer_start(disp_spi, 0, len*8);
while (disp_spi->handle->host->hw->cmd.usr); // Wait for SPI bus ready
uint32_t res = disp_spi->handle->host->hw->data_buf[0];
disp_deselect();
return res;
}
// 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_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_get_speed(disp_spi);
if (max_rdclock < current_clock) spi_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;
esp_err_t res = spi_transfer_data_nodma(disp_spi, &t); // Receive using direct mode
disp_deselect();
if (set_sp) {
// Restore spi clock if needed
if (max_rdclock < current_clock) spi_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)
{
/*
while (ts_spi->handle->host->hw->cmd.usr); // Wait for SPI bus ready
ts_spi->handle->host->hw->data_buf[0] = type;
_spi_transfer_start(ts_spi, 24, 24);
uint16_t res = (uint16_t)(ts_spi->handle->host->hw->data_buf[0] >> 8);
while (ts_spi->handle->host->hw->cmd.usr); // Wait for SPI bus ready
*/
spi_transaction_t t;
memset(&t, 0, sizeof(t)); //Zero out the transaction
uint32_t buf = type;
t.length = 24;
t.tx_buffer = (uint8_t *)&buf;
t.rxlength = 24;
t.rx_buffer = (uint8_t *)&buf;
esp_err_t ret = spi_transfer_data_nodma(ts_spi, &t);
if (ret != ESP_OK) return 0;
uint16_t res = (uint16_t)(buf >> 8);
return res;
}
// ==== STMPE610 ===============================================================
// ----- STMPE610 --------------------------------------------------------------------------
//----------------------------------------------------------------
static void IRAM_ATTR stmpe610_write_reg(uint8_t reg, uint8_t val)
{
spi_device_select(ts_spi, 0);
ts_spi->handle->host->hw->data_buf[0] = (val << 8) | reg;
_spi_transfer_start(ts_spi, 16, 16);
while (ts_spi->handle->host->hw->cmd.usr); // Wait for SPI bus ready
spi_device_deselect(ts_spi);
}
//------------------------------------------------------
static uint8_t IRAM_ATTR stmpe610_read_byte(uint8_t reg)
{
spi_device_select(ts_spi, 0);
ts_spi->handle->host->hw->data_buf[0] = (reg << 8) | (reg | 0x80);
_spi_transfer_start(ts_spi, 24, 24);
while (ts_spi->handle->host->hw->cmd.usr); // Wait for SPI bus ready
uint8_t res = (uint8_t)(ts_spi->handle->host->hw->data_buf[0] >> 8);
spi_device_deselect(ts_spi);
return res;
}
//-------------------------------------------------------
static uint16_t IRAM_ATTR stmpe610_read_word(uint8_t reg)
{
spi_device_select(ts_spi, 0);
ts_spi->handle->host->hw->data_buf[0] = ((((reg+1) << 8) | ((reg+1) | 0x80)) << 16) | (reg << 8) | (reg | 0x80);
_spi_transfer_start(ts_spi, 32, 32);
while (ts_spi->handle->host->hw->cmd.usr); // Wait for SPI bus ready
uint16_t res = (uint16_t)(ts_spi->handle->host->hw->data_buf[0] & 0xFF00);
res |= (uint16_t)(ts_spi->handle->host->hw->data_buf[0] >> 24);
spi_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 reference
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
*/
stmpe610_write_reg(STMPE610_REG_SYS_CTRL1, 0x02); // Software chip reset
vTaskDelay(10 / portTICK_RATE_MS);
stmpe610_write_reg(STMPE610_REG_SYS_CTRL2, 0x0); // turn on clocks!
vTaskDelay(2 / portTICK_RATE_MS);
stmpe610_write_reg(STMPE610_REG_TSC_CTRL, STMPE610_REG_TSC_CTRL_XYZ | STMPE610_REG_TSC_CTRL_EN); // XYZ and enable!
stmpe610_write_reg(STMPE610_REG_INT_EN, STMPE610_REG_INT_EN_TOUCHDET);
stmpe610_write_reg(STMPE610_REG_ADC_CTRL1, STMPE610_REG_ADC_CTRL1_10BIT | (0x6 << 4)); // 96 clocks per conversion
vTaskDelay(2 / portTICK_RATE_MS);
stmpe610_write_reg(STMPE610_REG_ADC_CTRL2, STMPE610_REG_ADC_CTRL2_6_5MHZ);
stmpe610_write_reg(STMPE610_REG_TSC_CFG, STMPE610_REG_TSC_CFG_4SAMPLE | STMPE610_REG_TSC_CFG_DELAY_1MS | STMPE610_REG_TSC_CFG_SETTLE_5MS);
stmpe610_write_reg(STMPE610_REG_TSC_FRACT_XYZ, 0x6);
stmpe610_write_reg(STMPE610_REG_FIFO_TH, 1);
stmpe610_write_reg(STMPE610_REG_FIFO_STA, STMPE610_REG_FIFO_STA_RESET);
stmpe610_write_reg(STMPE610_REG_FIFO_STA, 0); // unreset
stmpe610_write_reg(STMPE610_REG_TSC_I_DRIVE, STMPE610_REG_TSC_I_DRIVE_50MA);
stmpe610_write_reg(STMPE610_REG_INT_STA, 0xFF); // reset all ints
stmpe610_write_reg(STMPE610_REG_INT_CTRL, STMPE610_REG_INT_CTRL_POL_HIGH | STMPE610_REG_INT_CTRL_ENABLE);
}
//---------------------------------------------------------------
static void stmpe_readData(uint32_t *x, uint32_t *y, uint32_t *z)
{
uint8_t data[4];
data[0] = stmpe610_read_byte(STMPE610_REG_TSC_DATA);
data[1] = stmpe610_read_byte(STMPE610_REG_TSC_DATA);
data[2] = stmpe610_read_byte(STMPE610_REG_TSC_DATA);
data[3] = stmpe610_read_byte(STMPE610_REG_TSC_DATA);
*x += ((data[0]) << 4) | (data[1] >> 4);
*y += ((data[1] & 0x0F) << 8) | data[2];
*z += data[3];
if ((stmpe610_read_byte(STMPE610_REG_FIFO_STA) & STMPE610_REG_FIFO_STA_EMPTY)) {
stmpe610_write_reg(STMPE610_REG_INT_STA, 0xFF); // reset all 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;
int n = 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);
n++;
if (n > 1000) return 0;
}
n = 0;
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);
n++;
if (n > 1000) return 0;
}
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
//}
*/
*x = 0;
*y = 0;
*z = 0;
if (!(stmpe610_read_byte(STMPE610_REG_TSC_CTRL) & 0x80)) {
// no touch data
return 0;
}
int n = 0;
uint32_t tx = 0;
uint32_t ty = 0;
uint32_t tz = 0;
while (!((stmpe610_read_byte(STMPE610_REG_FIFO_STA) & STMPE610_REG_FIFO_STA_EMPTY))) {
stmpe_readData(&tx, &ty, &tz);
n++;
}
if (n) {
// return the average values
*x = 4096 - (uint16_t)(tx / n);
*y = (uint16_t)(ty / n);
*z = (uint16_t)(tz / n);
}
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_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){0xE8,0xAC,0x78};
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_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, 1);
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_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(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 (invertrot == 2) {
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;
}
}
else if (invertrot == 3) {
// used for M5Stack display
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 if (invertrot == 1) {
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;
}
}
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;
}
}
if (send) {
if (disp_select() == ESP_OK) {
disp_spi_transfer_cmd_data(TFT_MADCTL, &madctl, 1);
disp_deselect();
}
}
}
//---------------------------------------
void bcklOff(display_config_t *dconfig) {
if (dconfig->bckl >= 0) {
gpio_set_level(dconfig->bckl, (dconfig->bckl_on & 1) ^ 1);
}
}
//--------------------------------------
void bcklOn(display_config_t *dconfig) {
if (dconfig->bckl >= 0) {
gpio_set_level(dconfig->bckl, dconfig->bckl_on & 1);
}
}
//===========================================
void _tft_setBitsPerColor(uint8_t bitsperc) {
if ((tft_disp_type != DISP_TYPE_ILI9488) && ((bitsperc == 16) || (bitsperc == 24))) {
uint8_t bpc = DISP_COLOR_BITS_16;
if (bitsperc == 24) bpc = DISP_COLOR_BITS_24;
if (disp_select() == ESP_OK) {
disp_spi_transfer_cmd_data(TFT_CMD_PIXFMT, &bpc, 1);
disp_deselect();
}
bits_per_color = bitsperc;
}
}
//--------------------------------------------------------
static void TFT_PinsInit(display_config_t *dconfig)
{
// Route all used pins to GPIO control
if (!spibus_is_init) {
gpio_pad_select_gpio(dconfig->miso);
gpio_pad_select_gpio(dconfig->mosi);
gpio_pad_select_gpio(dconfig->sck);
gpio_set_direction(dconfig->miso, GPIO_MODE_INPUT);
gpio_set_pull_mode(dconfig->miso, GPIO_PULLUP_ONLY);
gpio_set_direction(dconfig->mosi, GPIO_MODE_OUTPUT);
gpio_set_direction(dconfig->sck, GPIO_MODE_OUTPUT);
}
gpio_pad_select_gpio(dconfig->cs);
gpio_pad_select_gpio(dconfig->dc);
gpio_set_direction(dconfig->cs, GPIO_MODE_OUTPUT);
gpio_set_direction(dconfig->dc, GPIO_MODE_OUTPUT);
gpio_set_level(dconfig->dc, 0);
gpio_set_level(dconfig->cs, 1);
if (dconfig->touch != TOUCH_TYPE_NONE) {
gpio_pad_select_gpio(dconfig->tcs);
gpio_set_direction(dconfig->tcs, GPIO_MODE_OUTPUT);
gpio_set_level(dconfig->tcs, 1);
}
if (dconfig->bckl >= 0) {
gpio_pad_select_gpio(dconfig->bckl);
gpio_set_direction(dconfig->bckl, GPIO_MODE_OUTPUT);
gpio_set_level(dconfig->bckl, dconfig->bckl_on ^ 1);
}
if (dconfig->rst >= 0) {
gpio_pad_select_gpio(dconfig->rst);
gpio_set_direction(dconfig->rst, GPIO_MODE_OUTPUT);
gpio_set_level(dconfig->rst, 0);
}
}
//-----------------------------------------------------
static esp_err_t TFT_spiInit(display_config_t *dconfig)
{
esp_err_t ret;
int used_spi = spi_host_used_by_sdspi();
if ((used_spi != 0) && (used_spi == dconfig->host)) {
// change spi host
if (used_spi == VSPI_HOST) disp_spi->spihost = HSPI_HOST;
else disp_spi->spihost = VSPI_HOST;
ESP_LOGW(TAG, "spi bus changed (%d -> %d)", used_spi, disp_spi->spihost);
}
else disp_spi->spihost = dconfig->host;
disp_spi->buscfg = SPIbus_configs[disp_spi->spihost];
if (disp_spi->buscfg == NULL) {
ESP_LOGE(TAG, "spi bus %d not available ", disp_spi->spihost);
return ESP_ERR_INVALID_ARG;
}
disp_spi->dma_channel = 1;
disp_spi->curr_clock = 8000000; // for initialization set the clock to 8MHz
disp_spi->handle = NULL;
disp_spi->cs = dconfig->cs;
disp_spi->dc = dconfig->dc;
disp_spi->selected = 0;
disp_spi->buscfg->miso_io_num = dconfig->miso; // set SPI MISO pin
disp_spi->buscfg->mosi_io_num = dconfig->mosi; // set SPI MOSI pin
disp_spi->buscfg->sclk_io_num = dconfig->sck; // set SPI CLK pin
disp_spi->buscfg->quadwp_io_num = -1;
disp_spi->buscfg->quadhd_io_num = -1;
disp_spi->buscfg->max_transfer_sz = 6*1024;
disp_spi->devcfg.clock_speed_hz = disp_spi->curr_clock; // Initial clock
disp_spi->devcfg.duty_cycle_pos = 128; // 50% duty cycle
disp_spi->devcfg.mode=0; // SPI mode 0
disp_spi->devcfg.spics_io_num = -1; // we will use external CS pin
disp_spi->devcfg.queue_size=1; // we need only one transaction
disp_spi->devcfg.flags=SPI_DEVICE_HALFDUPLEX; // ALWAYS SET to HALF DUPLEX MODE!! for display spi
if (dconfig->touch != TOUCH_TYPE_NONE) {
ts_spi->spihost = disp_spi->spihost; // Use the same spi bus as display
ts_spi->buscfg = SPIbus_configs[ts_spi->spihost];
ts_spi->dma_channel = 1;
ts_spi->handle = NULL;
ts_spi->cs = dconfig->tcs; // external CS
ts_spi->dc = -1;
ts_spi->selected = 0;
if (dconfig->touch == TOUCH_TYPE_STMPE610) {
ts_spi->curr_clock = 1000000;
ts_spi->devcfg.clock_speed_hz = 1000000; // Clock 1 MHz
ts_spi->devcfg.mode = STMPE610_SPI_MODE; // SPI mode 1
}
else {
ts_spi->curr_clock = 2500000;
ts_spi->devcfg.clock_speed_hz = 2500000; // Clock 2.5 MHz
ts_spi->devcfg.mode = 0; //SPI mode 0
}
ts_spi->devcfg.duty_cycle_pos = 128; // 50% duty cycle
ts_spi->devcfg.spics_io_num = -1; // External Touch CS pin (dconfig->tcs)
ts_spi->devcfg.queue_size = 1; // we need only one transaction
ts_spi->devcfg.flags = 0;
}
// ==================================================================
// ==== Initialize the SPI bus and attach the LCD to the SPI bus ====
ret = add_extspi_device(disp_spi);
if (ret != ESP_OK) {
return ret;
}
ESP_LOGD(TAG, "spi bus configured (%d)", disp_spi->spihost);
// ==== Test select/deselect ====
ret = spi_device_select(disp_spi, 1);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error selecting display device");
}
vTaskDelay(10 / portTICK_RATE_MS);
ret = spi_device_deselect(disp_spi);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error deselecting display device");
}
ESP_LOGI(TAG, "attached display device, speed=%u", spi_get_speed(disp_spi));
ESP_LOGI(TAG, "bus uses native pins: %s", spi_uses_native_pins(disp_spi->handle) ? "true" : "false");
if (dconfig->touch != TOUCH_TYPE_NONE) {
// =====================================================
// ==== Attach the touch screen to the same SPI bus ====
ret = add_extspi_device(ts_spi);
if (ret == ESP_OK) {
ESP_LOGD(TAG, "touch screen device added to spi bus (%d)", disp_spi->spihost);
// ==== Test select/deselect ====
ret = spi_device_select(ts_spi, 1);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error selecting touch device");
}
ret = spi_device_deselect(ts_spi);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error deselecting touch device");
}
ESP_LOGD(TAG, "attached TS device, speed=%u", spi_get_speed(ts_spi));
if (dconfig->touch == TOUCH_TYPE_STMPE610) {
stmpe610_Init();
vTaskDelay(10 / portTICK_RATE_MS);
uint32_t tver = stmpe610_getID();
ESP_LOGW(TAG, "STMPE touch initialized, ver: %04x - %02x", tver >> 8, tver & 0xFF);
}
}
else {
ESP_LOGE(TAG, "Error adding touch device to spi bus");
ts_spi->handle = NULL;
dconfig->touch = TOUCH_TYPE_NONE;
}
}
return ESP_OK;
}
//=================================================
void TFT_display_setvars(display_config_t *dconfig)
{
// === SET GLOBAL VARIABLES ==========================
tft_disp_type = dconfig->type;
tft_touch_type = dconfig->touch;
_width = dconfig->width;
_height = dconfig->height;
// ==== Set maximum spi clock for display read ====
// operations, function 'find_rd_speed()' ====
// can be used after display initialization ====
max_rdclock = dconfig->rdspeed;
TFT_RGB_BGR = dconfig->bgr;
gamma_curve = dconfig->gamma;
spi_speed = dconfig->speed;
bits_per_color = dconfig->color_bits;
invertrot = dconfig->invrot;
// ===================================================
}
// Initialize the display
// ==================================================
esp_err_t TFT_display_init(display_config_t *dconfig)
{
esp_err_t ret;
TFT_PinsInit(dconfig);
ret = TFT_spiInit(dconfig);
if (ret != ESP_OK) return ret;
TFT_display_setvars(dconfig);
if (dconfig->rst >= 0) {
//Reset the display
gpio_set_level(dconfig->rst, 0);
vTaskDelay(20 / portTICK_RATE_MS);
gpio_set_level(dconfig->rst, 1);
vTaskDelay(150 / portTICK_RATE_MS);
}
else {
disp_spi_transfer_cmd_data(TFT_CMD_SWRESET, NULL, 0);
vTaskDelay(200 / portTICK_RATE_MS);
}
disp_select();
//Send all the initialization commands
if ((tft_disp_type == DISP_TYPE_ILI9341) || (tft_disp_type == DISP_TYPE_M5STACK)) {
if (dconfig->rst < 0) {
vTaskDelay(50 / portTICK_RATE_MS);
}
commandList(ILI9341_init);
_tft_setBitsPerColor(bits_per_color);
}
else if (tft_disp_type == DISP_TYPE_ILI9488) {
commandList(ILI9488_init);
}
else if (tft_disp_type == DISP_TYPE_ST7789V) {
commandList(ST7789V_init);
_tft_setBitsPerColor(bits_per_color);
}
else if (tft_disp_type == DISP_TYPE_ST7735) {
commandList(STP7735_init);
}
else if (tft_disp_type == DISP_TYPE_ST7735R) {
commandList(STP7735R_init);
commandList(Rcmd2green);
commandList(Rcmd3);
}
else if (tft_disp_type == DISP_TYPE_ST7735B) {
commandList(STP7735R_init);
commandList(Rcmd2red);
commandList(Rcmd3);
uint8_t dt = 0xC0;
disp_spi_transfer_cmd_data(TFT_MADCTL, &dt, 1);
}
else if (tft_disp_type == DISP_TYPE_GENERIC) {
ESP_LOGW(TAG, "Generic display type, you must init the display manually");
///Enable backlight
if (dconfig->bckl >= 0) {
gpio_set_level(dconfig->bckl, dconfig->bckl_on);
}
return ESP_OK;
}
else {
ESP_LOGE(TAG, "Display type %d not supported", tft_disp_type);
disp_deselect();
return ESP_FAIL;
}
disp_deselect();
// 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 (dconfig->bckl >= 0) {
gpio_set_level(dconfig->bckl, dconfig->bckl_on);
}
return ESP_OK;
}