/* ---------------------------------------------------------------------------- | |
* ATMEL Microcontroller Software Support | |
* ---------------------------------------------------------------------------- | |
* Copyright (c) 2011, Atmel Corporation | |
* | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* | |
* - Redistributions of source code must retain the above copyright notice, | |
* this list of conditions and the disclaimer below. | |
* | |
* Atmel's name may not be used to endorse or promote products derived from | |
* this software without specific prior written permission. | |
* | |
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR | |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE | |
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, | |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, | |
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | |
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
* ---------------------------------------------------------------------------- | |
*/ | |
/** | |
* \file | |
* | |
* Implementation of touchscreen driver device irrelevance code. | |
*/ | |
/*---------------------------------------------------------------------------- | |
* Headers | |
*----------------------------------------------------------------------------*/ | |
#include "board.h" | |
#include <assert.h> | |
#include <stdint.h> | |
#include <string.h> | |
/*---------------------------------------------------------------------------- | |
* Definitions | |
*----------------------------------------------------------------------------*/ | |
/** Size in pixels of calibration points. */ | |
#define POINTS_SIZE 4 | |
/** Maximum difference in pixels between the test point and the measured point. */ | |
#define POINTS_MAX_ERROR 8 | |
/** Delay at the end of calibartion for result display */ | |
#define DELAY_RESULT_DISPLAY 8000000 | |
/*---------------------------------------------------------------------------- | |
* Types | |
*----------------------------------------------------------------------------*/ | |
/** \brief Point used during the touchscreen calibration process. */ | |
typedef struct _CalibrationPoint | |
{ | |
/** Coordinate of point along the X-axis of the screen. */ | |
uint32_t x; | |
/** Coordinate of point along the Y-axis of the screen. */ | |
uint32_t y; | |
/** Calibration data of point. */ | |
uint32_t data[2]; | |
} CalibrationPoint; | |
/*---------------------------------------------------------------------------- | |
* Variables | |
*----------------------------------------------------------------------------*/ | |
/** indicates if the touch screen has been calibrated. */ | |
/** If not, Callback functions are not called. */ | |
static volatile uint8_t bCalibrationOk = 0; | |
/** Slope for interpoling touchscreen measurements along the X-axis. */ | |
static int32_t xSlope; | |
/** Slope for interpoling touchscreen measurements along the Y-axis. */ | |
static int32_t ySlope; | |
/** Calibration points. */ | |
static CalibrationPoint calibrationPoints[] = | |
{ | |
/* Top-left corner calibration point */ | |
{ | |
BOARD_LCD_WIDTH / 10, | |
BOARD_LCD_HEIGHT / 10, | |
{0, 0} | |
}, | |
/* Top-right corner calibration point */ | |
{ | |
BOARD_LCD_WIDTH - BOARD_LCD_WIDTH / 10, | |
BOARD_LCD_HEIGHT / 10, | |
{0, 0} | |
}, | |
/* Bottom-right corner calibration point */ | |
{ | |
BOARD_LCD_WIDTH - BOARD_LCD_WIDTH / 10, | |
BOARD_LCD_HEIGHT - BOARD_LCD_HEIGHT / 10, | |
{0, 0} | |
}, | |
/* Bottom-left corner calibration point */ | |
{ | |
BOARD_LCD_WIDTH / 10, | |
BOARD_LCD_HEIGHT - BOARD_LCD_HEIGHT / 10, | |
{0, 0} | |
} | |
}; | |
/** Test point */ | |
static const CalibrationPoint testPoint = | |
{ | |
BOARD_LCD_WIDTH / 2, | |
BOARD_LCD_HEIGHT / 2, | |
{0, 0} | |
}; | |
/*---------------------------------------------------------------------------- | |
* Local functions | |
*----------------------------------------------------------------------------*/ | |
/** | |
* \brief Display a calibration point on the given buffer. | |
* | |
* \param pPoint Calibration point to display. | |
*/ | |
static void DrawCalibrationPoint(const CalibrationPoint *pPoint) | |
{ | |
LCD_SetColor( COLOR_RED ) ; | |
LCD_DrawFilledRectangle( pPoint->x - POINTS_SIZE / 2, | |
pPoint->y - POINTS_SIZE / 2, | |
pPoint->x + POINTS_SIZE / 2, | |
pPoint->y + POINTS_SIZE / 2 ) ; | |
} | |
/** | |
* \brief Clears a calibration point from the given buffer. | |
* | |
* \param pPoint Calibration point to clear. | |
*/ | |
static void ClearCalibrationPoint(const CalibrationPoint *pPoint) | |
{ | |
LCD_SetColor( COLOR_WHITE ) ; | |
LCD_DrawFilledRectangle( pPoint->x - POINTS_SIZE / 2, | |
pPoint->y - POINTS_SIZE / 2, | |
pPoint->x + POINTS_SIZE / 2, | |
pPoint->y + POINTS_SIZE / 2 ) ; | |
} | |
/*---------------------------------------------------------------------------- | |
* Exported functions | |
*----------------------------------------------------------------------------*/ | |
/** | |
* \brief Indicates if the calibration of the touch screen is Ok. | |
* | |
* \return 1 calibration Ok, 0 if not. | |
*/ | |
uint8_t TSDCom_IsCalibrationOk(void) | |
{ | |
if (bCalibrationOk == 1) | |
{ | |
return 1; | |
} else | |
{ | |
return 0; | |
} | |
} | |
/** | |
* \brief Interpolates the provided raw measurements using the previously calculated | |
* slope. The resulting x and y coordinates are stored in an array. | |
* | |
* \param pData Raw measurement data, as returned by TSD_GetRawMeasurement(). | |
* \param pPoint Array in which x and y will be stored. | |
*/ | |
void TSDCom_InterpolateMeasurement(const uint32_t *pData, uint32_t *pPoint) | |
{ | |
pPoint[0] = calibrationPoints[0].x | |
- (((int32_t) calibrationPoints[0].data[0] - (int32_t) pData[0]) * 1024) | |
/ xSlope; | |
pPoint[1] = calibrationPoints[0].y | |
- (((int32_t) calibrationPoints[0].data[1] - (int32_t) pData[1]) * 1024) | |
/ ySlope; | |
if(pPoint[0] & 0x80000000) /* Is pPoint[0] negative ? */ | |
{ | |
pPoint[0] = 0; | |
} | |
if(pPoint[0] > BOARD_LCD_WIDTH) /* Is pPoint[0] bigger than the LCD width ? */ | |
{ | |
pPoint[0] = BOARD_LCD_WIDTH; | |
} | |
if(pPoint[1] & 0x80000000) /* Is pPoint[1] negative ? */ | |
{ | |
pPoint[1] = 0; | |
} | |
if(pPoint[1] > BOARD_LCD_HEIGHT) /* Is pPoint[1] bigger than the LCD width ? */ | |
{ | |
pPoint[1] = BOARD_LCD_HEIGHT; | |
} | |
} | |
/** | |
* \brief Performs the calibration process using the provided buffer to display | |
* information. | |
* | |
* \return True if calibration was successful; otherwise false. | |
*/ | |
uint8_t TSDCom_Calibrate(void) | |
{ | |
volatile uint32_t i; /* to keep the tempo with gcc code optimisation */ | |
int32_t slope1, slope2; | |
CalibrationPoint measuredPoint; | |
uint8_t xOk, yOk; | |
int32_t xDiff, yDiff; | |
/* Calibration setup */ | |
LCD_SetColor(COLOR_WHITE); | |
LCD_DrawFilledRectangle(0, 0, BOARD_LCD_WIDTH-1, BOARD_LCD_HEIGHT-1); | |
LCD_SetColor(COLOR_BLACK); | |
LCD_DrawString(70, 80, (uint8_t *)"LCD calibration"); | |
LCD_DrawString(40, 160, (uint8_t *)"Touch the dots to\ncalibrate the screen"); | |
/* Calibration points */ | |
for (i=0; i < 4; i++) | |
{ | |
DrawCalibrationPoint(&calibrationPoints[i]); | |
/* Wait for touch & end of conversion */ | |
TSD_WaitPenPressed(); | |
TSD_GetRawMeasurement(calibrationPoints[i].data); | |
ClearCalibrationPoint(&calibrationPoints[i]); | |
/* Wait for contact loss */ | |
TSD_WaitPenReleased(); | |
} | |
/** | |
* Calculate slopes using the calibration data | |
* Theory behind those calculations: | |
* - We suppose the touchscreen measurements are linear, so the following equations are true (simple | |
* linear regression) for any two 'a' and 'b' points of the screen: | |
* dx = (a.data[0] - b.data[0]) / (a.x - b.x) | |
* dy = (a.data[1] - b.data[1]) / (a.y - b.y) | |
* | |
* - We calculate dx and dy (called xslope and yslope here) using the calibration points. | |
* | |
* - We can then use dx and dy to infer the position of a point 'p' given the measurements performed | |
* by the touchscreen ('c' is any of the calibration points): | |
* dx = (p.data[0] - c.data[0]) / (p.x - c.x) | |
* dy = (p.data[1] - c.data[1]) / (p.y - c.y) | |
* Thus: | |
* p.x = c.x - (p.data[0] - c.data[0]) / dx | |
* p.y = c.y - (p.data[1] - c.data[1]) / dy | |
* | |
* - Since there are four calibration points, dx and dy can be calculated twice, so we average | |
* the two values. | |
*/ | |
slope1 = ((int32_t) calibrationPoints[0].data[0]) - ((int32_t) calibrationPoints[1].data[0]); | |
slope1 *= 1024; | |
slope1 /= ((int32_t) calibrationPoints[0].x) - ((int32_t) calibrationPoints[1].x); | |
slope2 = ((int32_t) calibrationPoints[2].data[0]) - ((int32_t) calibrationPoints[3].data[0]); | |
slope2 *= 1024; | |
slope2 /= ((int32_t) calibrationPoints[2].x) - ((int32_t) calibrationPoints[3].x); | |
xSlope = (slope1 + slope2) / 2; | |
slope1 = ((int32_t) calibrationPoints[0].data[1]) - ((int32_t) calibrationPoints[2].data[1]); | |
slope1 *= 1024; | |
slope1 /= ((int32_t) calibrationPoints[0].y) - ((int32_t) calibrationPoints[2].y); | |
slope2 = ((int32_t) calibrationPoints[1].data[1]) - ((int32_t) calibrationPoints[3].data[1]); | |
slope2 *= 1024; | |
slope2 /= ((int32_t) calibrationPoints[1].y) - ((int32_t) calibrationPoints[3].y); | |
ySlope = (slope1 + slope2) / 2; | |
/* Test point */ | |
LCD_SetColor(COLOR_WHITE); | |
LCD_DrawFilledRectangle(0, 0, BOARD_LCD_WIDTH-1, BOARD_LCD_HEIGHT-1); | |
LCD_SetColor(COLOR_BLACK); | |
LCD_DrawString(70, 80, (uint8_t *)"LCD calibration"); | |
LCD_DrawString(40, 160, (uint8_t *)" Touch the point to\nvalidate calibration"); | |
DrawCalibrationPoint(&testPoint); | |
/* Wait for touch & end of conversion */ | |
TSD_WaitPenPressed(); | |
TSD_GetRawMeasurement(measuredPoint.data); | |
TSDCom_InterpolateMeasurement(measuredPoint.data, (uint32_t *) &measuredPoint); | |
DrawCalibrationPoint(&measuredPoint); | |
/* Check resulting x and y */ | |
xDiff = (int32_t) measuredPoint.x - (int32_t) testPoint.x; | |
yDiff = (int32_t) measuredPoint.y - (int32_t) testPoint.y; | |
xOk = (xDiff >= -POINTS_MAX_ERROR) && (xDiff <= POINTS_MAX_ERROR); | |
yOk = (yDiff >= -POINTS_MAX_ERROR) && (yDiff <= POINTS_MAX_ERROR); | |
/* Wait for contact loss */ | |
TSD_WaitPenReleased(); | |
/* Check calibration result */ | |
if (xOk && yOk) | |
{ | |
bCalibrationOk = 1; | |
LCD_SetColor(COLOR_WHITE); | |
LCD_DrawFilledRectangle(0, 0, BOARD_LCD_WIDTH-1, BOARD_LCD_HEIGHT-1); | |
LCD_SetColor(COLOR_BLACK); | |
LCD_DrawString(70, 80, (uint8_t *)"LCD calibration"); | |
LCD_SetColor(COLOR_GREEN); | |
LCD_DrawString(100, 160, (uint8_t *)"Success !"); | |
} | |
else | |
{ | |
bCalibrationOk = 0; | |
LCD_SetColor(COLOR_WHITE); | |
LCD_DrawFilledRectangle(0, 0, BOARD_LCD_WIDTH-1, BOARD_LCD_HEIGHT-1); | |
LCD_SetColor(COLOR_BLACK); | |
LCD_DrawString(70, 80, (uint8_t *)"LCD calibration"); | |
LCD_SetColor(COLOR_RED); | |
LCD_DrawString(80, 160, (uint8_t *)"Error too big"); | |
} | |
/* Slight delay */ | |
for (i = 0; i < DELAY_RESULT_DISPLAY; i++); | |
return (xOk && yOk); | |
} | |
/** | |
* \brief Read calibrate data to buffer. | |
* | |
* \param pBuffer Data buffer. | |
* \param dwSize Size of data buffer in bytes. | |
*/ | |
void TSDCom_ReadCalibrateData(void *pBuffer, uint32_t dwSize) | |
{ | |
uint8_t *pDest = (uint8_t *)pBuffer; | |
if ((sizeof(bCalibrationOk) + sizeof(xSlope) + sizeof(ySlope) + \ | |
sizeof(calibrationPoints[0].data)) > dwSize) | |
{ | |
TRACE_ERROR ("Size is too large !"); | |
return; | |
} | |
memcpy(pDest, (void const *)&bCalibrationOk, sizeof(bCalibrationOk)); | |
pDest += sizeof(bCalibrationOk); | |
memcpy(pDest, &xSlope, sizeof(xSlope)); | |
pDest += sizeof(xSlope); | |
memcpy(pDest, &ySlope, sizeof(ySlope)); | |
pDest += sizeof(ySlope); | |
memcpy(pDest, &calibrationPoints[0].data, sizeof(calibrationPoints[0].data)); | |
pDest += sizeof(calibrationPoints[0].data); | |
} | |
/** | |
* \brief Restore calibrate data with buffer data. | |
* | |
* \param pBuffer Data buffer. | |
* \param dwSize Size of data buffer in bytes. | |
*/ | |
void TSDCom_RestoreCalibrateData(void *pBuffer, uint32_t dwSize) | |
{ | |
uint8_t *pSrc = (uint8_t *)pBuffer; | |
if ((sizeof(bCalibrationOk) + sizeof(xSlope) + sizeof(ySlope) + \ | |
sizeof(calibrationPoints[0].data)) > dwSize) | |
{ | |
TRACE_ERROR ("Size is too large !"); | |
return; | |
} | |
memcpy((void *)&bCalibrationOk, pSrc, sizeof(bCalibrationOk)); | |
pSrc += sizeof(bCalibrationOk); | |
memcpy(&xSlope, pSrc, sizeof(xSlope)); | |
pSrc += sizeof(xSlope); | |
memcpy(&ySlope, pSrc, sizeof(ySlope)); | |
pSrc += sizeof(ySlope); | |
memcpy(&calibrationPoints[0].data, pSrc, sizeof(calibrationPoints[0].data)); | |
pSrc += sizeof(calibrationPoints[0].data); | |
} | |