1 /**************************************************************************/
4 @author K. Townsend (microBuilder.eu)
6 Parts copyright (c) 2001, Carlos E. Vidales. All rights reserved.
10 Software License Agreement (BSD License)
12 Copyright (c) 2010, microBuilder SARL
15 Redistribution and use in source and binary forms, with or without
16 modification, are permitted provided that the following conditions are met:
17 1. Redistributions of source code must retain the above copyright
18 notice, this list of conditions and the following disclaimer.
19 2. Redistributions in binary form must reproduce the above copyright
20 notice, this list of conditions and the following disclaimer in the
21 documentation and/or other materials provided with the distribution.
22 3. Neither the name of the copyright holders nor the
23 names of its contributors may be used to endorse or promote products
24 derived from this software without specific prior written permission.
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
27 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
28 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
30 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
33 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 /**************************************************************************/
38 #include "touchscreen.h"
40 #include "core/adc/adc.h"
41 #include "core/gpio/gpio.h"
42 #include "core/systick/systick.h"
43 #include "drivers/eeprom/eeprom.h"
44 #include "drivers/lcd/tft/lcd.h"
45 #include "drivers/lcd/tft/drawing.h"
46 #include "drivers/lcd/tft/fonts/dejavusans9.h"
48 #define TS_LINE1 "Touch the center of"
49 #define TS_LINE2 "the red circle using"
50 #define TS_LINE3 "a pen or stylus"
52 static bool _tsInitialised
= FALSE
;
53 static uint8_t _tsThreshhold
= CFG_TFTLCD_TS_DEFAULTTHRESHOLD
;
54 tsPoint_t _tsLCDPoints
[3];
55 tsPoint_t _tsTSPoints
[3];
58 /**************************************************************************/
60 /* ----------------------- Private Methods ------------------------------ */
62 /**************************************************************************/
64 /**************************************************************************/
66 @brief Reads the current Z/pressure level using the ADC
68 /**************************************************************************/
69 void tsReadZ(uint32_t* z1
, uint32_t* z2
)
71 if (!_tsInitialised
) tsInit();
74 // XM = GPIO Output Low
75 // YP = GPIO Output High
82 gpioSetDir (TS_XM_PORT
, TS_XM_PIN
, 1);
83 gpioSetDir (TS_YP_PORT
, TS_YP_PIN
, 1);
84 gpioSetDir (TS_YM_PORT
, TS_YM_PIN
, 0);
86 gpioSetValue(TS_XM_PORT
, TS_XM_PIN
, 0); // GND
87 gpioSetValue(TS_YP_PORT
, TS_YP_PIN
, 1); // 3.3V
90 *z1
= adcRead(TS_XP_ADC_CHANNEL
);
93 // XM = GPIO Output Low
94 // YP = GPIO Output High
98 gpioSetDir (TS_YM_PORT
, TS_YM_PIN
, 0);
101 *z2
= adcRead(TS_YM_ADC_CHANNEL
);
104 /**************************************************************************/
106 @brief Reads the current X position using the ADC
108 /**************************************************************************/
109 uint32_t tsReadX(void)
111 if (!_tsInitialised
) tsInit();
113 // XP = GPIO Output High
114 // XM = GPIO Output Low
122 gpioSetDir (TS_XP_PORT
, TS_XP_PIN
, 1);
123 gpioSetDir (TS_XM_PORT
, TS_XM_PIN
, 1);
124 gpioSetDir (TS_YM_PORT
, TS_YM_PIN
, 0);
126 gpioSetValue(TS_XP_PORT
, TS_XP_PIN
, 1); // 3.3V
127 gpioSetValue(TS_XM_PORT
, TS_XM_PIN
, 0); // GND
131 // Return the ADC results
132 return adcRead(TS_YP_ADC_CHANNEL
);
135 /**************************************************************************/
137 @brief Reads the current Y position using the ADC
139 /**************************************************************************/
140 uint32_t tsReadY(void)
142 if (!_tsInitialised
) tsInit();
144 // YP = GPIO Output High
145 // YM = GPIO Output Low
153 gpioSetDir (TS_YP_PORT
, TS_YP_PIN
, 1);
154 gpioSetDir (TS_YM_PORT
, TS_YM_PIN
, 1);
155 gpioSetDir (TS_XP_PORT
, TS_XP_PIN
, 0);
157 gpioSetValue(TS_YP_PORT
, TS_YP_PIN
, 1); // 3.3V
158 gpioSetValue(TS_YM_PORT
, TS_YM_PIN
, 0); // GND
162 // Return the ADC results
163 return adcRead(TS_XM_ADC_CHANNEL
);
166 /**************************************************************************/
168 @brief Centers a line of text horizontally
170 /**************************************************************************/
171 void tsCalibCenterText(char* text
, uint16_t y
, uint16_t color
)
173 drawString((lcdGetWidth() - drawGetStringWidth(&dejaVuSans9ptFontInfo
, text
)) / 2, y
, color
, &dejaVuSans9ptFontInfo
, text
);
176 /**************************************************************************/
178 @brief Renders the calibration screen with an appropriately
179 placed test point and waits for a touch event
181 /**************************************************************************/
182 tsTouchData_t
tsRenderCalibrationScreen(uint16_t x
, uint16_t y
, uint16_t radius
)
184 drawFill(COLOR_WHITE
);
185 tsCalibCenterText(TS_LINE1
, 50, COLOR_GRAY_50
);
186 tsCalibCenterText(TS_LINE2
, 65, COLOR_GRAY_50
);
187 tsCalibCenterText(TS_LINE3
, 80, COLOR_GRAY_50
);
188 drawCircle(x
, y
, radius
, COLOR_RED
);
189 drawCircle(x
, y
, radius
+ 2, COLOR_GRAY_128
);
191 // Wait for a valid touch events
193 tsTouchError_t error
;
197 // Set calibration flag for ts read
198 error
= tsRead(&data
, true);
199 if (!error
&& data
.valid
)
208 /**************************************************************************/
210 @brief Calculates the difference between the touch screen and the
211 actual screen co-ordinates, taking into account misalignment
212 and any physical offset of the touch screen.
214 @note This is based on the public domain touch screen calibration code
215 written by Carlos E. Vidales (copyright (c) 2001).
217 For more information, see the following app notes:
219 - AN2173 - Touch Screen Control and Calibration
220 Svyatoslav Paliy, Cypress Microsystems
221 - Calibration in touch-screen systems
222 Wendy Fang and Tony Chang,
223 Analog Applications Journal, 3Q 2007 (Texas Instruments)
225 /**************************************************************************/
226 int setCalibrationMatrix( tsPoint_t
* displayPtr
, tsPoint_t
* screenPtr
, tsMatrix_t
* matrixPtr
)
230 matrixPtr
->Divider
= ((screenPtr
[0].x
- screenPtr
[2].x
) * (screenPtr
[1].y
- screenPtr
[2].y
)) -
231 ((screenPtr
[1].x
- screenPtr
[2].x
) * (screenPtr
[0].y
- screenPtr
[2].y
)) ;
233 if( matrixPtr
->Divider
== 0 )
239 matrixPtr
->An
= ((displayPtr
[0].x
- displayPtr
[2].x
) * (screenPtr
[1].y
- screenPtr
[2].y
)) -
240 ((displayPtr
[1].x
- displayPtr
[2].x
) * (screenPtr
[0].y
- screenPtr
[2].y
)) ;
242 matrixPtr
->Bn
= ((screenPtr
[0].x
- screenPtr
[2].x
) * (displayPtr
[1].x
- displayPtr
[2].x
)) -
243 ((displayPtr
[0].x
- displayPtr
[2].x
) * (screenPtr
[1].x
- screenPtr
[2].x
)) ;
245 matrixPtr
->Cn
= (screenPtr
[2].x
* displayPtr
[1].x
- screenPtr
[1].x
* displayPtr
[2].x
) * screenPtr
[0].y
+
246 (screenPtr
[0].x
* displayPtr
[2].x
- screenPtr
[2].x
* displayPtr
[0].x
) * screenPtr
[1].y
+
247 (screenPtr
[1].x
* displayPtr
[0].x
- screenPtr
[0].x
* displayPtr
[1].x
) * screenPtr
[2].y
;
249 matrixPtr
->Dn
= ((displayPtr
[0].y
- displayPtr
[2].y
) * (screenPtr
[1].y
- screenPtr
[2].y
)) -
250 ((displayPtr
[1].y
- displayPtr
[2].y
) * (screenPtr
[0].y
- screenPtr
[2].y
)) ;
252 matrixPtr
->En
= ((screenPtr
[0].x
- screenPtr
[2].x
) * (displayPtr
[1].y
- displayPtr
[2].y
)) -
253 ((displayPtr
[0].y
- displayPtr
[2].y
) * (screenPtr
[1].x
- screenPtr
[2].x
)) ;
255 matrixPtr
->Fn
= (screenPtr
[2].x
* displayPtr
[1].y
- screenPtr
[1].x
* displayPtr
[2].y
) * screenPtr
[0].y
+
256 (screenPtr
[0].x
* displayPtr
[2].y
- screenPtr
[2].x
* displayPtr
[0].y
) * screenPtr
[1].y
+
257 (screenPtr
[1].x
* displayPtr
[0].y
- screenPtr
[0].x
* displayPtr
[1].y
) * screenPtr
[2].y
;
259 // Persist data to EEPROM
260 eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_AN
, matrixPtr
->An
);
261 eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_BN
, matrixPtr
->Bn
);
262 eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_CN
, matrixPtr
->Cn
);
263 eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_DN
, matrixPtr
->Dn
);
264 eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_EN
, matrixPtr
->En
);
265 eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_FN
, matrixPtr
->Fn
);
266 eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_DIVIDER
, matrixPtr
->Divider
);
267 eepromWriteU8(CFG_EEPROM_TOUCHSCREEN_CALIBRATED
, 1);
273 /**************************************************************************/
275 @brief Converts the supplied touch screen location (screenPtr) to
276 a pixel location on the display (displayPtr) using the
277 supplied matrix. The screen orientation is also taken into
278 account when converting the touch screen co-ordinate to
279 a pixel location on the LCD.
281 @note This is based on the public domain touch screen calibration code
282 written by Carlos E. Vidales (copyright (c) 2001).
284 /**************************************************************************/
285 int getDisplayPoint( tsPoint_t
* displayPtr
, tsPoint_t
* screenPtr
, tsMatrix_t
* matrixPtr
)
287 int retValue
= TS_ERROR_NONE
;
289 if( matrixPtr
->Divider
!= 0 )
291 displayPtr
->x
= ( (matrixPtr
->An
* screenPtr
->x
) +
292 (matrixPtr
->Bn
* screenPtr
->y
) +
294 ) / matrixPtr
->Divider
;
296 displayPtr
->y
= ( (matrixPtr
->Dn
* screenPtr
->x
) +
297 (matrixPtr
->En
* screenPtr
->y
) +
299 ) / matrixPtr
->Divider
;
303 // ToDo: Default values required or you can never read LCD position!
304 // return TS_ERROR_NOTCALIBRATED;
308 // Adjust value if the screen is in landscape mode
309 lcdOrientation_t orientation
;
310 orientation
= lcdGetOrientation();
311 if (orientation
== LCD_ORIENTATION_LANDSCAPE
)
314 oldx
= displayPtr
->x
;
315 oldy
= displayPtr
->y
;
316 displayPtr
->x
= oldy
;
317 displayPtr
->y
= lcdGetHeight() - oldx
;
323 /**************************************************************************/
325 /* ----------------------- Public Methods ------------------------------- */
327 /**************************************************************************/
329 /**************************************************************************/
331 @brief Initialises the appropriate GPIO pins and ADC for the
334 /**************************************************************************/
337 // Make sure that ADC is initialised
340 // Set initialisation flag
341 _tsInitialised
= TRUE
;
342 _tsThreshhold
= tsGetThreshhold();
344 // Load values from EEPROM if touch screen has already been calibrated
345 if (eepromReadU8(CFG_EEPROM_TOUCHSCREEN_CALIBRATED
) == 1)
347 // Load calibration data
348 _tsMatrix
.An
= eepromReadS32(CFG_EEPROM_TOUCHSCREEN_CAL_AN
);
349 _tsMatrix
.Bn
= eepromReadS32(CFG_EEPROM_TOUCHSCREEN_CAL_BN
);
350 _tsMatrix
.Cn
= eepromReadS32(CFG_EEPROM_TOUCHSCREEN_CAL_CN
);
351 _tsMatrix
.Dn
= eepromReadS32(CFG_EEPROM_TOUCHSCREEN_CAL_DN
);
352 _tsMatrix
.En
= eepromReadS32(CFG_EEPROM_TOUCHSCREEN_CAL_EN
);
353 _tsMatrix
.Fn
= eepromReadS32(CFG_EEPROM_TOUCHSCREEN_CAL_FN
);
354 _tsMatrix
.Divider
= eepromReadS32(CFG_EEPROM_TOUCHSCREEN_CAL_DIVIDER
);
358 // You may want to run the touch screen calibration sequence
359 // here since the ts has apparently never been calibrated!
364 /**************************************************************************/
366 @brief Reads the current X, Y and Z co-ordinates of the touch screen
368 @param[in] calibrating
369 Set to 1 if the read attempt is for calibration data.
370 No attempt will be made to correlate the touch screen
371 and LCD co-ordinates.
373 /**************************************************************************/
374 tsTouchError_t
tsRead(tsTouchData_t
* data
, uint8_t calibrating
)
376 uint32_t x1
, x2
, y1
, y2
, z1
, z2
;
378 // Assign pressure levels regardless of touch state
387 // Abort if the screen is not being touched (0 levels reported)
388 if (z1
< _tsThreshhold
)
391 return TS_ERROR_NONE
;
394 // Get two X/Y readings and compare results
400 // Throw an error if both readings aren't identical
401 if (x1
!= x2
|| y1
!= y2
)
406 return TS_ERROR_XYMISMATCH
;
409 // X/Y seems to be valid and reading has been confirmed twice
413 // Convert x/y values to pixel location with matrix multiply
414 tsPoint_t location
, touch
;
417 // Only calculate the relative LCD value if this isn't for calibration
420 getDisplayPoint( &location
, &touch
, &_tsMatrix
) ;
421 data
->xlcd
= location
.x
;
422 data
->ylcd
= location
.y
;
426 // Assign some false values, but only xraw and yraw are
427 // used for calibration
433 return TS_ERROR_NONE
;
436 /**************************************************************************/
438 @brief Starts the screen calibration process. Each corner will be
439 tested, meaning that each boundary (top, left, right and
440 bottom) will be tested twice and the readings averaged.
442 /**************************************************************************/
443 void tsCalibrate(void)
447 /* --------------- Welcome Screen --------------- */
448 data
= tsRenderCalibrationScreen(lcdGetWidth() / 2, lcdGetHeight() / 2, 5);
451 /* ----------------- First Dot ------------------ */
452 // 10% over and 10% down
453 data
= tsRenderCalibrationScreen(lcdGetWidth() / 10, lcdGetHeight() / 10, 5);
454 _tsLCDPoints
[0].x
= lcdGetWidth() / 10;
455 _tsLCDPoints
[0].y
= lcdGetHeight() / 10;
456 _tsTSPoints
[0].x
= data
.xraw
;
457 _tsTSPoints
[0].y
= data
.yraw
;
458 printf("Point 1 - LCD X:%04d Y:%04d TS X:%04d Y:%04d \r\n",
459 (int)_tsLCDPoints
[0].x
, (int)_tsLCDPoints
[0].y
, (int)_tsTSPoints
[0].x
, (int)_tsTSPoints
[0].y
);
462 /* ---------------- Second Dot ------------------ */
463 // 50% over and 90% down
464 data
= tsRenderCalibrationScreen(lcdGetWidth() / 2, lcdGetHeight() - lcdGetHeight() / 10, 5);
465 _tsLCDPoints
[1].x
= lcdGetWidth() / 2;
466 _tsLCDPoints
[1].y
= lcdGetHeight() - lcdGetHeight() / 10;
467 _tsTSPoints
[1].x
= data
.xraw
;
468 _tsTSPoints
[1].y
= data
.yraw
;
469 printf("Point 2 - LCD X:%04d Y:%04d TS X:%04d Y:%04d \r\n",
470 (int)_tsLCDPoints
[1].x
, (int)_tsLCDPoints
[1].y
, (int)_tsTSPoints
[1].x
, (int)_tsTSPoints
[1].y
);
473 /* ---------------- Third Dot ------------------- */
474 // 90% over and 50% down
475 data
= tsRenderCalibrationScreen(lcdGetWidth() - lcdGetWidth() / 10, lcdGetHeight() / 2, 5);
476 _tsLCDPoints
[2].x
= lcdGetWidth() - lcdGetWidth() / 10;
477 _tsLCDPoints
[2].y
= lcdGetHeight() / 2;
478 _tsTSPoints
[2].x
= data
.xraw
;
479 _tsTSPoints
[2].y
= data
.yraw
;
480 printf("Point 3 - LCD X:%04d Y:%04d TS X:%04d Y:%04d \r\n",
481 (int)_tsLCDPoints
[2].x
, (int)_tsLCDPoints
[2].y
, (int)_tsTSPoints
[2].x
, (int)_tsTSPoints
[2].y
);
484 // Do matrix calculations for calibration and store to EEPROM
485 setCalibrationMatrix(&_tsLCDPoints
[0], &_tsTSPoints
[0], &_tsMatrix
);
488 /**************************************************************************/
490 @brief Causes a blocking delay until a valid touch event occurs
492 @note Thanks to 'rossum' and limor for this nifty little tidbit on
493 debouncing the signals via pressure sensitivity (using Z)
498 #include "drivers/lcd/tft/touchscreen.h"
501 tsTouchError_t error;
505 // Cause a blocking delay until a touch event occurs or 5s passes
506 error = tsWaitForEvent(&data, 5000);
512 case TS_ERROR_TIMEOUT:
513 printf("Timeout occurred %s", CFG_PRINTF_NEWLINE);
521 // A valid touch event occurred ... display data
522 printf("Touch Event: X = %04u, Y = %04u %s",
531 /**************************************************************************/
532 tsTouchError_t
tsWaitForEvent(tsTouchData_t
* data
, uint32_t timeoutMS
)
534 if (!_tsInitialised
) tsInit();
538 // Return the results right away if reading is valid
541 return TS_ERROR_NONE
;
544 // Handle timeout if delay > 0 milliseconds
547 uint32_t startTick
= systickGetTicks();
548 // Systick rollover may occur while waiting for timeout
549 if (startTick
> 0xFFFFFFFF - timeoutMS
)
551 while (data
->valid
== false)
553 // Throw alert if timeout delay has been passed
554 if ((systickGetTicks() < startTick
) && (systickGetTicks() >= (timeoutMS
- (0xFFFFFFFF - startTick
))))
556 return TS_ERROR_TIMEOUT
;
561 // No systick rollover will occur ... calculate timeout the simple way
564 // Wait in infinite loop
565 while (data
->valid
== false)
567 // Throw timeout if delay has been passed
568 if ((systickGetTicks() - startTick
) > timeoutMS
)
570 return TS_ERROR_TIMEOUT
;
576 // No timeout requested ... wait forever
579 while (data
->valid
== false)
585 // Indicate correct reading
586 return TS_ERROR_NONE
;
589 /**************************************************************************/
591 @brief Updates the touch screen threshhold level and saves it
594 /**************************************************************************/
595 int tsSetThreshhold(uint8_t value
)
597 if ((value
< 0) || (value
> 254))
602 // Update threshhold value
603 _tsThreshhold
= value
;
606 eepromWriteU8(CFG_EEPROM_TOUCHSCREEN_THRESHHOLD
, value
);
611 /**************************************************************************/
613 @brief Gets the current touch screen threshhold level from EEPROM
614 (if present) or returns the default value from projectconfig.h
616 /**************************************************************************/
617 uint8_t tsGetThreshhold(void)
619 // Check if custom threshold has been set in eeprom
620 uint8_t thold
= eepromReadU8(CFG_EEPROM_TOUCHSCREEN_THRESHHOLD
);
623 // Use value from EEPROM
624 _tsThreshhold
= thold
;
628 // Use the default value from projectconfig.h
629 _tsThreshhold
= CFG_TFTLCD_TS_DEFAULTTHRESHOLD
;
632 return _tsThreshhold
;