Dieser Algorithmus liefert eine nahezu pixelgenaue Positionsangabe für ein Touch-LCD zurück. Nötig wurde dieser Vorgang, da bei einem LCD mit Touch-Screen die X/Y-Koordinaten bzw. deren AD-Werte nicht unabhängig voneinander waren. So änderte sich bei einer Bewegung parallel zur X-Achse auch der Y-Wert. Mit diesem Algorithmus können die X/Y-Koordinaten nun sozusagen "entkoppelt" werden. Zusätzlich ist durch die Angabe der Displayabmessungen eine Umrechnung auf eine pixelgenaue Position inklusive.
"Ausrutscher" mit berechneten Werten ausserhalb der Displaygrösse (Wie sie z.B. bei zu leichtem Druck auf resistive Displays auftreten) können als "invalid" (ungültig) markiert und ignoriert werden.
Thx an mare_crisium vom Roboternetz, der 2008 die mathematische Grundlage dieser Problemlösung ausgegraben hat.
Um mit dem Algorithmus arbeiten zu können, muss zuerst eine Kalibrierung durchgeführt werden; d.h. man braucht von den 4 Eckpunkten des Displays die ADC-Werte. Wie man zu diesen kommt, bleibt jedem selbst überlassen. Ich hab die ausgelesenen Werte im EEPROM abgelegt und lade sie von dort.
Danach wird die Funktion InitAlgorithm aufgerufen, die die für die Iteration konstant bleibenden Werte berechnet.
Dies muss nur 1x getan werden.
Mit GetPositionPrecise erhält man dann die korrigierten Werte.
Die zusätzlichen Zeilen mit delay(5); waren bei der damaligen Hardware (ATmega128) aus noch ungeklärten Gründen nötig. Ohne diese kurzen Pausen kam es zu einer Art Pipeline-Effekten o.ä., die dafür sorgten, dass die (Zwischen-)Ergebnisse falsch waren. Wenn die Pausen für die jeweilige Hardware nicht nötig sind, kann das Define delay(us) einfach als leeres Define angelegt werden:
#define delay(us)
Aktuell sind diese Funktionen nur für LCDs bis max. 255x255 Pixel geeignet. Ein Umbau auf grössere Displays kann aber durch einfaches Vergrössern der 8-Bit-Typen auf 16 Bit bewerkstelligt werden.
// constant defines #define R0 0 #define R1 1 #define R2 2 #define R3 3 #define X 0 #define Y 1 #define POSITION_INVALID 0 #define POSITION_VALID 1 // configuration defines #define DETERMINANT_MULTIPLICATION //#define DETERMINANT_DIVISION #define CHECK_LCD_DIMENSIONS TRUE #define LCD_WIDTH 128 // Display width in pixels #define LCD_HEIGHT 64 // Display height in pixels #define ITERATIONS 5 // Iteration steps for determining the exact position // global variables int16_t R_Calibrated[4][2]; // holds coordinates of all 4 corners, measured by ADC value. int16_t r1_r0[2]; // solution vector for R1 - R0 int16_t r3_r0[2]; // solution vector for R3 - R0 int16_t r01_r32[2]; // solution vector for (r0 - r1) - (r3 - r2) int16_t raw_r0[2]; // solution vector for rraw - r0 int32_t D; // determinant double W = 0; // Iteration parameter; Start value = 0 double factor[2]; // solution vector factors double solVec[2]; // soluton vector double D_XY[2]; // determinant D_X, D_Y #ifdef DETERMINANT_MULTIPLICATION double D_inv; // 1 / determinant; #endif #define delay(us) _delay4Cycles(((F_CPU / 4000)*us)/1000) static inline void _delay4Cycles(uint16_t __count) { if (__count == 0) { __asm__ __volatile__("rjmp 1f\n 1:"); } else { __asm__ __volatile__ ( "1: sbiw %0,1" "\n\t" "brne 1b" : "=w" (__count) : "0" (__count) ); } } void InitAlgorithm(void) { // The used delay-command is needed to minimize pipeline-effects // and therefore to prevent calculation errors. // Constant values, needed for calculation; constant for an iteration: // Vector r1-r0 r1_r0[X] = R_Calibrated[R1][X] - R_Calibrated[R0][X]; delay(5); r1_r0[Y] = R_Calibrated[R1][Y] - R_Calibrated[R0][Y]; delay(5); // Vector r3-r0 r3_r0[X] = R_Calibrated[R3][X] - R_Calibrated[R0][X]; delay(5); r3_r0[Y] = R_Calibrated[R3][Y] - R_Calibrated[R0][Y]; delay(5); // Determinant D = ((int32_t) r1_r0[X] * (int32_t) r3_r0[Y]) - ((int32_t) r1_r0[Y] * (int32_t) r3_r0[X]); delay(5); #ifdef DETERMINANT_MULTIPLICATION D_inv = (double) 1 / (double) D; delay(5); #endif // Result vector (r0 - r1) - (r3 - r2) r01_r32[X] = (R_Calibrated[R0][X] - R_Calibrated[R1][X]) - (R_Calibrated[R3][X] - R_Calibrated[R2][X]); delay(5); r01_r32[Y] = (R_Calibrated[R0][Y] - R_Calibrated[R1][Y]) - (R_Calibrated[R3][Y] - R_Calibrated[R2][Y]); delay(5); } uint8_t GetPositionPrecise(uint8_t *xp, uint8_t *yp) { uint8_t step; uint8_t retval; int16_t posRaw[2]; // Get here your touch position / ADC values: // posRaw[X] = ... // posRaw[Y] = ... // or F(&posRaw[X], &posRaw[Y] etc... // for Simulation purposes we use this values: posRaw[X] = 400; posRaw[Y] = 500; // Vector posRaw - r0 raw_r0[X] = posRaw[X] - R_Calibrated[R0][X]; raw_r0[Y] = posRaw[Y] - R_Calibrated[R0][Y]; // Iteration loop for (step = 0 ; step < ITERATIONS ; step++) { // Solution vector solVec[X] = (double) raw_r0[X] - (W * (double) r01_r32[X]); solVec[Y] = (double) raw_r0[Y] - (W * (double) r01_r32[Y]); // Determinant D_XY[X] = -(((double) r3_r0[X] * solVec[Y]) - ((double) r3_r0[Y] * solVec[X])); D_XY[Y] = ((double) r1_r0[X] * solVec[Y]) - ((double) r1_r0[Y] * solVec[X]); // factor #ifdef DETERMINANT_DIVISION factor[X] = (double) D_XY[X] / (double) D; factor[Y] = (double) D_XY[Y] / (double) D; #endif #ifdef DETERMINANT_MULTIPLICATION factor[X] = (double) D_XY[X] * (double) D_inv; factor[Y] = (double) D_XY[Y] * (double) D_inv; #endif // New iteration parameter: W = factor[X] * factor[Y]; } *xp = (uint8_t) (factor[X] * ((double) (LCD_WIDTH - 1))); *yp = (uint8_t) (factor[Y] * ((double) (LCD_HEIGHT - 1))); #if (CHECK_LCD_DIMENSIONS == TRUE) // check if values are valid if ((*xp > (LCD_WIDTH - 1)) || (*yp > (LCD_HEIGHT - 1))) { // values invalid: return 0 retval = POSITION_INVALID; } else { // values valid: return 1 retval = POSITION_VALID; } #else retval = POSITION_VALID; #endif return(retval); }
int main(void) { uint8_t x; uint8_t y; // Set calibration values; may also be read from EEPROM or calibration procedure R_Calibrated[R0][X] = 101; R_Calibrated[R0][Y] = 145; R_Calibrated[R1][X] = 867; R_Calibrated[R1][Y] = 150; R_Calibrated[R2][X] = 848; R_Calibrated[R2][Y] = 802; R_Calibrated[R3][X] = 102; R_Calibrated[R3][Y] = 759; InitAlgorithm(); uint8_t result = GetPositionPrecise(&x, &y); if (result == POSITION_VALID) // or simply if (result) { // do something with x, y } return 0; }