touch.c

Go to the documentation of this file.
00001 /**************************************************************************/
00017 #include <stdlib.h>
00018 #include <string.h>
00019 #include "em_device.h"
00020 #include "em_adc.h"
00021 #include "em_gpio.h"
00022 #include "em_cmu.h"
00023 #include "touch.h"
00024 #include "calibrate.h"
00025 #include "bsp.h"
00026 #ifndef TOUCH_WITHOUT_STORE
00027 #include "i2cdrv.h"
00028 #include "eeprom.h"
00029 #endif
00030 
00032 #define CALIBRATION_MAGIC_NUMBER     0xCA71B4A7
00033 #define CALIBRATION_EEPROM_OFFSET    0x80
00034 
00036 /* ADC inputs connected to touchpad */
00037 #define ADC_X                     adcSingleInpCh1 
00038 #define ADC_Y                     adcSingleInpCh4 
00040 /* touchpad PIOs */
00041 #define TOUCH_X1                  gpioPortD, 5 
00042 #define TOUCH_X2                  gpioPortD, 4 
00043 #define TOUCH_Y1                  gpioPortD, 3 
00044 #define TOUCH_Y2                  gpioPortD, 1 
00047 typedef enum
00048 { TOUCH_INIT,
00049   TOUCH_CHECK_PRESS,
00050   TOUCH_MEASURE_X,
00051   TOUCH_MEASURE_Y } TOUCH_State_TypeDef;
00052 
00055 static volatile TOUCH_State_TypeDef touch_state = TOUCH_INIT;
00056 static ADC_InitSingle_TypeDef       sInit       = ADC_INITSINGLE_DEFAULT;
00057 volatile TOUCH_Pos_TypeDef          newpos;
00058 volatile TOUCH_Pos_TypeDef          current_pos;
00059 static TOUCH_Pos_TypeDef            pos;
00060 
00061 static void                         (*upcall)(TOUCH_Pos_TypeDef *) = 0;
00062 
00063 uint32_t                            touch_ignore_move;
00067 MATRIX calibrationMatrix = { 103800, 2048, -8184704, -384, 102144, -16424640, 287650 };
00068 
00070 #ifndef TOUCH_WITHOUT_STORE
00071 /**************************************************************************/
00087 static uint32_t touch_CountChecksum(uint32_t magic, uint32_t *data, uint32_t len)
00088 {
00089   unsigned long checksum = magic;
00090 
00091   while (len--)
00092   {
00093     if (checksum & 0x80000000)
00094     {
00095       checksum <<= 1;
00096       checksum  |= 1;
00097     }
00098     else checksum <<= 1;
00099     checksum += *data;
00100     data++;
00101   }
00102   return(checksum);
00103 }
00104 
00106 static void touch_LoadCalibration(void)
00107 {
00108   I2C_Init_TypeDef i2cInit = I2C_INIT_DEFAULT;
00109   uint32_t         temp, checksum;
00110   int              count;
00111   MATRIX           new_matrix;
00112   /* Initialize I2C driver, using standard rate. Devices on DK itself */
00113   /* supports fast mode, but in case some slower devices are added on */
00114   /* prototype board, we use standard mode. */
00115   I2CDRV_Init(&i2cInit);
00116   count  = EEPROM_Read(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET, (uint8_t*) &temp, sizeof(temp));
00117   count += EEPROM_Read(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET + 4, (uint8_t*) &new_matrix, sizeof(new_matrix));
00118   if (count == sizeof(new_matrix) + 4)
00119   {
00120     if (temp == CALIBRATION_MAGIC_NUMBER)
00121     {
00122       checksum = touch_CountChecksum(temp, (uint32_t*) &new_matrix, sizeof(new_matrix) / 4);
00123       count    = EEPROM_Read(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET + 4 + sizeof(new_matrix), (uint8_t*) &temp, sizeof(temp));
00124       if (temp == checksum)
00125       {                                      /* looks like calibration table is valid */
00126         ADC_IntDisable(ADC0, ADC_IF_SINGLE); /* we need to disable ADC interrupt to avoid current_pos structure update for a while */
00127         memcpy(&calibrationMatrix, &new_matrix, sizeof(calibrationMatrix));
00128         ADC_IntEnable(ADC0, ADC_IF_SINGLE);
00129       }
00130     }
00131   }
00132 }
00133 
00135 static void touch_StoreCalibration(void)
00136 {
00137   int      count;
00138   uint32_t temp = CALIBRATION_MAGIC_NUMBER, checksum;
00139   checksum = touch_CountChecksum(CALIBRATION_MAGIC_NUMBER, (uint32_t*) &calibrationMatrix, sizeof(calibrationMatrix) / 4);
00140   count    = EEPROM_Write(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET, (uint8_t*) &temp, sizeof(temp));
00141   count   += EEPROM_Write(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET + 4, (uint8_t*) &calibrationMatrix, sizeof(calibrationMatrix));
00142   count   += EEPROM_Write(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET + 4 + sizeof(calibrationMatrix), (uint8_t*) &checksum, sizeof(checksum));
00143 }
00144 #endif
00145 
00147 /***************************************************************************/
00154 void TOUCH_RecalculatePosition(volatile TOUCH_Pos_TypeDef *pos)
00155 {
00156   POINT old_pos, new_pos;
00157 
00158   if (pos->pen)
00159   {
00160     old_pos.x = pos->adcx;
00161     old_pos.y = pos->adcy;
00162     if (getDisplayPoint(&new_pos, &old_pos, &calibrationMatrix) == OK)
00163     {
00164       if (new_pos.x >= 0) pos->x = new_pos.x;
00165       else pos->x = 0;
00166       if (new_pos.y >= 0) pos->y = new_pos.y;
00167       else pos->y = 0;
00168     }
00169   }
00170   else
00171   {
00172     pos->x = 0;
00173     pos->y = 0;
00174   }
00175 }
00176 
00177 /***************************************************************************/
00181 void TOUCH_CallUpcall(void)
00182 {
00183   if (upcall)
00184     upcall((TOUCH_Pos_TypeDef*) &current_pos);
00185 }
00186 
00187 /***************************************************************************/
00194 int TOUCH_StateChanged(void)
00195 {
00196   int result = 0;
00197   int diff, a, b;
00198   if (newpos.pen && !current_pos.pen) result = 1; /* pen down */
00199   a    = current_pos.x;
00200   b    = newpos.x;
00201   diff = a - b;
00202   if (abs(diff) > (int) touch_ignore_move) result = 1; /* move in X axis */
00203   a    = current_pos.y;
00204   b    = newpos.y;
00205   diff = a - b;
00206   if (abs(diff) > (int) touch_ignore_move) result = 1; /* move in Y axis */
00207   return result;
00208 }
00209 
00210 /***************************************************************************/
00215 void ADC0_IRQHandler(void)
00216 {
00217   switch (touch_state)
00218   {
00219   case TOUCH_INIT:   /* enter this state if touch panel is not pressed */
00220     GPIO_PinModeSet(TOUCH_Y1, gpioModePushPull, 1);
00221     GPIO_PinModeSet(TOUCH_Y2, gpioModePushPull, 1);
00222     GPIO_PinModeSet(TOUCH_X1, gpioModeInputPullFilter , 0);
00223     GPIO_PinModeSet(TOUCH_X2, gpioModeInput, 0);
00224     sInit.input      = ADC_Y;
00225     sInit.reference  = adcRefVDD;
00226     sInit.resolution = adcResOVS;
00227     sInit.acqTime    = adcAcqTime128;               /* used to slow down */
00228     if(GPIO_PinInGet(TOUCH_X2))
00229     {
00230       touch_state = TOUCH_MEASURE_Y;
00231       GPIO_PinModeSet(TOUCH_X1, gpioModePushPull, 1);
00232       GPIO_PinModeSet(TOUCH_X2, gpioModePushPull, 0);
00233       GPIO_PinModeSet(TOUCH_Y1, gpioModeInput, 0);
00234       GPIO_PinModeSet(TOUCH_Y2, gpioModeInput, 0);
00235       sInit.input   = ADC_X;
00236       sInit.acqTime = adcAcqTime16;                  /* pressed, so speed-up */
00237     }
00238     ADC_InitSingle(ADC0, &sInit);
00239     break;
00240   case TOUCH_CHECK_PRESS:   /* checks if touch panel is still pressed */
00241     if( GPIO_PinInGet(TOUCH_X2) )
00242     {
00243       touch_state = TOUCH_MEASURE_Y;
00244       GPIO_PinModeSet(TOUCH_X1, gpioModePushPull, 1);
00245       GPIO_PinModeSet(TOUCH_X2, gpioModePushPull, 0);
00246       GPIO_PinModeSet(TOUCH_Y1, gpioModeInput, 0);
00247       GPIO_PinModeSet(TOUCH_Y2, gpioModeInput, 0);
00248       sInit.input   = ADC_X;
00249       sInit.acqTime = adcAcqTime16;                  /* pressed, so speed-up */
00250       ADC_InitSingle(ADC0, &sInit);
00251       current_pos.pen = newpos.pen;
00252       TOUCH_RecalculatePosition(&newpos);
00253       if (newpos.pen)
00254       {
00255         int call_upcall = TOUCH_StateChanged();
00256         if (call_upcall)
00257         {
00258           current_pos.x = newpos.x;
00259           current_pos.y = newpos.y;
00260         }
00261         current_pos.adcx = newpos.adcx;
00262         current_pos.adcy = newpos.adcy;
00263         current_pos.pen  = 1;
00264         if (call_upcall) TOUCH_CallUpcall();
00265       }
00266       newpos.pen = 1;
00267     }
00268     else
00269     {
00270       touch_state     = TOUCH_INIT;
00271       newpos.pen      = 0;
00272       current_pos.pen = 0;
00273       TOUCH_CallUpcall();
00274     }
00275     break;
00276   case TOUCH_MEASURE_Y:                                             /* touch panel pressed, measure Y position */
00277     newpos.adcy = (ADC_DataSingleGet(ADC0) + 31) >> 6;              /* reduce ADC resolution to 10-bits */
00278     GPIO_PinModeSet(TOUCH_Y1, gpioModePushPull, 0);                 /* to avoid overflow in calibration routines */
00279     GPIO_PinModeSet(TOUCH_Y2, gpioModePushPull, 1);
00280     GPIO_PinModeSet(TOUCH_X1, gpioModeInput, 0);
00281     GPIO_PinModeSet(TOUCH_X2, gpioModeInput, 0);
00282     sInit.input = ADC_Y;
00283     ADC_InitSingle(ADC0, &sInit);
00284     touch_state = TOUCH_MEASURE_X;
00285     break;
00286   case TOUCH_MEASURE_X:   /* touch panel pressed, measure X position */
00287     newpos.adcx = (ADC_DataSingleGet(ADC0) + 31) >> 6;
00288     GPIO_PinModeSet(TOUCH_Y1, gpioModePushPull, 1);
00289     GPIO_PinModeSet(TOUCH_Y2, gpioModePushPull, 1);
00290     GPIO_PinModeSet(TOUCH_X1, gpioModeInputPullFilter , 0);
00291     GPIO_PinModeSet(TOUCH_X2, gpioModeInput, 0);
00292     sInit.input = ADC_Y;
00293     ADC_InitSingle(ADC0, &sInit);
00294     touch_state = TOUCH_CHECK_PRESS;
00295     break;
00296   default: touch_state = TOUCH_INIT;
00297   }
00298   ADC_IntClear(ADC0, ADC_IF_SINGLE);
00299   ADC_Start(ADC0, adcStartSingle);
00300 }
00301 
00302 /***************************************************************************/
00309 int TOUCH_IsBusy(void)
00310 {
00311   if( (touch_state == TOUCH_INIT) )
00312   {
00313     return GPIO_PinInGet(TOUCH_X2);
00314   }
00315 
00316   if( (touch_state == TOUCH_CHECK_PRESS) )
00317   {
00318     return TOUCH_BUSY_CHECK;
00319   }
00320 
00321   return(TOUCH_BUSY_SCAN);
00322 }
00323 
00324 /***************************************************************************/
00331 void TOUCH_Init(TOUCH_Config_TypeDef *config)
00332 {
00333   ADC_Init_TypeDef init = ADC_INIT_DEFAULT;
00334 #ifndef TOUCH_WITHOUT_STORE
00335   touch_LoadCalibration();
00336 #endif
00337   CMU_ClockEnable(cmuClock_ADC0, true);
00338   ADC_IntDisable(ADC0, _ADC_IF_MASK);
00339   init.prescale     = ADC_PrescaleCalc(config->frequency, 0);
00340   touch_ignore_move = config->ignore;
00341   init.ovsRateSel   = config->oversampling;
00342   ADC_Init(ADC0, &init);
00343   BSP_PeripheralAccess(BSP_TOUCH, true);
00344   sInit.input      = ADC_Y;
00345   sInit.reference  = adcRefVDD;
00346   sInit.resolution = adcResOVS;
00347   ADC_InitSingle(ADC0, &sInit);
00348   ADC_IntClear(ADC0, _ADC_IF_MASK);
00349   touch_state = TOUCH_INIT;
00350   NVIC_ClearPendingIRQ(ADC0_IRQn);
00351   NVIC_EnableIRQ(ADC0_IRQn);
00352   ADC_IntEnable(ADC0, ADC_IF_SINGLE);
00353   ADC_Start(ADC0, adcStartSingle);
00354 }
00355 
00356 /***************************************************************************/
00363 TOUCH_Pos_TypeDef *TOUCH_GetPos(void)
00364 {
00365   ADC_IntDisable(ADC0, ADC_IF_SINGLE); /* we need to disable ADC interrupt to avoid current_pos structure update for a while */
00366   pos.pen  = current_pos.pen;
00367   pos.x    = current_pos.x;
00368   pos.y    = current_pos.y;
00369   pos.adcx = current_pos.adcx;
00370   pos.adcy = current_pos.adcy;
00371   ADC_IntEnable(ADC0, ADC_IF_SINGLE);
00372 
00373   return &pos;
00374 }
00375 
00376 /***************************************************************************/
00383 void TOUCH_RegisterUpcall(TOUCH_Upcall_TypeDef *new_upcall)
00384 {
00385   upcall = new_upcall;
00386 }
00387 
00388 
00389 /***************************************************************************/
00401 int TOUCH_CalibrationTable(POINT * displayPtr, POINT * screenPtr)
00402 {
00403   int result;
00404   result = setCalibrationMatrix(displayPtr, screenPtr, &calibrationMatrix);
00405 #ifndef TOUCH_WITHOUT_STORE
00406   if (result == OK)
00407     touch_StoreCalibration();
00408 #endif
00409   return(result);
00410 }