Merge pull request #13 from abhishek-kakkar/master

Touchscreen 3 point calibration support
ugfx_release_2.6
Tectu 2012-11-08 14:35:30 -08:00
commit 3dd0b1a5af
2 changed files with 387 additions and 273 deletions

View File

@ -49,6 +49,9 @@
/* Include the low level driver information */ /* Include the low level driver information */
#include "touchpad_lld.h" #include "touchpad_lld.h"
/* For definitions of coord_t, we require gdisp.h */
#include "gdisp.h"
/*===========================================================================*/ /*===========================================================================*/
/* Type definitions */ /* Type definitions */
/*===========================================================================*/ /*===========================================================================*/
@ -57,10 +60,12 @@
* @brief Struct used for calibration * @brief Struct used for calibration
*/ */
typedef struct cal_t { typedef struct cal_t {
float xm; float ax;
float ym; float bx;
float xn; float cx;
float yn; float ay;
float by;
float cy;
} cal_t; } cal_t;
/*===========================================================================*/ /*===========================================================================*/
@ -72,12 +77,12 @@ extern "C" {
#endif #endif
void tpInit(const TOUCHPADDriver *tp); void tpInit(const TOUCHPADDriver *tp);
uint16_t tpReadX(void); coord_t tpReadX(void);
uint16_t tpReadY(void); coord_t tpReadY(void);
void tpCalibrate(void); void tpCalibrate(void);
#if TOUCHPAD_HAS_IRQ #if TOUCHPAD_HAS_IRQ
uint8_t tpIRQ(void); bool_t tpIRQ(void);
#endif #endif
#if TOUCHPAD_HAS_PRESSURE #if TOUCHPAD_HAS_PRESSURE

View File

@ -1,266 +1,375 @@
/* ChibiOS/GFX - Copyright (C) 2012 /* ChibiOS/GFX - Copyright (C) 2012
Joel Bodenmann aka Tectu <joel@unormal.org> Joel Bodenmann aka Tectu <joel@unormal.org>
This file is part of ChibiOS/GFX. This file is part of ChibiOS/GFX.
ChibiOS/GFX is free software; you can redistribute it and/or modify ChibiOS/GFX is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or the Free Software Foundation; either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
ChibiOS/GFX is distributed in the hope that it will be useful, ChibiOS/GFX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @file src/touchpad.c * @file src/touchpad.c
* @brief Touchpad Driver code. * @brief Touchpad Driver code.
* *
* @addtogroup TOUCHPAD * @addtogroup TOUCHPAD
* @{ * @{
*/ */
#include "ch.h" #include "ch.h"
#include "hal.h" #include "hal.h"
#include "gdisp.h" #include "gdisp.h"
#include "touchpad.h" #include "touchpad.h"
#if GFX_USE_TOUCHPAD || defined(__DOXYGEN__) #if GFX_USE_TOUCHPAD || defined(__DOXYGEN__)
#if TOUCHPAD_STORE_CALIBRATION #if TOUCHPAD_STORE_CALIBRATION
extern void tp_store_calibration_lld(struct cal_t *cal); extern void tp_store_calibration_lld(struct cal_t *cal);
extern struct cal_t *tp_restore_calibration_lld(void); extern struct cal_t *tp_restore_calibration_lld(void);
#endif #endif
/*===========================================================================*/ /*===========================================================================*/
/* Driver local definitions. */ /* Driver local definitions. */
/*===========================================================================*/ /*===========================================================================*/
/*===========================================================================*/ /*===========================================================================*/
/* Driver exported variables. */ /* Driver exported variables. */
/*===========================================================================*/ /*===========================================================================*/
/*===========================================================================*/ /*===========================================================================*/
/* Driver local variables. */ /* Driver local variables. */
/*===========================================================================*/ /*===========================================================================*/
static struct cal_t *cal; static struct cal_t *cal;
/*===========================================================================*/ /*===========================================================================*/
/* Driver local functions. */ /* Driver local functions. */
/*===========================================================================*/ /*===========================================================================*/
static uint16_t _tpReadRealX(void) { static coord_t _tpReadRealX(void) {
uint32_t results = 0; int32_t results = 0;
uint16_t i, x; int16_t i;
coord_t x;
/* Median filtering is already done in LLD */
for(i = 0; i < CONVERSIONS; i++) { for(i = 0; i < CONVERSIONS; i++) {
results += tp_lld_read_x(); results += tp_lld_read_x();
} }
/* Take the average of the readings */ /* Take the average of the readings */
x = results / CONVERSIONS; x = results / CONVERSIONS;
return x; return x;
} }
static uint16_t _tpReadRealY(void) { static coord_t _tpReadRealY(void) {
uint32_t results = 0; int32_t results = 0;
uint16_t i, y; int16_t i;
coord_t y;
/* Median filtering is already done in LLD */
for(i = 0; i < CONVERSIONS; i++) { for(i = 0; i < CONVERSIONS; i++) {
results += tp_lld_read_y(); results += tp_lld_read_y();
} }
/* Take the average of the readings */ /* Take the average of the readings */
y = results / CONVERSIONS; y = results / CONVERSIONS;
return y; return y;
} }
static void _tpDrawCross(uint16_t x, uint16_t y) { static void _tpDrawCross(uint16_t x, uint16_t y) {
gdispDrawLine(x-15, y, x-2, y, 0xffff); gdispDrawLine(x-15, y, x-2, y, 0xffff);
gdispDrawLine(x+2, y, x+15, y, 0xffff); gdispDrawLine(x+2, y, x+15, y, 0xffff);
gdispDrawLine(x, y-15, x, y-2, 0xffff); gdispDrawLine(x, y-15, x, y-2, 0xffff);
gdispDrawLine(x, y+2, x, y+15, 0xffff); gdispDrawLine(x, y+2, x, y+15, 0xffff);
gdispDrawLine(x-15, y+15, x-7, y+15, RGB565CONVERT(184,158,131)); gdispDrawLine(x-15, y+15, x-7, y+15, RGB565CONVERT(184,158,131));
gdispDrawLine(x-15, y+7, x-15, y+15, RGB565CONVERT(184,158,131)); gdispDrawLine(x-15, y+7, x-15, y+15, RGB565CONVERT(184,158,131));
gdispDrawLine(x-15, y-15, x-7, y-15, RGB565CONVERT(184,158,131)); gdispDrawLine(x-15, y-15, x-7, y-15, RGB565CONVERT(184,158,131));
gdispDrawLine(x-15, y-7, x-15, y-15, RGB565CONVERT(184,158,131)); gdispDrawLine(x-15, y-7, x-15, y-15, RGB565CONVERT(184,158,131));
gdispDrawLine(x+7, y+15, x+15, y+15, RGB565CONVERT(184,158,131)); gdispDrawLine(x+7, y+15, x+15, y+15, RGB565CONVERT(184,158,131));
gdispDrawLine(x+15, y+7, x+15, y+15, RGB565CONVERT(184,158,131)); gdispDrawLine(x+15, y+7, x+15, y+15, RGB565CONVERT(184,158,131));
gdispDrawLine(x+7, y-15, x+15, y-15, RGB565CONVERT(184,158,131)); gdispDrawLine(x+7, y-15, x+15, y-15, RGB565CONVERT(184,158,131));
gdispDrawLine(x+15, y-15, x+15, y-7, RGB565CONVERT(184,158,131)); gdispDrawLine(x+15, y-15, x+15, y-7, RGB565CONVERT(184,158,131));
} }
/*===========================================================================*/ static void _tpTransform(coord_t *x, coord_t *y) {
/* Driver exported functions. */ *x = (coord_t) (cal->ax * (*x) + cal->bx * (*y) + cal->cx);
/*===========================================================================*/ *y = (coord_t) (cal->ay * (*x) + cal->by * (*y) + cal->cy);
}
/**
* @brief Touchpad Driver initialization. static void _tpDo3PointCalibration(const coord_t (*cross)[2], coord_t (*points)[2],
* @note This function is NOT currently implicitly invoked by @p halInit(). cal_t *c)
* It must be called manually. {
* float dx, dx0, dx1, dx2, dy0, dy1, dy2;
* @param[in] tp The touchpad driver struct
* /* Compute all the required determinants */
* @api dx = ((float)(points[0][0] - points[2][0])) * ((float)(points[1][1] - points[2][1]))
*/ - ((float)(points[1][0] - points[2][0])) * ((float)(points[0][1] - points[2][1]));
void tpInit(const TOUCHPADDriver *tp) {
cal = (struct cal_t*)chHeapAlloc(NULL, sizeof(struct cal_t)); dx0 = ((float)(cross[0][0] - cross[2][0])) * ((float)(points[1][1] - points[2][1]))
if(cal == NULL) - ((float)(cross[1][0] - cross[2][0])) * ((float)(points[0][1] - points[2][1]));
return;
dx1 = ((float)(points[0][0] - points[2][0])) * ((float)(cross[1][0] - cross[2][0]))
/* Initialise Mutex */ - ((float)(points[1][0] - points[2][0])) * ((float)(cross[0][0] - cross[2][0]));
//MUTEX_INIT
dx2 = cross[0][0] * ((float)points[1][0] * (float)points[2][1] - (float)points[2][0] * (float)points[1][1]) -
/* Initialise driver */ cross[1][0] * ((float)points[0][0] * (float)points[2][1] - (float)points[2][0] * (float)points[0][1]) +
//MUTEX_ENTER cross[2][0] * ((float)points[0][0] * (float)points[1][1] - (float)points[1][0] * (float)points[0][1]);
tp_lld_init(tp);
//MUTEX_EXIT dy0 = ((float)(cross[0][1] - cross[2][1])) * ((float)(points[1][1] - points[2][1]))
- ((float)(cross[1][1] - cross[2][1])) * ((float)(points[0][1] - points[2][1]));
#if TOUCHPAD_STORE_CALIBRATION
cal = tp_restore_calibration_lld(); dy1 = ((float)(points[0][0] - points[2][0])) * ((float)(cross[1][1] - cross[2][1]))
if(cal == NULL) { - ((float)(points[1][0] - points[2][0])) * ((float)(cross[0][1] - cross[2][1]));
cal = (struct cal_t*)chHeapAlloc(NULL, sizeof(struct cal_t));
tpCalibrate(); dy2 = cross[0][1] * ((float)points[1][0] * (float)points[2][1] - (float)points[2][0] * (float)points[1][1]) -
} cross[1][1] * ((float)points[0][0] * (float)points[2][1] - (float)points[2][0] * (float)points[0][1]) +
#endif cross[2][1] * ((float)points[0][0] * (float)points[1][1] - (float)points[1][0] * (float)points[0][1]);
}
/* Now, calculate all the required coefficients */
/** c->ax = dx0 / dx;
* @brief Get the X-Coordinate, relative to screen zero point. c->bx = dx1 / dx;
* c->cx = dx2 / dx;
* @return The X position in pixels.
* c->ay = dy0 / dx;
* @api c->by = dy1 / dx;
*/ c->cy = dy2 / dx;
uint16_t tpReadX(void) { }
uint16_t x, y;
/*===========================================================================*/
#if TOUCHPAD_XY_INVERTED == TRUE /* Driver exported functions. */
x = cal->xm * _tpReadRealY() + cal->xn; /*===========================================================================*/
y = cal->ym * _tpReadRealX() + cal->yn;
#else /**
x = cal->xm * _tpReadRealX() + cal->xn; * @brief Touchpad Driver initialization.
y = cal->ym * _tpReadRealY() + cal->yn; * @note This function is NOT currently implicitly invoked by @p halInit().
#endif * It must be called manually.
*
switch(gdispGetOrientation()) { * @param[in] tp The touchpad driver struct
case GDISP_ROTATE_0: *
return x; * @api
case GDISP_ROTATE_90: */
return y; void tpInit(const TOUCHPADDriver *tp) {
case GDISP_ROTATE_180: cal = (struct cal_t*)chHeapAlloc(NULL, sizeof(struct cal_t));
return GDISP_SCREEN_WIDTH - x - 1; if(cal == NULL)
case GDISP_ROTATE_270: return;
return GDISP_SCREEN_HEIGHT - y - 1;
} /* Initialise Mutex */
//MUTEX_INIT
return 0;
} /* Initialise driver */
//MUTEX_ENTER
/** tp_lld_init(tp);
* @brief Get the X-Coordinate, relative to screen zero point. //MUTEX_EXIT
*
* @return The Y position in pixels. #if TOUCHPAD_STORE_CALIBRATION
* cal = tp_restore_calibration_lld();
* @api if(cal == NULL) {
*/ cal = (struct cal_t*)chHeapAlloc(NULL, sizeof(struct cal_t));
uint16_t tpReadY(void) { tpCalibrate();
uint16_t x, y; }
#endif
#if TOUCHPAD_XY_INVERTED == TRUE }
x = cal->xm * _tpReadRealY() + cal->xn;
y = cal->ym * _tpReadRealX() + cal->yn; /**
#else * @brief Get the X-Coordinate, relative to screen zero point.
x = cal->xm * _tpReadRealX() + cal->xn; *
y = cal->ym * _tpReadRealY() + cal->yn; * @return The X position in pixels.
#endif *
* @api
switch(gdispGetOrientation()) { */
case GDISP_ROTATE_0: coord_t tpReadX(void) {
return y; coord_t x, y;
case GDISP_ROTATE_90:
return GDISP_SCREEN_WIDTH - x - 1; #if TOUCHPAD_XY_INVERTED == TRUE
case GDISP_ROTATE_180: x = _tpReadRealY();
return GDISP_SCREEN_HEIGHT - y - 1; y = _tpReadRealX();
case GDISP_ROTATE_270: #else
return x; x = _tpReadRealX();
} y = _tpReadRealY();
#endif
return 0;
} _tpTransform(&x, &y);
/** switch(gdispGetOrientation()) {
* @brief Get the pressure. case GDISP_ROTATE_0:
* return x;
* @return The pressure. case GDISP_ROTATE_90:
* return y;
* @api case GDISP_ROTATE_180:
*/ return GDISP_SCREEN_WIDTH - x - 1;
#if TOUCHPAD_HAS_PRESSURE || defined(__DOXYGEN__) case GDISP_ROTATE_270:
uint16_t tpReadZ(void) { return GDISP_SCREEN_HEIGHT - y - 1;
/* ToDo */ }
return (tp_lld_read_z());
} return 0;
#endif }
/** /**
* @brief returns if touchpad is pressed or not * @brief Get the X-Coordinate, relative to screen zero point.
* *
* @return 1 if pressed, 0 otherwise * @return The Y position in pixels.
* *
* @api * @api
*/ */
#if TOUCHPAD_HAS_IRQ || defined(__DOXYGEN__) coord_t tpReadY(void) {
uint8_t tpIRQ(void) { coord_t x, y;
return tp_lld_irq();
} #if TOUCHPAD_XY_INVERTED == TRUE
#endif x = _tpReadRealY();
y = _tpReadRealX();
void tpCalibrate(void) { #else
const uint16_t h = gdispGetHeight(); x = _tpReadRealX();
const uint16_t w = gdispGetWidth(); y = _tpReadRealY();
const uint16_t cross[2][2] = {{(w/8), (h/8)}, {(w-(w/8)) , (h-(h/8))}}; #endif
uint16_t points[2][2];
uint8_t i; _tpTransform(&x, &y);
gdispSetOrientation(GDISP_ROTATE_0); switch(gdispGetOrientation()) {
gdispClear(Red); case GDISP_ROTATE_0:
gdispFillStringBox(0, 10, gdispGetWidth(), 30, "Calibration", &fontUI2Double, White, Red, justifyCenter); return y;
case GDISP_ROTATE_90:
for(i = 0; i < 2; i++) { return GDISP_SCREEN_WIDTH - x - 1;
_tpDrawCross(cross[i][0], cross[i][1]); case GDISP_ROTATE_180:
while(!tpIRQ()); return GDISP_SCREEN_HEIGHT - y - 1;
points[i][0] = _tpReadRealX(); case GDISP_ROTATE_270:
points[i][1] = _tpReadRealY(); return x;
chThdSleepMilliseconds(100); }
while(tpIRQ());
gdispFillArea(cross[i][0]-15, cross[i][1]-15, 42, 42, Red); return 0;
} }
cal->xm = ((float)cross[1][0] - (float)cross[0][0]) / ((float)points[1][0] - (float)points[0][0]); /**
cal->ym = ((float)cross[1][1] - (float)cross[0][1]) / ((float)points[1][1] - (float)points[0][1]); * @brief Get the pressure.
*
cal->xn = (float)cross[0][0] - cal->xm * (float)points[0][0]; * @return The pressure.
cal->yn = (float)cross[0][1] - cal->ym * (float)points[0][1]; *
* @api
#if TOUCHPAD_STORE_CALIBRATION */
tp_store_calibration_lld(cal); #if TOUCHPAD_HAS_PRESSURE || defined(__DOXYGEN__)
#endif uint16_t tpReadZ(void) {
} /* ToDo */
return (tp_lld_read_z());
#endif /* GFX_USE_TOUCHPAD */ }
/** @} */ #endif
/**
* @brief Returns if touchpad is pressed or not
*
* @return TRUE if pressed, FALSE otherwise
*
* @api
*/
#if TOUCHPAD_HAS_IRQ || defined(__DOXYGEN__)
bool_t tpIRQ(void) {
return tp_lld_irq();
}
#endif
/* Define maximum no. of times to sample the calibration point */
#define MAX_CAL_SAMPLES 10
/**
* @brief This function interactively performs calibration of the touchscreen
* using 3-point calibration algorithm. Optionally, it also verifies
* the accuracy of the calibration coefficients obtained if the symbol
* TOUCHPAD_VERIFY_CALIBRATION is defined in the configuration.
*
* @api
*/
void tpCalibrate(void) {
const uint16_t height = gdispGetHeight();
const uint16_t width = gdispGetWidth();
const coord_t cross[][2] = {{(width / 4), (height / 4)},
{(width - (width / 4)) , (height / 4)},
{(width - (width / 4)) , (height - (height / 4))},
{(width / 2), (height / 2)}}; /* Check point */
coord_t points[4][2];
int32_t px, py;
uint8_t i, j;
gdispSetOrientation(GDISP_ROTATE_0);
gdispClear(Blue);
gdispFillStringBox(0, 5, gdispGetWidth(), 30, "Calibration", &fontUI2Double, White, Blue, justifyCenter);
#if defined(TOUCHPAD_VERIFY_CALIBRATION)
calibrate:
for(i = 0; i < 4; i++) {
#else
for(i = 0; i < 3; i++) {
#endif
_tpDrawCross(cross[i][0], cross[i][1]);
while(!tpIRQ())
chThdSleepMilliseconds(2); /* Be nice to other threads*/
chThdSleepMilliseconds(20); /* Allow screen to settle */
/* Take a little more samples per point and their average
* for precise calibration */
px = py = 0;
j = 0;
while (j < MAX_CAL_SAMPLES) {
if (tpIRQ()) {
/* We have valid pointer data */
px += _tpReadRealX();
py += _tpReadRealY();
j++;
}
}
points[i][0] = px / j;
points[i][1] = py / j;
chThdSleepMilliseconds(100);
while(tpIRQ())
chThdSleepMilliseconds(2); /* Be nice to other threads*/
gdispFillArea(cross[i][0] - 15, cross[i][1] - 15, 42, 42, Blue);
}
/* Apply 3 point calibration algorithm */
_tpDo3PointCalibration(cross, points, cal);
#if defined(TOUCHPAD_VERIFY_CALIBRATION)
/* Verification of correctness of calibration (optional) :
* See if the 4th point (Middle of the screen) coincides with the calibrated
* result. If point is with +/- 2 pixel margin, then successful calibration
* Else, start from the beginning.
*/
/* Transform the co-ordinates */
_tpTransform(&points[3][0], &points[3][1]);
/* Calculate the delta */
px = (points[3][0] - cross[3][0]) * (points[3][0] - cross[3][0]) +
(points[3][1] - cross[3][1]) * (points[3][1] - cross[3][1]);
if (px > 4)
goto calibrate;
#endif
/* If enabled, serialize the calibration values for storage */
#if TOUCHPAD_STORE_CALIBRATION
tp_store_calibration_lld(cal);
#endif
}
#endif /* GFX_USE_TOUCHPAD */
/** @} */