Touch.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. // The following touch screen support code by maxpautsch was merged 1/10/17
  2. // https://github.com/maxpautsch
  3. // Define TOUCH_CS is the user setup file to enable this code
  4. // A demo is provided in examples Generic folder
  5. // Additions by Bodmer to double sample, use Z value to improve detection reliability
  6. // and to correct rotation handling
  7. // See license in root directory.
  8. /***************************************************************************************
  9. ** Function name: begin_touch_read_write - was spi_begin_touch
  10. ** Description: Start transaction and select touch controller
  11. ***************************************************************************************/
  12. // The touch controller has a low SPI clock rate
  13. inline void TFT_eSPI::begin_touch_read_write(void){
  14. DMA_BUSY_CHECK;
  15. CS_H; // Just in case it has been left low
  16. #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS)
  17. if (locked) {locked = false; spi.beginTransaction(SPISettings(SPI_TOUCH_FREQUENCY, MSBFIRST, SPI_MODE0));}
  18. #else
  19. spi.setFrequency(SPI_TOUCH_FREQUENCY);
  20. #endif
  21. SET_BUS_READ_MODE;
  22. T_CS_L;
  23. }
  24. /***************************************************************************************
  25. ** Function name: end_touch_read_write - was spi_end_touch
  26. ** Description: End transaction and deselect touch controller
  27. ***************************************************************************************/
  28. inline void TFT_eSPI::end_touch_read_write(void){
  29. T_CS_H;
  30. #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS)
  31. if(!inTransaction) {if (!locked) {locked = true; spi.endTransaction();}}
  32. #else
  33. spi.setFrequency(SPI_FREQUENCY);
  34. #endif
  35. //SET_BUS_WRITE_MODE;
  36. }
  37. /***************************************************************************************
  38. ** Function name: Legacy - deprecated
  39. ** Description: Start/end transaction
  40. ***************************************************************************************/
  41. void TFT_eSPI::spi_begin_touch() {begin_touch_read_write();}
  42. void TFT_eSPI::spi_end_touch() { end_touch_read_write();}
  43. /***************************************************************************************
  44. ** Function name: getTouchRaw
  45. ** Description: read raw touch position. Always returns true.
  46. ***************************************************************************************/
  47. uint8_t TFT_eSPI::getTouchRaw(uint16_t *x, uint16_t *y){
  48. uint16_t tmp;
  49. begin_touch_read_write();
  50. // Start YP sample request for x position, read 4 times and keep last sample
  51. spi.transfer(0xd0); // Start new YP conversion
  52. spi.transfer(0); // Read first 8 bits
  53. spi.transfer(0xd0); // Read last 8 bits and start new YP conversion
  54. spi.transfer(0); // Read first 8 bits
  55. spi.transfer(0xd0); // Read last 8 bits and start new YP conversion
  56. spi.transfer(0); // Read first 8 bits
  57. spi.transfer(0xd0); // Read last 8 bits and start new YP conversion
  58. tmp = spi.transfer(0); // Read first 8 bits
  59. tmp = tmp <<5;
  60. tmp |= 0x1f & (spi.transfer(0x90)>>3); // Read last 8 bits and start new XP conversion
  61. *x = tmp;
  62. // Start XP sample request for y position, read 4 times and keep last sample
  63. spi.transfer(0); // Read first 8 bits
  64. spi.transfer(0x90); // Read last 8 bits and start new XP conversion
  65. spi.transfer(0); // Read first 8 bits
  66. spi.transfer(0x90); // Read last 8 bits and start new XP conversion
  67. spi.transfer(0); // Read first 8 bits
  68. spi.transfer(0x90); // Read last 8 bits and start new XP conversion
  69. tmp = spi.transfer(0); // Read first 8 bits
  70. tmp = tmp <<5;
  71. tmp |= 0x1f & (spi.transfer(0)>>3); // Read last 8 bits
  72. *y = tmp;
  73. end_touch_read_write();
  74. return true;
  75. }
  76. /***************************************************************************************
  77. ** Function name: getTouchRawZ
  78. ** Description: read raw pressure on touchpad and return Z value.
  79. ***************************************************************************************/
  80. uint16_t TFT_eSPI::getTouchRawZ(void){
  81. begin_touch_read_write();
  82. digitalWrite(PIN_D1, LOW);
  83. // Z sample request
  84. int16_t tz = 0xFFF;
  85. spi.transfer(0xb0); // Start new Z1 conversion
  86. tz += spi.transfer16(0xc0) >> 3; // Read Z1 and start Z2 conversion
  87. tz -= spi.transfer16(0x00) >> 3; // Read Z2
  88. end_touch_read_write();
  89. return (uint16_t)tz;
  90. }
  91. /***************************************************************************************
  92. ** Function name: validTouch
  93. ** Description: read validated position. Return false if not pressed.
  94. ***************************************************************************************/
  95. #define _RAWERR 20 // Deadband error allowed in successive position samples
  96. uint8_t TFT_eSPI::validTouch(uint16_t *x, uint16_t *y, uint16_t threshold){
  97. uint16_t x_tmp, y_tmp, x_tmp2, y_tmp2;
  98. // Wait until pressure stops increasing to debounce pressure
  99. uint16_t z1 = 1;
  100. uint16_t z2 = 0;
  101. while (z1 > z2)
  102. {
  103. z2 = z1;
  104. z1 = getTouchRawZ();
  105. delay(1);
  106. }
  107. // Serial.print("Z = ");Serial.println(z1);
  108. if (z1 <= threshold) return false;
  109. getTouchRaw(&x_tmp,&y_tmp);
  110. // Serial.print("Sample 1 x,y = "); Serial.print(x_tmp);Serial.print(",");Serial.print(y_tmp);
  111. // Serial.print(", Z = ");Serial.println(z1);
  112. delay(1); // Small delay to the next sample
  113. if (getTouchRawZ() <= threshold) return false;
  114. delay(2); // Small delay to the next sample
  115. getTouchRaw(&x_tmp2,&y_tmp2);
  116. // Serial.print("Sample 2 x,y = "); Serial.print(x_tmp2);Serial.print(",");Serial.println(y_tmp2);
  117. // Serial.print("Sample difference = ");Serial.print(abs(x_tmp - x_tmp2));Serial.print(",");Serial.println(abs(y_tmp - y_tmp2));
  118. if (abs(x_tmp - x_tmp2) > _RAWERR) return false;
  119. if (abs(y_tmp - y_tmp2) > _RAWERR) return false;
  120. *x = x_tmp;
  121. *y = y_tmp;
  122. return true;
  123. }
  124. /***************************************************************************************
  125. ** Function name: getTouch
  126. ** Description: read callibrated position. Return false if not pressed.
  127. ***************************************************************************************/
  128. #define Z_THRESHOLD 350 // Touch pressure threshold for validating touches
  129. uint8_t TFT_eSPI::getTouch(uint16_t *x, uint16_t *y, uint16_t threshold){
  130. uint16_t x_tmp, y_tmp;
  131. if (threshold<20) threshold = 10;
  132. if (_pressTime > millis()) threshold=10;
  133. uint8_t n = 5;
  134. uint8_t valid = 0;
  135. while (n--)
  136. {
  137. if (validTouch(&x_tmp, &y_tmp, threshold)) valid++;;
  138. }
  139. if (valid<1) { _pressTime = 0; return false; }
  140. _pressTime = millis() + 50;
  141. convertRawXY(&x_tmp, &y_tmp);
  142. if (x_tmp >= _width || y_tmp >= _height) return false;
  143. _pressX = x_tmp;
  144. _pressY = y_tmp;
  145. *x = _pressX;
  146. *y = _pressY;
  147. return valid;
  148. }
  149. /***************************************************************************************
  150. ** Function name: convertRawXY
  151. ** Description: convert raw touch x,y values to screen coordinates
  152. ***************************************************************************************/
  153. void TFT_eSPI::convertRawXY(uint16_t *x, uint16_t *y)
  154. {
  155. uint16_t x_tmp = *x, y_tmp = *y, xx, yy;
  156. if(!touchCalibration_rotate){
  157. xx=(x_tmp-touchCalibration_x0)*_width/touchCalibration_x1;
  158. yy=(y_tmp-touchCalibration_y0)*_height/touchCalibration_y1;
  159. if(touchCalibration_invert_x)
  160. xx = _width - xx;
  161. if(touchCalibration_invert_y)
  162. yy = _height - yy;
  163. } else {
  164. xx=(y_tmp-touchCalibration_x0)*_width/touchCalibration_x1;
  165. yy=(x_tmp-touchCalibration_y0)*_height/touchCalibration_y1;
  166. if(touchCalibration_invert_x)
  167. xx = _width - xx;
  168. if(touchCalibration_invert_y)
  169. yy = _height - yy;
  170. }
  171. *x = xx;
  172. *y = yy;
  173. }
  174. /***************************************************************************************
  175. ** Function name: calibrateTouch
  176. ** Description: generates calibration parameters for touchscreen.
  177. ***************************************************************************************/
  178. void TFT_eSPI::calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t color_bg, uint8_t size){
  179. int16_t values[] = {0,0,0,0,0,0,0,0};
  180. uint16_t x_tmp, y_tmp;
  181. for(uint8_t i = 0; i<4; i++){
  182. fillRect(0, 0, size+1, size+1, color_bg);
  183. fillRect(0, _height-size-1, size+1, size+1, color_bg);
  184. fillRect(_width-size-1, 0, size+1, size+1, color_bg);
  185. fillRect(_width-size-1, _height-size-1, size+1, size+1, color_bg);
  186. if (i == 5) break; // used to clear the arrows
  187. switch (i) {
  188. case 0: // up left
  189. drawLine(0, 0, 0, size, color_fg);
  190. drawLine(0, 0, size, 0, color_fg);
  191. drawLine(0, 0, size , size, color_fg);
  192. break;
  193. case 1: // bot left
  194. drawLine(0, _height-size-1, 0, _height-1, color_fg);
  195. drawLine(0, _height-1, size, _height-1, color_fg);
  196. drawLine(size, _height-size-1, 0, _height-1 , color_fg);
  197. break;
  198. case 2: // up right
  199. drawLine(_width-size-1, 0, _width-1, 0, color_fg);
  200. drawLine(_width-size-1, size, _width-1, 0, color_fg);
  201. drawLine(_width-1, size, _width-1, 0, color_fg);
  202. break;
  203. case 3: // bot right
  204. drawLine(_width-size-1, _height-size-1, _width-1, _height-1, color_fg);
  205. drawLine(_width-1, _height-1-size, _width-1, _height-1, color_fg);
  206. drawLine(_width-1-size, _height-1, _width-1, _height-1, color_fg);
  207. break;
  208. }
  209. // user has to get the chance to release
  210. if(i>0) delay(1000);
  211. for(uint8_t j= 0; j<8; j++){
  212. // Use a lower detect threshold as corners tend to be less sensitive
  213. while(!validTouch(&x_tmp, &y_tmp, Z_THRESHOLD/2));
  214. values[i*2 ] += x_tmp;
  215. values[i*2+1] += y_tmp;
  216. }
  217. values[i*2 ] /= 8;
  218. values[i*2+1] /= 8;
  219. }
  220. // from case 0 to case 1, the y value changed.
  221. // 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.
  222. touchCalibration_rotate = false;
  223. if(abs(values[0]-values[2]) > abs(values[1]-values[3])){
  224. touchCalibration_rotate = true;
  225. touchCalibration_x0 = (values[1] + values[3])/2; // calc min x
  226. touchCalibration_x1 = (values[5] + values[7])/2; // calc max x
  227. touchCalibration_y0 = (values[0] + values[4])/2; // calc min y
  228. touchCalibration_y1 = (values[2] + values[6])/2; // calc max y
  229. } else {
  230. touchCalibration_x0 = (values[0] + values[2])/2; // calc min x
  231. touchCalibration_x1 = (values[4] + values[6])/2; // calc max x
  232. touchCalibration_y0 = (values[1] + values[5])/2; // calc min y
  233. touchCalibration_y1 = (values[3] + values[7])/2; // calc max y
  234. }
  235. // in addition, the touch screen axis could be in the opposite direction of the TFT axis
  236. touchCalibration_invert_x = false;
  237. if(touchCalibration_x0 > touchCalibration_x1){
  238. values[0]=touchCalibration_x0;
  239. touchCalibration_x0 = touchCalibration_x1;
  240. touchCalibration_x1 = values[0];
  241. touchCalibration_invert_x = true;
  242. }
  243. touchCalibration_invert_y = false;
  244. if(touchCalibration_y0 > touchCalibration_y1){
  245. values[0]=touchCalibration_y0;
  246. touchCalibration_y0 = touchCalibration_y1;
  247. touchCalibration_y1 = values[0];
  248. touchCalibration_invert_y = true;
  249. }
  250. // pre calculate
  251. touchCalibration_x1 -= touchCalibration_x0;
  252. touchCalibration_y1 -= touchCalibration_y0;
  253. if(touchCalibration_x0 == 0) touchCalibration_x0 = 1;
  254. if(touchCalibration_x1 == 0) touchCalibration_x1 = 1;
  255. if(touchCalibration_y0 == 0) touchCalibration_y0 = 1;
  256. if(touchCalibration_y1 == 0) touchCalibration_y1 = 1;
  257. // export parameters, if pointer valid
  258. if(parameters != NULL){
  259. parameters[0] = touchCalibration_x0;
  260. parameters[1] = touchCalibration_x1;
  261. parameters[2] = touchCalibration_y0;
  262. parameters[3] = touchCalibration_y1;
  263. parameters[4] = touchCalibration_rotate | (touchCalibration_invert_x <<1) | (touchCalibration_invert_y <<2);
  264. }
  265. }
  266. /***************************************************************************************
  267. ** Function name: setTouch
  268. ** Description: imports calibration parameters for touchscreen.
  269. ***************************************************************************************/
  270. void TFT_eSPI::setTouch(uint16_t *parameters){
  271. touchCalibration_x0 = parameters[0];
  272. touchCalibration_x1 = parameters[1];
  273. touchCalibration_y0 = parameters[2];
  274. touchCalibration_y1 = parameters[3];
  275. if(touchCalibration_x0 == 0) touchCalibration_x0 = 1;
  276. if(touchCalibration_x1 == 0) touchCalibration_x1 = 1;
  277. if(touchCalibration_y0 == 0) touchCalibration_y0 = 1;
  278. if(touchCalibration_y1 == 0) touchCalibration_y1 = 1;
  279. touchCalibration_rotate = parameters[4] & 0x01;
  280. touchCalibration_invert_x = parameters[4] & 0x02;
  281. touchCalibration_invert_y = parameters[4] & 0x04;
  282. }