touch.c

Go to the documentation of this file.
00001 /**************************************************************************/
00035 #include <stdlib.h>
00036 #include <string.h>
00037 #include "em_device.h"
00038 #include "em_adc.h"
00039 #include "em_gpio.h"
00040 #include "em_cmu.h"
00041 #include "touch.h"
00042 #include "calibrate.h"
00043 #include "bsp.h"
00044 #ifndef TOUCH_WITHOUT_STORE
00045 #include "i2cdrv.h"
00046 #include "eeprom.h"
00047 #endif
00048 
00050 #define CALIBRATION_MAGIC_NUMBER     0xCA71B4A7
00051 #define CALIBRATION_EEPROM_OFFSET    0x80
00052 
00054 /* ADC inputs connected to touchpad */
00055 #define ADC_X                     adcSingleInpCh1 
00056 #define ADC_Y                     adcSingleInpCh4 
00058 /* touchpad PIOs */
00059 #define TOUCH_X1                  gpioPortD, 5 
00060 #define TOUCH_X2                  gpioPortD, 4 
00061 #define TOUCH_Y1                  gpioPortD, 3 
00062 #define TOUCH_Y2                  gpioPortD, 1 
00065 typedef enum
00066 { TOUCH_INIT,
00067   TOUCH_CHECK_PRESS,
00068   TOUCH_MEASURE_X,
00069   TOUCH_MEASURE_Y } TOUCH_State_TypeDef;
00070 
00073 static volatile TOUCH_State_TypeDef touch_state = TOUCH_INIT;
00074 static ADC_InitSingle_TypeDef       sInit       = ADC_INITSINGLE_DEFAULT;
00075 volatile TOUCH_Pos_TypeDef          newpos;
00076 volatile TOUCH_Pos_TypeDef          current_pos;
00077 static TOUCH_Pos_TypeDef            pos;
00078 
00079 static void                         (*upcall)(TOUCH_Pos_TypeDef *) = 0;
00080 
00081 uint32_t                            touch_ignore_move;
00085 MATRIX calibrationMatrix = { 103800, 2048, -8184704, -384, 102144, -16424640, 287650 };
00086 
00088 #ifndef TOUCH_WITHOUT_STORE
00089 /**************************************************************************/
00105 static uint32_t touch_CountChecksum(uint32_t magic, uint32_t *data, uint32_t len)
00106 {
00107   unsigned long checksum = magic;
00108 
00109   while (len--)
00110   {
00111     if (checksum & 0x80000000)
00112     {
00113       checksum <<= 1;
00114       checksum  |= 1;
00115     }
00116     else checksum <<= 1;
00117     checksum += *data;
00118     data++;
00119   }
00120   return(checksum);
00121 }
00122 
00124 static void touch_LoadCalibration(void)
00125 {
00126   I2C_Init_TypeDef i2cInit = I2C_INIT_DEFAULT;
00127   uint32_t         temp, checksum;
00128   int              count;
00129   MATRIX           new_matrix;
00130   /* Initialize I2C driver, using standard rate. Devices on DK itself */
00131   /* supports fast mode, but in case some slower devices are added on */
00132   /* prototype board, we use standard mode. */
00133   I2CDRV_Init(&i2cInit);
00134   count  = EEPROM_Read(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET, (uint8_t*) &temp, sizeof(temp));
00135   count += EEPROM_Read(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET + 4, (uint8_t*) &new_matrix, sizeof(new_matrix));
00136   if (count == sizeof(new_matrix) + 4)
00137   {
00138     if (temp == CALIBRATION_MAGIC_NUMBER)
00139     {
00140       checksum = touch_CountChecksum(temp, (uint32_t*) &new_matrix, sizeof(new_matrix) / 4);
00141       count    = EEPROM_Read(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET + 4 + sizeof(new_matrix), (uint8_t*) &temp, sizeof(temp));
00142       if (temp == checksum)
00143       {                                      /* looks like calibration table is valid */
00144         ADC_IntDisable(ADC0, ADC_IF_SINGLE); /* we need to disable ADC interrupt to avoid current_pos structure update for a while */
00145         memcpy(&calibrationMatrix, &new_matrix, sizeof(calibrationMatrix));
00146         ADC_IntEnable(ADC0, ADC_IF_SINGLE);
00147       }
00148     }
00149   }
00150 }
00151 
00153 static void touch_StoreCalibration(void)
00154 {
00155   int      count;
00156   uint32_t temp = CALIBRATION_MAGIC_NUMBER, checksum;
00157   checksum = touch_CountChecksum(CALIBRATION_MAGIC_NUMBER, (uint32_t*) &calibrationMatrix, sizeof(calibrationMatrix) / 4);
00158   count    = EEPROM_Write(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET, (uint8_t*) &temp, sizeof(temp));
00159   count   += EEPROM_Write(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET + 4, (uint8_t*) &calibrationMatrix, sizeof(calibrationMatrix));
00160   count   += EEPROM_Write(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET + 4 + sizeof(calibrationMatrix), (uint8_t*) &checksum, sizeof(checksum));
00161 }
00162 #endif
00163 
00165 /***************************************************************************/
00172 void TOUCH_RecalculatePosition(volatile TOUCH_Pos_TypeDef *pos)
00173 {
00174   POINT old_pos, new_pos;
00175 
00176   if (pos->pen)
00177   {
00178     old_pos.x = pos->adcx;
00179     old_pos.y = pos->adcy;
00180     if (getDisplayPoint(&new_pos, &old_pos, &calibrationMatrix) == OK)
00181     {
00182       if (new_pos.x >= 0) pos->x = new_pos.x;
00183       else pos->x = 0;
00184       if (new_pos.y >= 0) pos->y = new_pos.y;
00185       else pos->y = 0;
00186     }
00187   }
00188   else
00189   {
00190     pos->x = 0;
00191     pos->y = 0;
00192   }
00193 }
00194 
00195 /***************************************************************************/
00199 void TOUCH_CallUpcall(void)
00200 {
00201   if (upcall)
00202     upcall((TOUCH_Pos_TypeDef*) &current_pos);
00203 }
00204 
00205 /***************************************************************************/
00212 int TOUCH_StateChanged(void)
00213 {
00214   int result = 0;
00215   int diff, a, b;
00216   if (newpos.pen && !current_pos.pen) result = 1; /* pen down */
00217   a    = current_pos.x;
00218   b    = newpos.x;
00219   diff = a - b;
00220   if (abs(diff) > (int) touch_ignore_move) result = 1; /* move in X axis */
00221   a    = current_pos.y;
00222   b    = newpos.y;
00223   diff = a - b;
00224   if (abs(diff) > (int) touch_ignore_move) result = 1; /* move in Y axis */
00225   return result;
00226 }
00227 
00228 /***************************************************************************/
00233 void ADC0_IRQHandler(void)
00234 {
00235   switch (touch_state)
00236   {
00237   case TOUCH_INIT:   /* enter this state if touch panel is not pressed */
00238     GPIO_PinModeSet(TOUCH_Y1, gpioModePushPull, 1);
00239     GPIO_PinModeSet(TOUCH_Y2, gpioModePushPull, 1);
00240     GPIO_PinModeSet(TOUCH_X1, gpioModeInputPullFilter , 0);
00241     GPIO_PinModeSet(TOUCH_X2, gpioModeInput, 0);
00242     sInit.input      = ADC_Y;
00243     sInit.reference  = adcRefVDD;
00244     sInit.resolution = adcResOVS;
00245     sInit.acqTime    = adcAcqTime128;               /* used to slow down */
00246     if(GPIO_PinInGet(TOUCH_X2))
00247     {
00248       touch_state = TOUCH_MEASURE_Y;
00249       GPIO_PinModeSet(TOUCH_X1, gpioModePushPull, 1);
00250       GPIO_PinModeSet(TOUCH_X2, gpioModePushPull, 0);
00251       GPIO_PinModeSet(TOUCH_Y1, gpioModeInput, 0);
00252       GPIO_PinModeSet(TOUCH_Y2, gpioModeInput, 0);
00253       sInit.input   = ADC_X;
00254       sInit.acqTime = adcAcqTime16;                  /* pressed, so speed-up */
00255     }
00256     ADC_InitSingle(ADC0, &sInit);
00257     break;
00258   case TOUCH_CHECK_PRESS:   /* checks if touch panel is still pressed */
00259     if( GPIO_PinInGet(TOUCH_X2) )
00260     {
00261       touch_state = TOUCH_MEASURE_Y;
00262       GPIO_PinModeSet(TOUCH_X1, gpioModePushPull, 1);
00263       GPIO_PinModeSet(TOUCH_X2, gpioModePushPull, 0);
00264       GPIO_PinModeSet(TOUCH_Y1, gpioModeInput, 0);
00265       GPIO_PinModeSet(TOUCH_Y2, gpioModeInput, 0);
00266       sInit.input   = ADC_X;
00267       sInit.acqTime = adcAcqTime16;                  /* pressed, so speed-up */
00268       ADC_InitSingle(ADC0, &sInit);
00269       current_pos.pen = newpos.pen;
00270       TOUCH_RecalculatePosition(&newpos);
00271       if (newpos.pen)
00272       {
00273         int call_upcall = TOUCH_StateChanged();
00274         if (call_upcall)
00275         {
00276           current_pos.x = newpos.x;
00277           current_pos.y = newpos.y;
00278         }
00279         current_pos.adcx = newpos.adcx;
00280         current_pos.adcy = newpos.adcy;
00281         current_pos.pen  = 1;
00282         if (call_upcall) TOUCH_CallUpcall();
00283       }
00284       newpos.pen = 1;
00285     }
00286     else
00287     {
00288       touch_state     = TOUCH_INIT;
00289       newpos.pen      = 0;
00290       current_pos.pen = 0;
00291       TOUCH_CallUpcall();
00292     }
00293     break;
00294   case TOUCH_MEASURE_Y:                                             /* touch panel pressed, measure Y position */
00295     newpos.adcy = (ADC_DataSingleGet(ADC0) + 31) >> 6;              /* reduce ADC resolution to 10-bits */
00296     GPIO_PinModeSet(TOUCH_Y1, gpioModePushPull, 0);                 /* to avoid overflow in calibration routines */
00297     GPIO_PinModeSet(TOUCH_Y2, gpioModePushPull, 1);
00298     GPIO_PinModeSet(TOUCH_X1, gpioModeInput, 0);
00299     GPIO_PinModeSet(TOUCH_X2, gpioModeInput, 0);
00300     sInit.input = ADC_Y;
00301     ADC_InitSingle(ADC0, &sInit);
00302     touch_state = TOUCH_MEASURE_X;
00303     break;
00304   case TOUCH_MEASURE_X:   /* touch panel pressed, measure X position */
00305     newpos.adcx = (ADC_DataSingleGet(ADC0) + 31) >> 6;
00306     GPIO_PinModeSet(TOUCH_Y1, gpioModePushPull, 1);
00307     GPIO_PinModeSet(TOUCH_Y2, gpioModePushPull, 1);
00308     GPIO_PinModeSet(TOUCH_X1, gpioModeInputPullFilter , 0);
00309     GPIO_PinModeSet(TOUCH_X2, gpioModeInput, 0);
00310     sInit.input = ADC_Y;
00311     ADC_InitSingle(ADC0, &sInit);
00312     touch_state = TOUCH_CHECK_PRESS;
00313     break;
00314   default: touch_state = TOUCH_INIT;
00315   }
00316   ADC_IntClear(ADC0, ADC_IF_SINGLE);
00317   ADC_Start(ADC0, adcStartSingle);
00318 }
00319 
00320 /***************************************************************************/
00327 int TOUCH_IsBusy(void)
00328 {
00329   if( (touch_state == TOUCH_INIT) )
00330   {
00331     return GPIO_PinInGet(TOUCH_X2);
00332   }
00333 
00334   if( (touch_state == TOUCH_CHECK_PRESS) )
00335   {
00336     return TOUCH_BUSY_CHECK;
00337   }
00338 
00339   return(TOUCH_BUSY_SCAN);
00340 }
00341 
00342 /***************************************************************************/
00349 void TOUCH_Init(TOUCH_Config_TypeDef *config)
00350 {
00351   ADC_Init_TypeDef init = ADC_INIT_DEFAULT;
00352 #ifndef TOUCH_WITHOUT_STORE
00353   touch_LoadCalibration();
00354 #endif
00355   CMU_ClockEnable(cmuClock_ADC0, true);
00356   ADC_IntDisable(ADC0, _ADC_IF_MASK);
00357   init.prescale     = ADC_PrescaleCalc(config->frequency, 0);
00358   touch_ignore_move = config->ignore;
00359   init.ovsRateSel   = config->oversampling;
00360   ADC_Init(ADC0, &init);
00361   BSP_PeripheralAccess(BSP_TOUCH, true);
00362   sInit.input      = ADC_Y;
00363   sInit.reference  = adcRefVDD;
00364   sInit.resolution = adcResOVS;
00365   ADC_InitSingle(ADC0, &sInit);
00366   ADC_IntClear(ADC0, _ADC_IF_MASK);
00367   touch_state = TOUCH_INIT;
00368   NVIC_ClearPendingIRQ(ADC0_IRQn);
00369   NVIC_EnableIRQ(ADC0_IRQn);
00370   ADC_IntEnable(ADC0, ADC_IF_SINGLE);
00371   ADC_Start(ADC0, adcStartSingle);
00372 }
00373 
00374 /***************************************************************************/
00381 TOUCH_Pos_TypeDef *TOUCH_GetPos(void)
00382 {
00383   ADC_IntDisable(ADC0, ADC_IF_SINGLE); /* we need to disable ADC interrupt to avoid current_pos structure update for a while */
00384   pos.pen  = current_pos.pen;
00385   pos.x    = current_pos.x;
00386   pos.y    = current_pos.y;
00387   pos.adcx = current_pos.adcx;
00388   pos.adcy = current_pos.adcy;
00389   ADC_IntEnable(ADC0, ADC_IF_SINGLE);
00390 
00391   return &pos;
00392 }
00393 
00394 /***************************************************************************/
00401 void TOUCH_RegisterUpcall(TOUCH_Upcall_TypeDef *new_upcall)
00402 {
00403   upcall = new_upcall;
00404 }
00405 
00406 
00407 /***************************************************************************/
00419 int TOUCH_CalibrationTable(POINT * displayPtr, POINT * screenPtr)
00420 {
00421   int result;
00422   result = setCalibrationMatrix(displayPtr, screenPtr, &calibrationMatrix);
00423 #ifndef TOUCH_WITHOUT_STORE
00424   if (result == OK)
00425     touch_StoreCalibration();
00426 #endif
00427   return(result);
00428 }