si114x_algorithm.c

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