forked from Bodmer/TFT_eSPI
Added 24 bit colour handling alphaBlend to reduce precision loss in multiple blend stages (e.g. in 2D colour gradients). Added option for alpha dither to reduce colour banding in gradients with 16 bit colours. Get rid of compile warnings.
344 lines
13 KiB
C++
344 lines
13 KiB
C++
// The following touch screen support code by maxpautsch was merged 1/10/17
|
|
// https://github.com/maxpautsch
|
|
|
|
// Define TOUCH_CS is the user setup file to enable this code
|
|
|
|
// A demo is provided in examples Generic folder
|
|
|
|
// Additions by Bodmer to double sample, use Z value to improve detection reliability
|
|
// and to correct rotation handling
|
|
|
|
// See license in root directory.
|
|
|
|
/***************************************************************************************
|
|
** Function name: begin_touch_read_write - was spi_begin_touch
|
|
** Description: Start transaction and select touch controller
|
|
***************************************************************************************/
|
|
// The touch controller has a low SPI clock rate
|
|
inline void TFT_eSPI::begin_touch_read_write(void){
|
|
DMA_BUSY_CHECK;
|
|
CS_H; // Just in case it has been left low
|
|
#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS)
|
|
if (locked) {locked = false; spi.beginTransaction(SPISettings(SPI_TOUCH_FREQUENCY, MSBFIRST, SPI_MODE0));}
|
|
#else
|
|
spi.setFrequency(SPI_TOUCH_FREQUENCY);
|
|
#endif
|
|
SET_BUS_READ_MODE;
|
|
T_CS_L;
|
|
}
|
|
|
|
/***************************************************************************************
|
|
** Function name: end_touch_read_write - was spi_end_touch
|
|
** Description: End transaction and deselect touch controller
|
|
***************************************************************************************/
|
|
inline void TFT_eSPI::end_touch_read_write(void){
|
|
T_CS_H;
|
|
#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS)
|
|
if(!inTransaction) {if (!locked) {locked = true; spi.endTransaction();}}
|
|
#else
|
|
spi.setFrequency(SPI_FREQUENCY);
|
|
#endif
|
|
SET_BUS_WRITE_MODE;
|
|
}
|
|
|
|
/***************************************************************************************
|
|
** Function name: Legacy - deprecated
|
|
** Description: Start/end transaction
|
|
***************************************************************************************/
|
|
void TFT_eSPI::spi_begin_touch() {begin_touch_read_write();}
|
|
void TFT_eSPI::spi_end_touch() { end_touch_read_write();}
|
|
|
|
/***************************************************************************************
|
|
** Function name: getTouchRaw
|
|
** Description: read raw touch position. Always returns true.
|
|
***************************************************************************************/
|
|
uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){
|
|
uint16_t tmp;
|
|
|
|
begin_touch_read_write();
|
|
|
|
// Start YP sample request for x position, read 4 times and keep last sample
|
|
spi.transfer(0xd0); // Start new YP conversion
|
|
spi.transfer(0); // Read first 8 bits
|
|
spi.transfer(0xd0); // Read last 8 bits and start new YP conversion
|
|
spi.transfer(0); // Read first 8 bits
|
|
spi.transfer(0xd0); // Read last 8 bits and start new YP conversion
|
|
spi.transfer(0); // Read first 8 bits
|
|
spi.transfer(0xd0); // Read last 8 bits and start new YP conversion
|
|
|
|
tmp = spi.transfer(0); // Read first 8 bits
|
|
tmp = tmp <<5;
|
|
tmp |= 0x1f & (spi.transfer(0x90)>>3); // Read last 8 bits and start new XP conversion
|
|
|
|
*x = tmp;
|
|
|
|
// Start XP sample request for y position, read 4 times and keep last sample
|
|
spi.transfer(0); // Read first 8 bits
|
|
spi.transfer(0x90); // Read last 8 bits and start new XP conversion
|
|
spi.transfer(0); // Read first 8 bits
|
|
spi.transfer(0x90); // Read last 8 bits and start new XP conversion
|
|
spi.transfer(0); // Read first 8 bits
|
|
spi.transfer(0x90); // Read last 8 bits and start new XP conversion
|
|
|
|
tmp = spi.transfer(0); // Read first 8 bits
|
|
tmp = tmp <<5;
|
|
tmp |= 0x1f & (spi.transfer(0)>>3); // Read last 8 bits
|
|
|
|
*y = tmp;
|
|
|
|
end_touch_read_write();
|
|
|
|
return true;
|
|
}
|
|
|
|
/***************************************************************************************
|
|
** Function name: getTouchRawZ
|
|
** Description: read raw pressure on touchpad and return Z value.
|
|
***************************************************************************************/
|
|
uint16_t TFT_eSPI::getTouchRawZ(void){
|
|
|
|
begin_touch_read_write();
|
|
|
|
// Z sample request
|
|
int16_t tz = 0xFFF;
|
|
spi.transfer(0xb0); // Start new Z1 conversion
|
|
tz += spi.transfer16(0xc0) >> 3; // Read Z1 and start Z2 conversion
|
|
tz -= spi.transfer16(0x00) >> 3; // Read Z2
|
|
|
|
end_touch_read_write();
|
|
|
|
return (uint16_t)tz;
|
|
}
|
|
|
|
/***************************************************************************************
|
|
** Function name: validTouch
|
|
** Description: read validated position. Return false if not pressed.
|
|
***************************************************************************************/
|
|
#define _RAWERR 20 // Deadband error allowed in successive position samples
|
|
uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){
|
|
uint16_t x_tmp, y_tmp, x_tmp2, y_tmp2;
|
|
|
|
// Wait until pressure stops increasing to debounce pressure
|
|
uint16_t z1 = 1;
|
|
uint16_t z2 = 0;
|
|
while (z1 > z2)
|
|
{
|
|
z2 = z1;
|
|
z1 = getTouchRawZ();
|
|
delay(1);
|
|
}
|
|
|
|
// Serial.print("Z = ");Serial.println(z1);
|
|
|
|
if (z1 <= threshold) return false;
|
|
|
|
getTouchRaw(&x_tmp,&y_tmp);
|
|
|
|
// Serial.print("Sample 1 x,y = "); Serial.print(x_tmp);Serial.print(",");Serial.print(y_tmp);
|
|
// Serial.print(", Z = ");Serial.println(z1);
|
|
|
|
delay(1); // Small delay to the next sample
|
|
if (getTouchRawZ() <= threshold) return false;
|
|
|
|
delay(2); // Small delay to the next sample
|
|
getTouchRaw(&x_tmp2,&y_tmp2);
|
|
|
|
// Serial.print("Sample 2 x,y = "); Serial.print(x_tmp2);Serial.print(",");Serial.println(y_tmp2);
|
|
// Serial.print("Sample difference = ");Serial.print(abs(x_tmp - x_tmp2));Serial.print(",");Serial.println(abs(y_tmp - y_tmp2));
|
|
|
|
if (abs(x_tmp - x_tmp2) > _RAWERR) return false;
|
|
if (abs(y_tmp - y_tmp2) > _RAWERR) return false;
|
|
|
|
*x = x_tmp;
|
|
*y = y_tmp;
|
|
|
|
return true;
|
|
}
|
|
|
|
/***************************************************************************************
|
|
** Function name: getTouch
|
|
** Description: read callibrated position. Return false if not pressed.
|
|
***************************************************************************************/
|
|
#define Z_THRESHOLD 350 // Touch pressure threshold for validating touches
|
|
uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){
|
|
uint16_t x_tmp, y_tmp;
|
|
|
|
if (threshold<20) threshold = 20;
|
|
if (_pressTime > millis()) threshold=20;
|
|
|
|
uint8_t n = 5;
|
|
uint8_t valid = 0;
|
|
while (n--)
|
|
{
|
|
if (validTouch(&x_tmp, &y_tmp, threshold)) valid++;;
|
|
}
|
|
|
|
if (valid<1) { _pressTime = 0; return false; }
|
|
|
|
_pressTime = millis() + 50;
|
|
|
|
convertRawXY(&x_tmp, &y_tmp);
|
|
|
|
if (x_tmp >= _width || y_tmp >= _height) return false;
|
|
|
|
_pressX = x_tmp;
|
|
_pressY = y_tmp;
|
|
*x = _pressX;
|
|
*y = _pressY;
|
|
return valid;
|
|
}
|
|
|
|
/***************************************************************************************
|
|
** Function name: convertRawXY
|
|
** Description: convert raw touch x,y values to screen coordinates
|
|
***************************************************************************************/
|
|
void TFT_eSPI::convertRawXY(uint16_t *x, uint16_t *y)
|
|
{
|
|
uint16_t x_tmp = *x, y_tmp = *y, xx, yy;
|
|
|
|
if(!touchCalibration_rotate){
|
|
xx=(x_tmp-touchCalibration_x0)*_width/touchCalibration_x1;
|
|
yy=(y_tmp-touchCalibration_y0)*_height/touchCalibration_y1;
|
|
if(touchCalibration_invert_x)
|
|
xx = _width - xx;
|
|
if(touchCalibration_invert_y)
|
|
yy = _height - yy;
|
|
} else {
|
|
xx=(y_tmp-touchCalibration_x0)*_width/touchCalibration_x1;
|
|
yy=(x_tmp-touchCalibration_y0)*_height/touchCalibration_y1;
|
|
if(touchCalibration_invert_x)
|
|
xx = _width - xx;
|
|
if(touchCalibration_invert_y)
|
|
yy = _height - yy;
|
|
}
|
|
*x = xx;
|
|
*y = yy;
|
|
}
|
|
|
|
/***************************************************************************************
|
|
** Function name: calibrateTouch
|
|
** Description: generates calibration parameters for touchscreen.
|
|
***************************************************************************************/
|
|
void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t color_bg, uint8_t size){
|
|
int16_t values[] = {0,0,0,0,0,0,0,0};
|
|
uint16_t x_tmp, y_tmp;
|
|
|
|
|
|
|
|
for(uint8_t i = 0; i<4; i++){
|
|
fillRect(0, 0, size+1, size+1, color_bg);
|
|
fillRect(0, _height-size-1, size+1, size+1, color_bg);
|
|
fillRect(_width-size-1, 0, size+1, size+1, color_bg);
|
|
fillRect(_width-size-1, _height-size-1, size+1, size+1, color_bg);
|
|
|
|
if (i == 5) break; // used to clear the arrows
|
|
|
|
switch (i) {
|
|
case 0: // up left
|
|
drawLine(0, 0, 0, size, color_fg);
|
|
drawLine(0, 0, size, 0, color_fg);
|
|
drawLine(0, 0, size , size, color_fg);
|
|
break;
|
|
case 1: // bot left
|
|
drawLine(0, _height-size-1, 0, _height-1, color_fg);
|
|
drawLine(0, _height-1, size, _height-1, color_fg);
|
|
drawLine(size, _height-size-1, 0, _height-1 , color_fg);
|
|
break;
|
|
case 2: // up right
|
|
drawLine(_width-size-1, 0, _width-1, 0, color_fg);
|
|
drawLine(_width-size-1, size, _width-1, 0, color_fg);
|
|
drawLine(_width-1, size, _width-1, 0, color_fg);
|
|
break;
|
|
case 3: // bot right
|
|
drawLine(_width-size-1, _height-size-1, _width-1, _height-1, color_fg);
|
|
drawLine(_width-1, _height-1-size, _width-1, _height-1, color_fg);
|
|
drawLine(_width-1-size, _height-1, _width-1, _height-1, color_fg);
|
|
break;
|
|
}
|
|
|
|
// user has to get the chance to release
|
|
if(i>0) delay(1000);
|
|
|
|
for(uint8_t j= 0; j<8; j++){
|
|
// Use a lower detect threshold as corners tend to be less sensitive
|
|
while(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD/2));
|
|
values[i*2 ] += x_tmp;
|
|
values[i*2+1] += y_tmp;
|
|
}
|
|
values[i*2 ] /= 8;
|
|
values[i*2+1] /= 8;
|
|
}
|
|
|
|
|
|
// from case 0 to case 1, the y value changed.
|
|
// If the measured delta of the touch x axis is bigger than the delta of the y axis, the touch and TFT axes are switched.
|
|
touchCalibration_rotate = false;
|
|
if(abs(values[0]-values[2]) > abs(values[1]-values[3])){
|
|
touchCalibration_rotate = true;
|
|
touchCalibration_x0 = (values[1] + values[3])/2; // calc min x
|
|
touchCalibration_x1 = (values[5] + values[7])/2; // calc max x
|
|
touchCalibration_y0 = (values[0] + values[4])/2; // calc min y
|
|
touchCalibration_y1 = (values[2] + values[6])/2; // calc max y
|
|
} else {
|
|
touchCalibration_x0 = (values[0] + values[2])/2; // calc min x
|
|
touchCalibration_x1 = (values[4] + values[6])/2; // calc max x
|
|
touchCalibration_y0 = (values[1] + values[5])/2; // calc min y
|
|
touchCalibration_y1 = (values[3] + values[7])/2; // calc max y
|
|
}
|
|
|
|
// in addition, the touch screen axis could be in the opposite direction of the TFT axis
|
|
touchCalibration_invert_x = false;
|
|
if(touchCalibration_x0 > touchCalibration_x1){
|
|
values[0]=touchCalibration_x0;
|
|
touchCalibration_x0 = touchCalibration_x1;
|
|
touchCalibration_x1 = values[0];
|
|
touchCalibration_invert_x = true;
|
|
}
|
|
touchCalibration_invert_y = false;
|
|
if(touchCalibration_y0 > touchCalibration_y1){
|
|
values[0]=touchCalibration_y0;
|
|
touchCalibration_y0 = touchCalibration_y1;
|
|
touchCalibration_y1 = values[0];
|
|
touchCalibration_invert_y = true;
|
|
}
|
|
|
|
// pre calculate
|
|
touchCalibration_x1 -= touchCalibration_x0;
|
|
touchCalibration_y1 -= touchCalibration_y0;
|
|
|
|
if(touchCalibration_x0 == 0) touchCalibration_x0 = 1;
|
|
if(touchCalibration_x1 == 0) touchCalibration_x1 = 1;
|
|
if(touchCalibration_y0 == 0) touchCalibration_y0 = 1;
|
|
if(touchCalibration_y1 == 0) touchCalibration_y1 = 1;
|
|
|
|
// export parameters, if pointer valid
|
|
if(parameters != NULL){
|
|
parameters[0] = touchCalibration_x0;
|
|
parameters[1] = touchCalibration_x1;
|
|
parameters[2] = touchCalibration_y0;
|
|
parameters[3] = touchCalibration_y1;
|
|
parameters[4] = touchCalibration_rotate | (touchCalibration_invert_x <<1) | (touchCalibration_invert_y <<2);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************************
|
|
** Function name: setTouch
|
|
** Description: imports calibration parameters for touchscreen.
|
|
***************************************************************************************/
|
|
void TFT_eSPI::setTouch(uint16_t *parameters){
|
|
touchCalibration_x0 = parameters[0];
|
|
touchCalibration_x1 = parameters[1];
|
|
touchCalibration_y0 = parameters[2];
|
|
touchCalibration_y1 = parameters[3];
|
|
|
|
if(touchCalibration_x0 == 0) touchCalibration_x0 = 1;
|
|
if(touchCalibration_x1 == 0) touchCalibration_x1 = 1;
|
|
if(touchCalibration_y0 == 0) touchCalibration_y0 = 1;
|
|
if(touchCalibration_y1 == 0) touchCalibration_y1 = 1;
|
|
|
|
touchCalibration_rotate = parameters[4] & 0x01;
|
|
touchCalibration_invert_x = parameters[4] & 0x02;
|
|
touchCalibration_invert_y = parameters[4] & 0x04;
|
|
}
|