si114x_algorithm.c

00001 /**************************************************************************/
00016 #include "si114x_algorithm.h"
00017 #include "si114x_functions.h"
00018 #include "si1147_i2c.h"
00019 
00020 #define LED1I                 0xb
00021 #define LED2I                 0xb
00022 #define LED3I                 0xb
00023 #define UV_TASKLIST           0x80
00024 #define GESTURE_TASKLIST      0x7
00025 #define HOVER_TASKLIST        0x1
00026 #define UV_IRQ_EN             IE_ALS_EVRYSAMPLE
00027 #define GESTURE_IRQ           (IE_ALS_EVRYSAMPLE | IE_PS1_EVRYSAMPLE | IE_PS2_EVRYSAMPLE | IE_PS3_EVRYSAMPLE)
00028 #define MEASRATE_FAST         320
00029 #define MEASRATE_SLOW         32000
00030 #define PS_HOVER_THRESHOLD    295
00031 #define PS_THRESHOLD          275
00032 #define SI1147_DEVICE_ID      0x47
00033 
00034 /*
00035    The Si114x PGM toolkit functions uses a void* to pass hardware parameters
00036    through to the lower level i2c functions. The struct below is passed down
00037    to the low level i2c functions using that pointer.
00038 */
00039 static si114x_i2c_t si114x_i2c;
00040 static si114x_i2c_t *si114x_handle = &si114x_i2c;
00041 
00042 
00043 /* Function Prototypes */
00044 static gesture_t ProcessSi1147Samples(Si114x_Sample_TypeDef *samples);
00045 
00046 /**************************************************************************/
00056 int Si1147_Detect_Device(I2C_TypeDef *i2c, uint8_t addr)
00057 {
00058   uint8_t data;
00059   si114x_handle->addr = addr;
00060   si114x_handle->i2c  = i2c;
00061   data                = Si114xReadFromRegister(si114x_handle, 0);
00062 
00063   if (data == SI1147_DEVICE_ID)
00064   {
00065     return 1;
00066   }
00067   return 0;
00068 }
00069 
00070 /**************************************************************************/
00082 int Si1147_SetInterruptOutputEnable(I2C_TypeDef *i2c, uint8_t addr, int enable)
00083 {
00084   int retval;
00085   si114x_handle->addr = addr;
00086   si114x_handle->i2c  = i2c;
00087   if (enable)
00088   {
00089     retval = Si114xWriteToRegister(si114x_handle, REG_INT_CFG, ICG_INTOE);
00090   }
00091   else
00092   {
00093     retval = Si114xWriteToRegister(si114x_handle, REG_INT_CFG, 0);
00094   }
00095   return retval;
00096 }
00097 
00098 /**************************************************************************/
00111 int Si1147_GetInterruptOutputEnable(I2C_TypeDef *i2c, uint8_t addr, int *enable)
00112 {
00113   int retval = 0;
00114   si114x_handle->addr = addr;
00115   si114x_handle->i2c  = i2c;
00116   *enable             = Si114xReadFromRegister(si114x_handle, REG_INT_CFG);
00117   return retval;
00118 }
00119 
00120 /**************************************************************************/
00137 int Si1147_MeasureUVAndObjectPresent(I2C_TypeDef *i2c, uint8_t addr, uint16_t *uvIndex, int *objectDetect)
00138 {
00139   uint16_t data;
00140   int      retval = 0;
00141   int      gestureMode;
00142   *objectDetect       = 0;
00143   si114x_handle->addr = addr;
00144   si114x_handle->i2c  = i2c;
00145   Si1147_GetInterruptOutputEnable(i2c,addr,&gestureMode);
00146   if ( !gestureMode ) /* Force only if not already running swipe detection. */
00147   {
00148     Si114xPsAlsForce(si114x_handle);
00149     while ((Si114xReadFromRegister(si114x_handle, REG_IRQ_STATUS) & 1) == 0)
00150     ;                                                           /*wait for measurement data */
00151   }
00152   data  = Si114xReadFromRegister(si114x_handle, REG_AUX_DATA0); /*read sample data from si114x */
00153   data |= Si114xReadFromRegister(si114x_handle, REG_AUX_DATA1) << 8;
00154   /*round to nearest*/
00155   *uvIndex  = data + 50;
00156   *uvIndex /= 100;
00157 
00158   if ( !gestureMode ) /* Check for object only if not already running swipe detection. */
00159   {
00160     data  = Si114xReadFromRegister(si114x_handle, REG_PS1_DATA0); /*read sample data from si114x */
00161     data |= Si114xReadFromRegister(si114x_handle, REG_PS1_DATA1) << 8;
00162     if (data > PS_HOVER_THRESHOLD)
00163     {
00164       *objectDetect = 1;
00165     }
00166   }
00167   /*clear irq*/
00168   Si114xWriteToRegister(si114x_handle, REG_IRQ_STATUS, 0xff);
00169 
00170   return retval;
00171 }
00172 
00173 /**************************************************************************/
00181 static void readPSData(HANDLE si114x_handle, Si114x_Sample_TypeDef *sample)
00182 {
00183   /*read sample data from si114x */
00184   sample->ps1  = Si114xReadFromRegister(si114x_handle, REG_PS1_DATA0);
00185   sample->ps1 |= Si114xReadFromRegister(si114x_handle, REG_PS1_DATA1) << 8;
00186 
00187   sample->ps2  = Si114xReadFromRegister(si114x_handle, REG_PS2_DATA0);
00188   sample->ps2 |= Si114xReadFromRegister(si114x_handle, REG_PS2_DATA1) << 8;
00189 
00190   sample->ps3  = Si114xReadFromRegister(si114x_handle, REG_PS3_DATA0);
00191   sample->ps3 |= Si114xReadFromRegister(si114x_handle, REG_PS3_DATA1) << 8;
00192 }
00193 
00194 
00195 /**************************************************************************/
00208 gesture_t Si1147_NewSample(I2C_TypeDef *i2c, uint8_t addr, uint32_t timestamp)
00209 {
00210   Si114x_Sample_TypeDef sample;
00211   si114x_handle->addr = addr;
00212   si114x_handle->i2c  = i2c;
00213   sample.timestamp    = timestamp;
00214   /*read sample data from si114x */
00215   readPSData(si114x_handle, &sample);
00216   /*clear irq*/
00217   Si114xWriteToRegister(si114x_handle, REG_IRQ_STATUS, Si114xReadFromRegister(si114x_handle, REG_IRQ_STATUS));
00218   /*look for gestures */
00219   return ProcessSi1147Samples(&sample);
00220 }
00221 
00222 
00223 /**************************************************************************/
00233 static gesture_t ProcessSi1147Samples(Si114x_Sample_TypeDef *samples)
00234 {
00235   u16        ps[3];
00236 
00237   static u32 ps_entry_time[3] = { 0, 0, 0 };
00238   static u32 ps_exit_time[3]  = { 0, 0, 0 };
00239 
00240   static u8  ps_state[3] = { 0, 0, 0 };
00241 
00242   u8         array_counter;
00243   u32 diff_x ;
00244   u32 diff_y1 ;
00245   u32 diff_y2 ;
00246   u32 ps_time[3] ;
00247   u32 ps_avg;
00248   gesture_t  ret = NONE;  /*gesture result return value */
00249   /*save new samples into ps array */
00250   ps[0] = samples->ps1;
00251   ps[1] = samples->ps2;
00252   ps[2] = samples->ps3;
00253 
00254   /* Check state of all three measurements */
00255   for (array_counter = 0; array_counter < 3; array_counter++)
00256   {
00257     /* If measurement higher than the ps_threshold value, */
00258     /*   record the time of entry and change the state to look for the exit time */
00259     if (ps[array_counter] >= PS_THRESHOLD)
00260     {
00261       if (ps_state[array_counter] == 0)
00262       {
00263         ps_state[array_counter]      = 1;
00264         ps_entry_time[array_counter] = samples->timestamp;
00265       }
00266     }
00267     else
00268     {
00269       if (ps_state[array_counter] == 1)
00270       {
00271         ps_state[array_counter]     = 0;
00272         ps_exit_time[array_counter] = samples->timestamp;
00273       }
00274     }
00275   }
00276 
00277   /* If there is no object in front of the board, look at history to see if a gesture occured */
00278   if ((ps[0] < PS_THRESHOLD) && (ps[1] < PS_THRESHOLD) && (ps[2] < PS_THRESHOLD))
00279   {
00280     /* If the ps_max values are high enough and there exit entry and exit times, */
00281     /*   then begin processing gestures */
00282     if ((ps_entry_time[0] != 0) && (ps_entry_time[1] != 0) && (ps_entry_time[2] != 0)
00283         && (ps_exit_time[0] != 0) && (ps_exit_time[1] != 0) && (ps_exit_time[2] != 0))
00284     {
00285       /* Make sure no timestamps overflowed, indicated possibility if any of them are close to overflowing */
00286       if ((ps_exit_time[0] > 0xFC000000L) || (ps_exit_time[1] > 0xFC000000L) || (ps_exit_time[2] > 0xFC000000L)
00287           || (ps_entry_time[0] > 0xFC000000L) || (ps_entry_time[1] > 0xFC000000L) || (ps_entry_time[2] > 0xFC000000L))
00288       {         /* If any of them are close to overflowing, overflow them all so they all have the same reference */
00289         ps_exit_time[0] += 0x1FFFFFFFL;
00290         ps_exit_time[1] += 0x1FFFFFFFL;
00291         ps_exit_time[2] += 0x1FFFFFFFL;
00292 
00293         ps_entry_time[0] += 0x1FFFFFFFL;
00294         ps_entry_time[1] += 0x1FFFFFFFL;
00295         ps_entry_time[2] += 0x1FFFFFFFL;
00296       }
00297 
00298       /* Calculate the midpoint (between entry and exit times) of each waveform */
00299       /*  the order of these midpoints helps determine the gesture */
00300       ps_time[0] = (ps_exit_time[0] - ps_entry_time[0]) / 2;
00301       ps_time[0] = ps_time[0] + ps_entry_time[0];
00302 
00303       ps_time[1] = (ps_exit_time[1] - ps_entry_time[1]) / 2;
00304       ps_time[1] = ps_time[1] + ps_entry_time[1];
00305 
00306       ps_time[2] = (ps_exit_time[2] - ps_entry_time[2]) / 2;
00307       ps_time[2] = ps_time[2] + ps_entry_time[2];
00308 
00309       /* The diff_x and diff_y values help determine a gesture by comparing the */
00310       /*  LED measurements that are on a single axis */
00311       if (ps_time[1] > ps_time[2])
00312       {
00313         diff_x = ps_time[1] - ps_time[2];
00314       }
00315       else
00316       {
00317         diff_x = ps_time[2] - ps_time[1];
00318       }
00319       if( ps_time[0] > ps_time[1] )
00320       {
00321         diff_y1 = ps_time[0] - ps_time[1];
00322       }
00323       else
00324       {
00325         diff_y1 = ps_time[1] - ps_time[0];
00326       }
00327 
00328       if( ps_time[0] > ps_time[2] )
00329       {
00330         diff_y2 = ps_time[0] - ps_time[2];
00331       }
00332       else
00333       {
00334         diff_y2 = ps_time[2] - ps_time[0];
00335       }
00336 
00337 
00338       /* Take the average of all three midpoints to make a comparison point for each midpoint */
00339       ps_avg = (u32) ps_time[0] + (u32) ps_time[1] + (u32) ps_time[2];
00340       ps_avg = ps_avg / 3;
00341 
00342       if ((ps_exit_time[0] - ps_entry_time[0]) > 10 || (ps_exit_time[1] - ps_entry_time[1]) > 10 || (ps_exit_time[2] - ps_entry_time[2]) > 10)
00343       {
00344         if( ( (ps_time[0] < ps_time[1]) &&  (diff_y1 > diff_x) ) || ( (ps_time[0] <= ps_time[2]) && (diff_y2 > diff_x) ) )
00345         {           /* An up gesture occured if the bottom LED had its midpoint first */
00346           ret = UP;
00347         }
00348         else if  ( ( (ps_time[0] < ps_time[1]) &&  (diff_y1 > diff_x) ) || ( (ps_time[0] > ps_time[2]) && (diff_y2 > diff_x) ) )
00349         {           /* A down gesture occured if the bottom LED had its midpoint last */
00350           ret = DOWN;
00351         }
00352         else if((ps_time[0] < ps_time[1]) && (ps_time[2] < ps_time[1]) && (diff_x > ((diff_y1+diff_y2)/2)))
00353         {           /* A left gesture occured if the left LED had its midpoint last */
00354           ret = LEFT;
00355         }
00356         else if( (ps_time[0] < ps_time[2]) && (ps_time[1] < ps_time[2])  && (diff_x > ((diff_y1+diff_y2)/2)))
00357         {           /* A right gesture occured if the right LED had midpoint later than the right LED */
00358           ret = RIGHT;
00359         }
00360       }
00361     }
00362     for (array_counter = 0; array_counter < 3; array_counter++)
00363     {
00364       ps_exit_time[array_counter]  = 0;
00365       ps_entry_time[array_counter] = 0;
00366     }
00367   }
00368 
00369   return ret;
00370 }
00371 
00372 
00373 /**************************************************************************/
00386 int Si1147_ConfigureDetection(I2C_TypeDef *i2c, uint8_t addr, int lowpower)
00387 {
00388   s16          retval = 0;
00389   SI114X_CAL_S si114x_cal;
00390   si114x_handle->addr = addr;
00391   si114x_handle->i2c  = i2c;
00392 
00393 
00394   /* Note that the Si114xReset() actually performs the following functions: */
00395   /*     1. Pauses all prior measurements */
00396   /*     2. Clear  i2c registers that need to be cleared */
00397   /*     3. Clears irq status to make sure INT* is negated */
00398   /*     4. Delays 10 ms */
00399   /*     5. Sends HW Key */
00400   retval += Si114xReset(si114x_handle);
00401 
00402   retval += Si114xWriteToRegister(si114x_handle, REG_PS_LED21, (LED1I << 4) + LED2I);
00403   retval += Si114xWriteToRegister(si114x_handle, REG_PS_LED3, LED3I);
00404 
00405 
00406 
00407   /* UV Coefficients */
00408   si114x_get_calibration(si114x_handle, &si114x_cal, 0);
00409   si114x_set_ucoef(si114x_handle, 0, &si114x_cal);
00410 
00411 
00412 
00413   retval += Si114xParamSet(si114x_handle, PARAM_CH_LIST, GESTURE_TASKLIST | UV_TASKLIST);
00414 
00415   retval += Si114xWriteToRegister(si114x_handle, REG_IRQ_ENABLE, GESTURE_IRQ);
00416 
00417   retval += Si114xParamSet(si114x_handle, PARAM_PS_ADC_MISC, 0x24);  /* PS_ADC_MISC to high signal range */
00418   retval += Si114xParamSet(si114x_handle, PARAM_PS1_ADC_MUX, 0x00);  /* PS1_ADCMUX, PS2_ADCMUX, PS3_ADCMUX to small photodiode */
00419   retval += Si114xParamSet(si114x_handle, PARAM_PS2_ADC_MUX, 0x00);
00420   retval += Si114xParamSet(si114x_handle, PARAM_PS3_ADC_MUX, 0x00);
00421 
00422   /* Configure the ALS IR channel for the same settings as PS */
00423 
00424   retval += Si114xParamSet(si114x_handle, PARAM_ALSIR_ADC_MISC, RANGE_EN);
00425   retval += Si114xParamSet(si114x_handle, PARAM_ALSVIS_ADC_MISC, RANGE_EN);
00426 
00427   if (!lowpower)
00428   {
00429     /* Set up how often the device wakes up to make measurements (10ms) */
00430     retval += Si114xWriteToRegister(si114x_handle, REG_MEAS_RATE_MSB, (MEASRATE_FAST & 0xff00) >> 8);
00431     retval += Si114xWriteToRegister(si114x_handle, REG_MEAS_RATE_LSB, MEASRATE_FAST & 0x00ff);
00432     /* Enable Autonomous Operation */
00433     retval += Si114xPsAlsAuto(si114x_handle);
00434 
00435   }
00436 
00437   /* If nothing went wrong after all of this time, the value */
00438   /* returned will be 0. Otherwise, it will be some negative */
00439   /* number */
00440   return retval;
00441 }