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 "i2cspm.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_PORT             gpioPortD     
00042 #define TOUCH_X1_PIN              5             
00043 #define TOUCH_X2_PORT             gpioPortD     
00044 #define TOUCH_X2_PIN              4             
00045 #define TOUCH_Y1_PORT             gpioPortD     
00046 #define TOUCH_Y1_PIN              3             
00047 #define TOUCH_Y2_PORT             gpioPortD     
00048 #define TOUCH_Y2_PIN              1             
00051 typedef enum
00052 { TOUCH_INIT,
00053   TOUCH_CHECK_PRESS,
00054   TOUCH_MEASURE_X,
00055   TOUCH_MEASURE_Y } TOUCH_State_TypeDef;
00056 
00059 static volatile TOUCH_State_TypeDef touch_state = TOUCH_INIT;
00060 static ADC_InitSingle_TypeDef       sInit       = ADC_INITSINGLE_DEFAULT;
00061 volatile TOUCH_Pos_TypeDef          newpos;
00062 volatile TOUCH_Pos_TypeDef          current_pos;
00063 static TOUCH_Pos_TypeDef            pos;
00064 
00065 static void                         (*upcall)(TOUCH_Pos_TypeDef *) = 0;
00066 
00067 uint32_t                            touch_ignore_move;
00071 MATRIX calibrationMatrix = { 103800, 2048, -8184704, -384, 102144, -16424640, 287650 };
00072 
00074 #ifndef TOUCH_WITHOUT_STORE
00075 /**************************************************************************/
00091 static uint32_t touch_CountChecksum(uint32_t magic, uint32_t *data, uint32_t len)
00092 {
00093   unsigned long checksum = magic;
00094 
00095   while (len--)
00096   {
00097     if (checksum & 0x80000000)
00098     {
00099       checksum <<= 1;
00100       checksum  |= 1;
00101     }
00102     else checksum <<= 1;
00103     checksum += *data;
00104     data++;
00105   }
00106   return(checksum);
00107 }
00108 
00110 static void touch_LoadCalibration(void)
00111 {
00112   I2CSPM_Init_TypeDef i2cInit = I2CSPM_INIT_DEFAULT;
00113   uint32_t         temp, checksum;
00114   int              count;
00115   MATRIX           new_matrix;
00116 
00117 #if !defined( BSP_STK )
00118   BSP_PeripheralAccess(BSP_I2C, true);
00119 #endif
00120 
00121   /* Initialize I2C driver, using standard rate. Devices on DK itself */
00122   /* supports fast mode, but in case some slower devices are added on */
00123   /* prototype board, we use standard mode. */
00124   I2CSPM_Init(&i2cInit);
00125   count  = EEPROM_Read(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET, (uint8_t*) &temp, sizeof(temp));
00126   count += EEPROM_Read(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET + 4, (uint8_t*) &new_matrix, sizeof(new_matrix));
00127   if (count == sizeof(new_matrix) + 4)
00128   {
00129     if (temp == CALIBRATION_MAGIC_NUMBER)
00130     {
00131       checksum = touch_CountChecksum(temp, (uint32_t*) &new_matrix, sizeof(new_matrix) / 4);
00132       count    = EEPROM_Read(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET + 4 + sizeof(new_matrix), (uint8_t*) &temp, sizeof(temp));
00133       if (temp == checksum)
00134       {                                      /* looks like calibration table is valid */
00135         ADC_IntDisable(ADC0, ADC_IF_SINGLE); /* we need to disable ADC interrupt to avoid current_pos structure update for a while */
00136         memcpy(&calibrationMatrix, &new_matrix, sizeof(calibrationMatrix));
00137         ADC_IntEnable(ADC0, ADC_IF_SINGLE);
00138       }
00139     }
00140   }
00141 }
00142 
00144 static void touch_StoreCalibration(void)
00145 {
00146   int      count;
00147   uint32_t temp = CALIBRATION_MAGIC_NUMBER, checksum;
00148   checksum = touch_CountChecksum(CALIBRATION_MAGIC_NUMBER, (uint32_t*) &calibrationMatrix, sizeof(calibrationMatrix) / 4);
00149   count    = EEPROM_Write(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET, (uint8_t*) &temp, sizeof(temp));
00150   count   += EEPROM_Write(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET + 4, (uint8_t*) &calibrationMatrix, sizeof(calibrationMatrix));
00151   count   += EEPROM_Write(I2C0, EEPROM_DVK_ADDR, CALIBRATION_EEPROM_OFFSET + 4 + sizeof(calibrationMatrix), (uint8_t*) &checksum, sizeof(checksum));
00152 }
00153 #endif
00154 
00156 /***************************************************************************/
00163 void TOUCH_RecalculatePosition(volatile TOUCH_Pos_TypeDef *pos)
00164 {
00165   POINT old_pos, new_pos;
00166 
00167   if (pos->pen)
00168   {
00169     old_pos.x = pos->adcx;
00170     old_pos.y = pos->adcy;
00171     if (getDisplayPoint(&new_pos, &old_pos, &calibrationMatrix) == OK)
00172     {
00173       if (new_pos.x >= 0) pos->x = new_pos.x;
00174       else pos->x = 0;
00175       if (new_pos.y >= 0) pos->y = new_pos.y;
00176       else pos->y = 0;
00177     }
00178   }
00179   else
00180   {
00181     pos->x = 0;
00182     pos->y = 0;
00183   }
00184 }
00185 
00186 /***************************************************************************/
00190 void TOUCH_CallUpcall(void)
00191 {
00192   if (upcall)
00193     upcall((TOUCH_Pos_TypeDef*) &current_pos);
00194 }
00195 
00196 /***************************************************************************/
00203 int TOUCH_StateChanged(void)
00204 {
00205   int result = 0;
00206   int diff, a, b;
00207   if (newpos.pen && !current_pos.pen) result = 1; /* pen down */
00208   a    = current_pos.x;
00209   b    = newpos.x;
00210   diff = a - b;
00211   if (abs(diff) > (int) touch_ignore_move) result = 1; /* move in X axis */
00212   a    = current_pos.y;
00213   b    = newpos.y;
00214   diff = a - b;
00215   if (abs(diff) > (int) touch_ignore_move) result = 1; /* move in Y axis */
00216   return result;
00217 }
00218 
00219 /***************************************************************************/
00224 void ADC0_IRQHandler(void)
00225 {
00226   switch (touch_state)
00227   {
00228   case TOUCH_INIT:   /* enter this state if touch panel is not pressed */
00229     GPIO_PinModeSet(TOUCH_Y1_PORT, TOUCH_Y1_PIN, gpioModePushPull, 1);
00230     GPIO_PinModeSet(TOUCH_Y2_PORT, TOUCH_Y2_PIN, gpioModePushPull, 1);
00231     GPIO_PinModeSet(TOUCH_X1_PORT, TOUCH_X1_PIN, gpioModeInputPullFilter , 0);
00232     GPIO_PinModeSet(TOUCH_X2_PORT, TOUCH_X2_PIN, gpioModeInput, 0);
00233     sInit.input      = ADC_Y;
00234     sInit.reference  = adcRefVDD;
00235     sInit.resolution = adcResOVS;
00236     sInit.acqTime    = adcAcqTime128;               /* used to slow down */
00237     if(GPIO_PinInGet(TOUCH_X2_PORT, TOUCH_X2_PIN))
00238     {
00239       touch_state = TOUCH_MEASURE_Y;
00240       GPIO_PinModeSet(TOUCH_X1_PORT, TOUCH_X1_PIN, gpioModePushPull, 1);
00241       GPIO_PinModeSet(TOUCH_X2_PORT, TOUCH_X2_PIN, gpioModePushPull, 0);
00242       GPIO_PinModeSet(TOUCH_Y1_PORT, TOUCH_Y1_PIN, gpioModeInput, 0);
00243       GPIO_PinModeSet(TOUCH_Y2_PORT, TOUCH_Y2_PIN, gpioModeInput, 0);
00244       sInit.input   = ADC_X;
00245       sInit.acqTime = adcAcqTime16;                  /* pressed, so speed-up */
00246     }
00247     ADC_InitSingle(ADC0, &sInit);
00248     break;
00249   case TOUCH_CHECK_PRESS:   /* checks if touch panel is still pressed */
00250     if( GPIO_PinInGet(TOUCH_X2_PORT, TOUCH_X2_PIN) )
00251     {
00252       touch_state = TOUCH_MEASURE_Y;
00253       GPIO_PinModeSet(TOUCH_X1_PORT, TOUCH_X1_PIN, gpioModePushPull, 1);
00254       GPIO_PinModeSet(TOUCH_X2_PORT, TOUCH_X2_PIN, gpioModePushPull, 0);
00255       GPIO_PinModeSet(TOUCH_Y1_PORT, TOUCH_Y1_PIN, gpioModeInput, 0);
00256       GPIO_PinModeSet(TOUCH_Y2_PORT, TOUCH_Y2_PIN, gpioModeInput, 0);
00257       sInit.input   = ADC_X;
00258       sInit.acqTime = adcAcqTime16;                  /* pressed, so speed-up */
00259       ADC_InitSingle(ADC0, &sInit);
00260       current_pos.pen = newpos.pen;
00261       TOUCH_RecalculatePosition(&newpos);
00262       if (newpos.pen)
00263       {
00264         int call_upcall = TOUCH_StateChanged();
00265         if (call_upcall)
00266         {
00267           current_pos.x = newpos.x;
00268           current_pos.y = newpos.y;
00269         }
00270         current_pos.adcx = newpos.adcx;
00271         current_pos.adcy = newpos.adcy;
00272         current_pos.pen  = 1;
00273         if (call_upcall) TOUCH_CallUpcall();
00274       }
00275       newpos.pen = 1;
00276     }
00277     else
00278     {
00279       touch_state     = TOUCH_INIT;
00280       newpos.pen      = 0;
00281       current_pos.pen = 0;
00282       TOUCH_CallUpcall();
00283     }
00284     break;
00285   case TOUCH_MEASURE_Y:                                             /* touch panel pressed, measure Y position */
00286     newpos.adcy = (ADC_DataSingleGet(ADC0) + 31) >> 6;              /* reduce ADC resolution to 10-bits */
00287     GPIO_PinModeSet(TOUCH_Y1_PORT, TOUCH_Y1_PIN, gpioModePushPull, 0);                 /* to avoid overflow in calibration routines */
00288     GPIO_PinModeSet(TOUCH_Y2_PORT, TOUCH_Y2_PIN, gpioModePushPull, 1);
00289     GPIO_PinModeSet(TOUCH_X1_PORT, TOUCH_X1_PIN, gpioModeInput, 0);
00290     GPIO_PinModeSet(TOUCH_X2_PORT, TOUCH_X2_PIN, gpioModeInput, 0);
00291     sInit.input = ADC_Y;
00292     ADC_InitSingle(ADC0, &sInit);
00293     touch_state = TOUCH_MEASURE_X;
00294     break;
00295   case TOUCH_MEASURE_X:   /* touch panel pressed, measure X position */
00296     newpos.adcx = (ADC_DataSingleGet(ADC0) + 31) >> 6;
00297     GPIO_PinModeSet(TOUCH_Y1_PORT, TOUCH_Y1_PIN, gpioModePushPull, 1);
00298     GPIO_PinModeSet(TOUCH_Y2_PORT, TOUCH_Y2_PIN, gpioModePushPull, 1);
00299     GPIO_PinModeSet(TOUCH_X1_PORT, TOUCH_X1_PIN, gpioModeInputPullFilter , 0);
00300     GPIO_PinModeSet(TOUCH_X2_PORT, TOUCH_X2_PIN, gpioModeInput, 0);
00301     sInit.input = ADC_Y;
00302     ADC_InitSingle(ADC0, &sInit);
00303     touch_state = TOUCH_CHECK_PRESS;
00304     break;
00305   default: touch_state = TOUCH_INIT;
00306   }
00307   ADC_IntClear(ADC0, ADC_IF_SINGLE);
00308   ADC_Start(ADC0, adcStartSingle);
00309 }
00310 
00311 /***************************************************************************/
00318 int TOUCH_IsBusy(void)
00319 {
00320   if( (touch_state == TOUCH_INIT) )
00321   {
00322     return GPIO_PinInGet(TOUCH_X2_PORT, TOUCH_X2_PIN);
00323   }
00324 
00325   if( (touch_state == TOUCH_CHECK_PRESS) )
00326   {
00327     return TOUCH_BUSY_CHECK;
00328   }
00329 
00330   return(TOUCH_BUSY_SCAN);
00331 }
00332 
00333 /***************************************************************************/
00340 void TOUCH_Init(TOUCH_Config_TypeDef *config)
00341 {
00342   ADC_Init_TypeDef init = ADC_INIT_DEFAULT;
00343 #ifndef TOUCH_WITHOUT_STORE
00344   touch_LoadCalibration();
00345 #endif
00346   CMU_ClockEnable(cmuClock_ADC0, true);
00347   ADC_IntDisable(ADC0, _ADC_IF_MASK);
00348   init.prescale     = ADC_PrescaleCalc(config->frequency, 0);
00349   touch_ignore_move = config->ignore;
00350   init.ovsRateSel   = config->oversampling;
00351   ADC_Init(ADC0, &init);
00352   BSP_PeripheralAccess(BSP_TOUCH, true);
00353   sInit.input      = ADC_Y;
00354   sInit.reference  = adcRefVDD;
00355   sInit.resolution = adcResOVS;
00356   ADC_InitSingle(ADC0, &sInit);
00357   ADC_IntClear(ADC0, _ADC_IF_MASK);
00358   touch_state = TOUCH_INIT;
00359   NVIC_ClearPendingIRQ(ADC0_IRQn);
00360   NVIC_EnableIRQ(ADC0_IRQn);
00361   ADC_IntEnable(ADC0, ADC_IF_SINGLE);
00362   ADC_Start(ADC0, adcStartSingle);
00363 }
00364 
00365 /***************************************************************************/
00372 TOUCH_Pos_TypeDef *TOUCH_GetPos(void)
00373 {
00374   ADC_IntDisable(ADC0, ADC_IF_SINGLE); /* we need to disable ADC interrupt to avoid current_pos structure update for a while */
00375   pos.pen  = current_pos.pen;
00376   pos.x    = current_pos.x;
00377   pos.y    = current_pos.y;
00378   pos.adcx = current_pos.adcx;
00379   pos.adcy = current_pos.adcy;
00380   ADC_IntEnable(ADC0, ADC_IF_SINGLE);
00381 
00382   return &pos;
00383 }
00384 
00385 /***************************************************************************/
00392 void TOUCH_RegisterUpcall(TOUCH_Upcall_TypeDef *new_upcall)
00393 {
00394   upcall = new_upcall;
00395 }
00396 
00397 
00398 /***************************************************************************/
00410 int TOUCH_CalibrationTable(POINT * displayPtr, POINT * screenPtr)
00411 {
00412   int result;
00413   result = setCalibrationMatrix(displayPtr, screenPtr, &calibrationMatrix);
00414 #ifndef TOUCH_WITHOUT_STORE
00415   if (result == OK)
00416     touch_StoreCalibration();
00417 #endif
00418   return(result);
00419 }