diff --git a/Adafruit_RA8875.h b/Adafruit_RA8875.h index c5b7391..3df5d8f 100644 --- a/Adafruit_RA8875.h +++ b/Adafruit_RA8875.h @@ -14,6 +14,33 @@ // Sizes! enum RA8875sizes { RA8875_480x272, RA8875_800x480 }; +// Touch screen cal structs +typedef struct Point +{ + int32_t x; + int32_t y; +} tsPoint_t; + +typedef struct Matrix +{ + int32_t An, + Bn, + Cn, + Dn, + En, + Fn, + Divider ; +} tsMatrix_t; + +typedef struct +{ + uint32_t xraw; // Touch screen x + uint32_t yraw; // Touch screen Y + uint16_t xlcd; // LCD co-ordinate X + uint16_t ylcd; // LCD co-ordinate Y + bool valid; // Whether this is a valid reading or not +} tsTouchData_t; + class Adafruit_RA8875 : public Adafruit_GFX { public: Adafruit_RA8875(uint8_t cs, uint8_t rst); diff --git a/examples/ts_calibration/ts_calibration.ino b/examples/ts_calibration/ts_calibration.ino new file mode 100644 index 0000000..9ca177b --- /dev/null +++ b/examples/ts_calibration/ts_calibration.ino @@ -0,0 +1,343 @@ +#include +#include "Adafruit_GFX.h" +#include "Adafruit_RA8875.h" + +#define RA8875_INT 3 +#define RA8875_CS 10 +#define RA8875_RESET 9 + +Adafruit_RA8875 tft = Adafruit_RA8875(RA8875_CS, RA8875_RESET); +tsPoint_t _tsLCDPoints[3]; +tsPoint_t _tsTSPoints[3]; +tsMatrix_t _tsMatrix; + +/**************************************************************************/ +/*! + @brief Calculates the difference between the touch screen and the + actual screen co-ordinates, taking into account misalignment + and any physical offset of the touch screen. + + @note This is based on the public domain touch screen calibration code + written by Carlos E. Vidales (copyright (c) 2001). + + For more information, see the following app notes: + + - AN2173 - Touch Screen Control and Calibration + Svyatoslav Paliy, Cypress Microsystems + - Calibration in touch-screen systems + Wendy Fang and Tony Chang, + Analog Applications Journal, 3Q 2007 (Texas Instruments) +*/ +/**************************************************************************/ +int setCalibrationMatrix( tsPoint_t * displayPtr, tsPoint_t * screenPtr, tsMatrix_t * matrixPtr) +{ + int retValue = 0; + + matrixPtr->Divider = ((screenPtr[0].x - screenPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - + ((screenPtr[1].x - screenPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; + + if( matrixPtr->Divider == 0 ) + { + retValue = -1 ; + } + else + { + matrixPtr->An = ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - + ((displayPtr[1].x - displayPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; + + matrixPtr->Bn = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].x - displayPtr[2].x)) - + ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].x - screenPtr[2].x)) ; + + matrixPtr->Cn = (screenPtr[2].x * displayPtr[1].x - screenPtr[1].x * displayPtr[2].x) * screenPtr[0].y + + (screenPtr[0].x * displayPtr[2].x - screenPtr[2].x * displayPtr[0].x) * screenPtr[1].y + + (screenPtr[1].x * displayPtr[0].x - screenPtr[0].x * displayPtr[1].x) * screenPtr[2].y ; + + matrixPtr->Dn = ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].y - screenPtr[2].y)) - + ((displayPtr[1].y - displayPtr[2].y) * (screenPtr[0].y - screenPtr[2].y)) ; + + matrixPtr->En = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].y - displayPtr[2].y)) - + ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].x - screenPtr[2].x)) ; + + matrixPtr->Fn = (screenPtr[2].x * displayPtr[1].y - screenPtr[1].x * displayPtr[2].y) * screenPtr[0].y + + (screenPtr[0].x * displayPtr[2].y - screenPtr[2].x * displayPtr[0].y) * screenPtr[1].y + + (screenPtr[1].x * displayPtr[0].y - screenPtr[0].x * displayPtr[1].y) * screenPtr[2].y ; + + // Persist data to EEPROM + // eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_AN, matrixPtr->An); + // eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_BN, matrixPtr->Bn); + // eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_CN, matrixPtr->Cn); + // eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_DN, matrixPtr->Dn); + // eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_EN, matrixPtr->En); + // eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_FN, matrixPtr->Fn); + // eepromWriteS32(CFG_EEPROM_TOUCHSCREEN_CAL_DIVIDER, matrixPtr->Divider); + // eepromWriteU8(CFG_EEPROM_TOUCHSCREEN_CALIBRATED, 1); + } + + return( retValue ) ; +} + +/**************************************************************************/ +/*! + @brief Converts the supplied touch screen location (screenPtr) to + a pixel location on the display (displayPtr) using the + supplied matrix. + + @param[in] displayPtr Pointer to the tsPoint_t that will hold the + compensated co-ordinates after calibration (the + actual equivalent pixel location) + @param[in] screenPtr Pointer to the tsPonint_t that contains the + raw touch screen co-ordinated (pre-cal) + @param[in] matrixPtr Pointer to the calibration matrix coefficients + used during the calibration process (calculated + via the tsCalibrate() helper function) + + @note This is based on the public domain touch screen calibration code + written by Carlos E. Vidales (copyright (c) 2001). +*/ +/**************************************************************************/ +int getDisplayPoint( tsPoint_t * displayPtr, tsPoint_t * screenPtr, tsMatrix_t * matrixPtr ) +{ + int retValue = 0 ; + + if( matrixPtr->Divider != 0 ) + { + displayPtr->x = ( (matrixPtr->An * screenPtr->x) + + (matrixPtr->Bn * screenPtr->y) + + matrixPtr->Cn + ) / matrixPtr->Divider ; + + displayPtr->y = ( (matrixPtr->Dn * screenPtr->x) + + (matrixPtr->En * screenPtr->y) + + matrixPtr->Fn + ) / matrixPtr->Divider ; + } + else + { + return -1; + } + + return( retValue ); +} + +/**************************************************************************/ +/*! + @brief Waits for a touch event +*/ +/**************************************************************************/ +void waitForTouchEvent(tsTouchData_t * touchData) +{ + uint16_t tx, ty; + + /* Clear the touch data object and placeholder variables */ + memset(touchData, 0, sizeof(touchData)); + tx = ty = 0; + + /* Clear any previous interrupts to avoid false buffered reads */ + tft.touchRead(&tx, &ty); + delay(1); + + /* Wait around for a new touch event (INT pin goes low) */ + while (digitalRead(RA8875_INT)) + { + } + + /* Make sure this is really a touch event */ + if (tft.touched()) + { + tft.touchRead(&tx, &ty); + Serial.print("Touch: "); + Serial.print(tx); Serial.print(", "); Serial.println(ty); + touchData->xraw = tx; + touchData->yraw = ty; + touchData->valid = true; + } + else + { + touchData->valid = false; + } +} + +/**************************************************************************/ +/*! + @brief Renders the calibration screen with an appropriately + placed test point and waits for a touch event +*/ +/**************************************************************************/ +tsTouchData_t tsRenderCalibrationScreen(uint16_t x, uint16_t y, uint16_t radius) +{ + tft.fillScreen(RA8875_WHITE); + tft.drawCircle(x, y, radius, RA8875_RED); + tft.drawCircle(x, y, radius + 2, 0x8410); /* 50% Gray */ + + // Wait for a valid touch events + tsTouchData_t data; + + bool valid = false; + while (!valid) + { + /* Keep polling until the TS event flag is valid */ + waitForTouchEvent(&data); + valid = data.valid; + } + + return data; +} + +/**************************************************************************/ +/*! + @brief Starts the screen calibration process. Each corner will be + tested, meaning that each boundary (top, left, right and + bottom) will be tested twice and the readings averaged. +*/ +/**************************************************************************/ +void tsCalibrate(void) +{ + tsTouchData_t data; + + /* --------------- Welcome Screen --------------- */ + Serial.println("Starting the calibration process"); + data = tsRenderCalibrationScreen(tft.width() / 2, tft.height() / 2, 5); + delay(250); + + /* ----------------- First Dot ------------------ */ + // 10% over and 10% down + data = tsRenderCalibrationScreen(tft.width() / 10, tft.height() / 10, 5); + _tsLCDPoints[0].x = tft.width() / 10; + _tsLCDPoints[0].y = tft.height() / 10; + _tsTSPoints[0].x = data.xraw; + _tsTSPoints[0].y = data.yraw; + Serial.print("Point 1 - LCD"); + Serial.print(" X: "); + Serial.print(_tsLCDPoints[0].x); + Serial.print(" Y: "); + Serial.print(_tsLCDPoints[0].y); + Serial.print(" TS X: "); + Serial.print(_tsTSPoints[0].x); + Serial.print(" Y: "); + Serial.println(_tsTSPoints[0].y); + delay(750); + + /* ---------------- Second Dot ------------------ */ + // 50% over and 90% down + data = tsRenderCalibrationScreen(tft.width() / 2, tft.height() - tft.height() / 10, 5); + _tsLCDPoints[1].x = tft.width() / 2; + _tsLCDPoints[1].y = tft.height() - tft.height() / 10; + _tsTSPoints[1].x = data.xraw; + _tsTSPoints[1].y = data.yraw; + Serial.print("Point 2 - LCD"); + Serial.print(" X: "); + Serial.print(_tsLCDPoints[1].x); + Serial.print(" Y: "); + Serial.print(_tsLCDPoints[1].y); + Serial.print(" TS X: "); + Serial.print(_tsTSPoints[1].x); + Serial.print(" Y: "); + Serial.println(_tsTSPoints[1].y); + delay(750); + + /* ---------------- Third Dot ------------------- */ + // 90% over and 50% down + data = tsRenderCalibrationScreen(tft.width() - tft.width() / 10, tft.height() / 2, 5); + _tsLCDPoints[2].x = tft.width() - tft.width() / 10; + _tsLCDPoints[2].y = tft.height() / 2; + _tsTSPoints[2].x = data.xraw; + _tsTSPoints[2].y = data.yraw; + Serial.print("Point 3 - LCD"); + Serial.print(" X: "); + Serial.print(_tsLCDPoints[2].x); + Serial.print(" Y: "); + Serial.print(_tsLCDPoints[2].y); + Serial.print(" TS X: "); + Serial.print(_tsTSPoints[2].x); + Serial.print(" Y: "); + Serial.println(_tsTSPoints[2].y); + delay(750); + + /* Clear the screen */ + tft.fillScreen(RA8875_WHITE); + + // Do matrix calculations for calibration and store to EEPROM + setCalibrationMatrix(&_tsLCDPoints[0], &_tsTSPoints[0], &_tsMatrix); +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +void setup() +{ + Serial.begin(9600); + Serial.println("Hello, RA8875!"); + + /* Initialise the display using 'RA8875_480x272' or 'RA8875_800x480' */ + if (!tft.begin(RA8875_480x272)) + { + Serial.println("RA8875 not found ... check your wires!"); + while (1); + } + + /* Enables the display and sets up the backlight */ + Serial.println("Found RA8875"); + tft.displayOn(true); + tft.GPIOX(true); // Enable TFT - display enable tied to GPIOX + tft.PWM1config(true, RA8875_PWM_CLK_DIV1024); // PWM output for backlight + tft.PWM1out(255); + + /* Enable the touch screen */ + Serial.println("Enabled the touch screen"); + pinMode(RA8875_INT, INPUT); + digitalWrite(RA8875_INT, HIGH); + tft.touchEnable(true); + + // Try some GFX acceleration! + //tft.drawCircle(100, 100, 50, RA8875_BLACK); + //tft.fillCircle(100, 100, 49, RA8875_GREEN); + //tft.drawPixel(10,10,RA8875_BLACK); + //tft.drawPixel(11,11,RA8875_BLACK); + //tft.drawRect(10, 10, 400, 200, RA8875_GREEN); + //tft.fillRect(11, 11, 398, 198, RA8875_BLUE); + //tft.drawLine(10, 10, 200, 100, RA8875_RED); + + tft.fillScreen(RA8875_WHITE); + delay(100); + + /* Start the calibration process */ + tsCalibrate(); + + /* _tsMatrix should now be populated with the correct coefficients! */ + Serial.println("Waiting for touch events ..."); +} + +/**************************************************************************/ +/*! + +*/ +/**************************************************************************/ +void loop() +{ + tsTouchData_t data = { 0 }; + waitForTouchEvent(&data); + + /* Calcuate the real X/Y position based on the calibration matrix */ + tsPoint_t raw = { data.xraw, data.yraw }; /* Raw TS co-ordinates */ + tsPoint_t calibrated = { 0, 0 }; /* Placeholder for calibrated co-ordinates */ + + Serial.print("Raw: "); + Serial.print(raw.x); + Serial.print(", "); + Serial.print(raw.y); + Serial.println(""); + + getDisplayPoint(&calibrated, &raw, &_tsMatrix ); + + Serial.print("Cal: "); + Serial.print(calibrated.x); + Serial.print(", "); + Serial.print(calibrated.y); + Serial.println(""); + + /* Draw a single pixel at the calibrated point */ + tft.fillCircle(calibrated.x, calibrated.y, 3, RA8875_BLACK); +} +