si114x_functions.c

Go to the documentation of this file.
00001 /*************************************************************************/
00028 #include "si114x_functions.h"
00029 
00030 /*****************************************************************************/
00031 /**************    Compile Switches   ****************************************/
00032 /*****************************************************************************/
00033 
00034 #ifndef INCLUDE_SI114X_CALIBRATIONCODE
00035 /***************************************************************************/
00041 #define INCLUDE_SI114X_CALIBRATIONCODE   1
00042 #endif
00043 
00044 
00045 
00046 #ifndef INCLUDE_SI114X_COMPRESS_CODE
00047 /***************************************************************************/
00054 #define INCLUDE_SI114X_COMPRESS_CODE     1
00055 #endif
00056 
00057 #define LOOP_TIMEOUT_MS 200
00058 /***************************************************************************/
00062 static int16_t _waitUntilSleep(HANDLE si114x_handle)
00063 {
00064   int8_t  retval = -1;
00065   uint8_t count = 0;
00066   // This loops until the Si114x is known to be in its sleep state
00067   // or if an i2c error occurs
00068   while(count < LOOP_TIMEOUT_MS)
00069   {
00070     retval = Si114xReadFromRegister(si114x_handle, REG_CHIP_STAT);
00071     if(retval == 1) break;
00072     if(retval <  0) return retval;
00073     count++;
00074     delay_1ms();
00075   }
00076   return 0;
00077 }
00078 
00079 /***************************************************************************/
00090 int16_t Si114xReset(HANDLE si114x_handle)
00091 {
00092   int32_t retval = 0;
00093 
00094   //
00095   // Do not access the Si114x earlier than 25 ms from power-up. 
00096   // Uncomment the following lines if Si114xReset() is the first
00097   // instruction encountered, and if your system MCU boots up too 
00098   // quickly. 
00099   //
00100   delay_10ms();
00101   delay_10ms();
00102   delay_10ms();
00103 
00104   retval+=Si114xWriteToRegister(si114x_handle, REG_MEAS_RATE,  0x00);
00105   retval+=Si114xWriteToRegister(si114x_handle, REG_ALS_RATE,   0x00);
00106   retval+=Si114xPauseAll(si114x_handle);
00107 
00108   // The clearing of the registers could be redundant, but it is okay.
00109   // This is to make sure that these registers are cleared.
00110   retval+=Si114xWriteToRegister(si114x_handle, REG_MEAS_RATE,  0x00);
00111   retval+=Si114xWriteToRegister(si114x_handle, REG_IRQ_ENABLE, 0x00);
00112   retval+=Si114xWriteToRegister(si114x_handle, REG_IRQ_MODE1,  0x00);
00113   retval+=Si114xWriteToRegister(si114x_handle, REG_IRQ_MODE2,  0x00);
00114   retval+=Si114xWriteToRegister(si114x_handle, REG_INT_CFG  ,  0x00);
00115   retval+=Si114xWriteToRegister(si114x_handle, REG_IRQ_STATUS, 0xFF);
00116 
00117   // Perform the Reset Command
00118   retval+=Si114xWriteToRegister(si114x_handle, REG_COMMAND, 1);
00119 
00120   // Delay for 10 ms. This delay is needed to allow the Si114x
00121   // to perform internal reset sequence. 
00122   delay_10ms();
00123 
00124   // Write Hardware Key
00125   retval+=Si114xWriteToRegister(si114x_handle, REG_HW_KEY, HW_KEY_VAL0);
00126 
00127   return retval;
00128 }
00129 
00130 /***************************************************************************/
00134 static int16_t _sendCmd(HANDLE si114x_handle, uint8_t command)
00135 {
00136   int16_t  response; 
00137   int8_t   retval;
00138   uint8_t  count = 0; 
00139 
00140   // Get the response register contents
00141   response = Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
00142   if(response < 0)
00143     return response;
00144 
00145   // Double-check the response register is consistent
00146   while(count < LOOP_TIMEOUT_MS)
00147   {
00148     if((retval=_waitUntilSleep(si114x_handle)) != 0) return retval;
00149 
00150     if(command==0) break; // Skip if the command is NOP 
00151 
00152     retval=Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
00153     if(retval==response) break;
00154     else if(retval<0) return retval;
00155     else response = retval;
00156     count++;
00157   }
00158 
00159   // Send the Command
00160   if((retval=Si114xWriteToRegister(si114x_handle, REG_COMMAND, command)) !=0) 
00161     return retval;
00162 
00163   count = 0;
00164   // Expect a change in the response register
00165   while(count < LOOP_TIMEOUT_MS)
00166   {
00167     if(command==0) break; // Skip if the command is NOP
00168 
00169     retval= Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
00170     if(retval != response) break;
00171     else if(retval<0) return retval;
00172     count++;
00173     delay_1ms();
00174   }
00175   return 0;
00176 }
00177 
00178 /***************************************************************************/
00186 int16_t Si114xNop(HANDLE si114x_handle)
00187 {
00188   return _sendCmd(si114x_handle,0x00);
00189 }
00190 
00191 /***************************************************************************/
00201 int16_t Si114xPsForce(HANDLE si114x_handle)
00202 {
00203   return _sendCmd(si114x_handle,0x05);
00204 }
00205 
00206 /***************************************************************************/
00216 int16_t Si114xAlsForce(HANDLE si114x_handle)
00217 {
00218   return _sendCmd(si114x_handle,0x06);
00219 }
00220 
00221 /***************************************************************************/
00231 int16_t Si114xPsAlsForce(HANDLE si114x_handle)
00232 {
00233   return _sendCmd(si114x_handle,0x07);
00234 }
00235 
00236 /***************************************************************************/
00246 int16_t Si114xPsAlsAuto (HANDLE si114x_handle)
00247 {
00248   return _sendCmd(si114x_handle,0x0F);
00249 }
00250 
00251 /***************************************************************************/
00263 int16_t Si114xParamRead(HANDLE si114x_handle, uint8_t address)
00264 {
00265   // returns Parameter[address]
00266   int16_t retval;
00267   uint8_t cmd = 0x80 + (address & 0x1F);
00268 
00269   retval=_sendCmd(si114x_handle, cmd);
00270   if( retval != 0 ) return retval;
00271 
00272   retval = Si114xReadFromRegister(si114x_handle, REG_PARAM_RD);
00273   return retval;
00274 }
00275 
00276 /***************************************************************************/
00296 int16_t Si114xParamSet(HANDLE si114x_handle, uint8_t address, uint8_t value)
00297 {
00298   int16_t retval;
00299   uint8_t buffer[2];
00300   int16_t response_stored;
00301   int16_t response;
00302 
00303   if((retval = _waitUntilSleep(si114x_handle))!=0) return retval;
00304 
00305   response_stored = Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
00306 
00307   buffer[0]= value;
00308   buffer[1]= 0xA0 + (address & 0x1F);
00309 
00310   retval=Si114xBlockWrite(si114x_handle, REG_PARAM_WR, 2, ( uint8_t* ) buffer);
00311   if(retval != 0) return retval;
00312 
00313   // Wait for command to finish
00314   response = Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
00315   while(response == response_stored )
00316   {
00317     response = Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
00318     if (response == response_stored)
00319     {
00320       delay_1ms();
00321     }
00322   }
00323 
00324   if(retval < 0)
00325     return retval;
00326   else
00327     return 0;
00328 }
00329 
00330 /***************************************************************************/
00334 static int16_t _PsAlsPause (HANDLE si114x_handle) 
00335 {
00336   return _sendCmd(si114x_handle,0x0B);
00337 }
00338 
00339 /***************************************************************************/
00349 int16_t Si114xPauseAll(HANDLE si114x_handle)
00350 {
00351   uint8_t countA, countB;
00352   int8_t  retval;
00353 
00354 
00355   //  After a RESET, if the Si114x receives a command (including NOP) before the
00356   //  Si114x has gone to sleep, the chip hangs. This first while loop avoids 
00357   //  this.  The reading of the REG_CHIPSTAT does not disturb the internal MCU.
00358   //
00359 
00360    retval = 0; //initialize data so that we guarantee to enter the loop
00361    while(retval != 0x01)
00362    {    
00363      retval = Si114xReadFromRegister( si114x_handle, REG_CHIP_STAT);
00364      if (retval != 0x01)
00365      {
00366        delay_1ms();
00367      }
00368    }
00369 
00370   countA = 0;
00371   while(countA < LOOP_TIMEOUT_MS)
00372   {
00373     countB = 0;
00374     // Keep sending nops until the response is zero
00375     while(countB < LOOP_TIMEOUT_MS)
00376     {
00377       retval = Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
00378       if( retval == 0 )
00379           break;
00380       else 
00381       {
00382         // Send the NOP Command to clear any error...we cannot use Si114xNop()
00383         // because it first checks if REG_RESPONSE < 0 and if so it does not
00384         // perform the cmd. Since we have a saturation REG_RESPONSE will be <0
00385         Si114xWriteToRegister(si114x_handle, REG_COMMAND, 0x00);
00386       }
00387       countB++;
00388       delay_1ms();
00389     }
00390 
00391     // Pause the device
00392     _PsAlsPause(si114x_handle);
00393 
00394     countB = 0;
00395     // Wait for response
00396     while(countB < LOOP_TIMEOUT_MS)
00397     {
00398       retval = Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
00399       if( retval !=0 )
00400         break;
00401       countB++;
00402       delay_1ms();
00403     }
00404 
00405     // When the PsAlsPause() response is good, we expect it to be a '1'.
00406     retval = Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
00407     if( retval == 1 )
00408       break;  // otherwise, start over.
00409     countA++;
00410   }
00411   return 0;
00412 }
00413 
00414 /******************************************************************************/
00415 /***********   INCLUDE_SI114x_COMPRESS_CODE  **********************************/
00416 /******************************************************************************/
00417 #if( INCLUDE_SI114X_COMPRESS_CODE == 1 )
00418 //
00419 // The goal of uncompress is to arrive at a 16-bit value, when the input is a
00420 // single byte of information. 
00421 //
00422 // The approach taken here is to reuse the floating point concept, but apply it
00423 // to this. Just as it is possible to store relatively large numbers using an
00424 // IEEE 754 representation of a 32 bit value, we make use of a similar concept.
00425 //
00426 // In IEEE 754 representation, there consists of concept of a signed exponent,
00427 // and a signed significand. The signed exponent allows representation of
00428 // values between 2^127 to 2^-128. The signficand is also signed.
00429 //
00430 // The term 'significand' is the integer bit plus the fraction. The 'fraction'
00431 // is the fractional part of the significand.
00432 //
00433 //              IEEE Single Precision Format
00434 //
00435 //     |  b31   |   b30 to  b23   |  bit22 to bit0  |
00436 //     |  Sign  | Signed Exponent |   Fraction      |
00437 //
00438 // In what we need, we do not need signed exponents nor do we need signed
00439 // significands. So, we use an unsigned exponent representation and an unsigned
00440 // significand. 
00441 //
00442 // uncompress takes an input byte and interprets the first 4 bits as an 
00443 // exponent, and the last 4 bits as a fraction, with an implicit integer bit
00444 //
00445 // The mathematical representation is similar to the concept for floating point
00446 // numbers. First off, the bit field 7:4 is the Exponent, and the bit field 3:0
00447 // is the fractional part of the significand.
00448 //
00449 //
00450 //      | b7 b6 b5 b4 | b3 b2 b1 b0 |
00451 //      |   unsigned  |             |
00452 //      |   Exponent  |  Fraction   |
00453 //
00454 // The number representation is:
00455 //
00456 //      ( 2 ^ Exponent ) * 1.Fraction
00457 //
00458 // Note the 'implicit integer bit'. Normally, the hidden integer is 1. However,
00459 // there is an exception. If the Exponent is zero, the representation
00460 // becomes the following:
00461 //
00462 //      ( 2 ^ 0 ) * 0.Fraction
00463 //
00464 // This is the concept called the 'denormalized number' identical to the IEEE
00465 // 754 representation of floating point numbers. Concept isn't new... this
00466 // allows us to represent the value 0.
00467 //
00468 // Let's go through one example...
00469 //
00470 // Let's say input is 0x9A.
00471 //
00472 //     Exponent = 9
00473 //     Fraction = A 
00474 //
00475 // Since the Exponent is non-zero, the number representation is:
00476 //
00477 //     2 ^ 9 * (1.1010)
00478 //
00479 // So, we take 1.1010 and shift left by 9 positions. It is best illustrated in
00480 // binary...
00481 //
00482 //    1.1010 << 9 = 1 1010 00000 = 0x340
00483 //
00484 // The main advantage is that it allows a very large range dynamic range 
00485 // to be represented in 8 bits. The largest number that can be represented 
00486 // is 0xFF, and this translates to:
00487 //
00488 //     2 ^ 15 * 1.1111
00489 //
00490 //     1.1111 << 15 = 1111 1000 0000 0000 = 0xF800
00491 //
00492 // When the exponent is less than 4, notice that the fraction bits are
00493 // truncated. What this means is that there can be multiple ways of getting an
00494 // output from 0 to 
00495 // the value '0x0000' to 0x000F
00496 //
00497 // To illustrate the case where exponents are less than 4:
00498 //      Input     Output
00499 //      00        0000
00500 //      02        0000
00501 //      08        0001
00502 //      0A        0001
00503 //      10        0002
00504 //      14        0002
00505 //      18        0003
00506 //      1A        0003
00507 //      20        0004
00508 //      24        0005
00509 //      28        0006
00510 //      2c        0007
00511 //      30        0008
00512 //      32        0009
00513 //      34        000a
00514 //      36        000b
00515 //      38        000c
00516 //      3c        000e
00517 //      3e        000f
00518 //
00519 // At exponent of 4 or greater, the fraction bits are no longer being thrown
00520 // away, so, we now have linear values
00521 //      40        0010
00522 //      41        0011
00523 //      42        0012
00524 //      43        0013
00525 //      44        0014
00526 //
00527 // But alas, once the exponent is greater than 4, we now stuff the lower
00528 // fractional bits with zero, and we begin to skip numbers...
00529 //      50        0020
00530 //      51        0022
00531 //      52        0024
00532 //      53        0026
00533 //      54        0028
00534 //
00535 // Well...strictly speaking, the IEEE format treats the largest possible 
00536 // exponent as 'infinity' or NAN. Let's not go there... Denorm concept is useful
00537 // for us since it allows us to represent zero. However, infinity or NAN 
00538 // concepts are not useful for us. 
00539 //
00540 
00541 /***************************************************************************/
00551 uint16_t Uncompress(uint8_t input) // It is important for the input to be 
00552                                    // unsigned 8-bit.
00553 {
00554   uint16_t output   = 0;
00555   uint8_t  exponent = 0;
00556       
00557   // Handle denorm case where exponent is zero. In this case, we are
00558   // evaluating the value with the integer bit is zero (0.F). So, we round up
00559   // if the fraction represents a value of 1/2 or greater. Since the fraction
00560   // is 4 bits, an input of less than 8/16 is less than half. If less than
00561   // half, return zero. Otherwise, we know that we will return a 1 later.
00562   //
00563   if( input < 8 ) return 0;  
00564 
00565   //
00566   // At this point, the exponent is non-zero, so, put in the implicit
00567   // fraction. Note that when we get the input, it comes in already shifted
00568   // by 4. So, we are dealing with a value already 4 times larger than the
00569   // actual starting point.
00570   //
00571   // Never fear... we just make an adjustment to the exponent and shift
00572   // left/right accordingly. The result will be the same as the floating
00573   // point concept described above.
00574   //
00575   
00576   exponent = (input & 0xF0 ) >> 4;      // extracts the exponent
00577   output = 0x10  | (input & 0x0F);      // extracts the fraction and adds 
00578                                         // in the implicit integer
00579 
00580   if( exponent >= 4 ) return ( output << (exponent-4) );
00581   return( output >> (4-exponent) );
00582 }
00583 
00584 
00585 // --------------------------------------------------------------------
00586 // What if someone wants to do the inverse function? 
00587 //
00588 // Let's say we want to figure out what byte value best represents the number
00589 // of 32 KHz timer ticks for 500 ms.
00590 //
00591 // We start of by knowing how many 32 KHz cycles are in that given time period.
00592 // Let's say that we want to have the RTC wake up every 500 ms. 
00593 //
00594 //     500 ms * 32 KHz = 16000 cycles
00595 //
00596 // Then, we take the calculator, and find out what 64 looks like from a binary
00597 // value viewpoint. Using a hex calculator, we see that:
00598 //
00599 //     16000 = 11111010000000
00600 //
00601 //     ... in floating point representation...
00602 //
00603 //           = 11111010000000.00000
00604 //
00605 // The next step is to normalize the value. Normalizing the value means that 
00606 // we represent the value in 1.F format. We do this by moving the decimal value 
00607 // left until we get the 1.F representation. The number of times we move the 
00608 // decimal point left is the exponent. Since we need to move the decimal point 
00609 // left before we get to the 1.F represenation...
00610 //
00611 //     16000 = 2^13 * 1.1111010000000
00612 //
00613 // The exponent is therefore 13, and the digits to the right hand side of the
00614 // decimal point is the fraction. What we need is the the first four fractional
00615 // bits. The first four fraction bits is 1111. We truncate the rest,
00616 // unfortunately.
00617 //
00618 // Therefore, the nearest byte representation for 500 ms is 0xDF
00619 //
00620 // Notice that if you plugged in 0xDF into this uncompress function, you will 
00621 // get 496 ms. The reason we didn't quite get 500 ms is that we had to throw 
00622 // away the 6th fractional bit. 
00623 //     
00624 // Anyway, this leads us to the following function. This function takes in a
00625 // 16-bit value and compresses it.
00626 
00627 /***************************************************************************/
00639 uint8_t Compress(uint16_t input) // input should be a 16-bit unsigned value
00640 {
00641   uint32_t tmp         = 0;
00642   uint32_t exponent    = 0;
00643   uint32_t significand = 0;
00644 
00645   if(input==0)
00646     return 0;      
00647 
00648 
00649   // handle denorm cases
00650   // There are multiple answers to 0x0000 and 0x0001 input due to rounding
00651   // error introduced throught the gradual underflow
00652   //     Answer for 0x0000 is from 0x00 to 0x07
00653   //     Answer for 0x0001 is from 0x08 to 0x0F
00654   // We will just 'pick one' answer.
00655   if(input == 0x0000) return 0x00; 
00656   if(input == 0x0001) return 0x08; 
00657 
00658   // Now we have the denorm cases out of the way, the exponent should be at
00659   // least one at this point. 
00660   exponent = 0;
00661   tmp = input;
00662   while(1) 
00663   {
00664     tmp >>= 1;  // Shift until there is only the integer in the lease 
00665                 //  significant position 
00666     exponent += 1;
00667     if(tmp == 1) 
00668     {
00669       break;  // the integer bit has been found. Stop.
00670     }
00671   }
00672 
00673   // Once exponent is found, look for the four fractional bits.
00674   //
00675   // If the exponent is between 1 to 4, we do not need to do any kind of
00676   // fractional rounding. Take care of those cases first 
00677 
00678   if(exponent < 5) // shift left to align the significant and return the 
00679                     // result
00680   { 
00681     significand = ( input << (4 - exponent) ) ;
00682     return ( (exponent << 4) | (significand & 0xF));
00683   }
00684 
00685   // At this point, we need to calculate the fraction.
00686   //
00687   // Easiest way is to align the value so that we have the integer and
00688   // fraction bits at a known bit position.
00689   //
00690   // We then round the signficand to the nearest four fractional bits. To do
00691   // so, it is best that we also look at the 5th fractional bit and update
00692   // the 4th fractional bit as necessary. During rounding, it is possible for
00693   // a carry to occur. If this happens, simply add one to the exponent, and
00694   // shift the signficand by one to get to the same bit positioning. 
00695 
00696   significand = input >> (exponent - 5);
00697 
00698   //
00699   // After the shift, the significand looks like this since we shift the
00700   // value by 5 less than the exponent. This is what we expect at this point:
00701   //
00702   // bit[15:6]     bit5  bit4  bit3  bit2  bit1  bit0
00703   //
00704   //   zeroes       1    2^-1  2^-2  2^-3  2^-4  2^-5
00705   //
00706   //                ^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
00707   //               int            fraction        
00708   //
00709 
00710   if(significand & 1) // Check if we need to round up
00711   {                
00712     significand += 2;   // Increment the 4th fraction (in bit1 position)               
00713     
00714     // We then check if a carry occurred due to the addition. If a carry
00715     // did occur, it would have bumped up the number such that bit6 would
00716     // be set. Bit6 is 0x0040.
00717     if(significand & 0x0040)         // Check for a carry
00718     {       
00719       exponent += 1;                // A carry occurred. Increment the exponent
00720       significand >>= 1;            // shift the signficand right by one
00721     }
00722   }
00723 
00724   // Rounding is done... Encode value and return.
00725   return ( (exponent << 4) | ( (significand >> 1) & 0xF ) );  
00726 }
00727 #endif // INCLUDE_SI114X_COMPRESS_CODE
00728 
00729 /******************************************************************************/
00730 /***********   END INCLUDE_SI114X_COMPRESS_CDOE  ******************************/
00731 /******************************************************************************/
00732 
00733 /******************************************************************************/
00734 /***********   INCLUDE_SI114x_COMPRESS_CODE  **********************************/
00735 /******************************************************************************/
00737 // Start of Calibration Code addition
00738 #define FLT_TO_FX20(x)       ((int32_t)((x*1048576)+.5))
00739 #define FX20_ONE             FLT_TO_FX20( 1.000000)
00740 #define FX20_BAD_VALUE       0xffffffff
00741 
00742 
00743 #if(INCLUDE_SI114X_CALIBRATIONCODE == 1)
00744 
00745 //                                             msb   lsb   align
00746 //                                             i2c   i2c   ment
00747 //                                             addr  addr
00748 #define SIRPD_ADCHI_IRLED    (collect(buffer, 0x23, 0x22,  0))
00749 #define SIRPD_ADCLO_IRLED    (collect(buffer, 0x22, 0x25,  1))
00750 #define SIRPD_ADCLO_WHLED    (collect(buffer, 0x24, 0x26,  0))
00751 #define VISPD_ADCHI_WHLED    (collect(buffer, 0x26, 0x27,  1))
00752 #define VISPD_ADCLO_WHLED    (collect(buffer, 0x28, 0x29,  0))
00753 #define LIRPD_ADCHI_IRLED    (collect(buffer, 0x29, 0x2a,  1))
00754 #define LED_DRV65            (collect(buffer, 0x2b, 0x2c,  0))
00755 
00756 // This is for internal Silabs debug only. Keep it as it is as 
00757 // this automatically defines away the embedded debug code 
00758 #ifdef SI114x_CAL_DEBUG 
00759 #include  "Si114x_cal_debug.c"
00760 #else
00761 #define DEBUG_PRINT_OUTPUT
00762 #define DEBUG_PRINT_OUTPUT_2
00763 #define DEBUG_UCOEF
00764 #endif
00765 
00766 
00767 /***************************************************************************/
00771 struct cal_ref_t 
00772 {
00773   uint32_t sirpd_adchi_irled; 
00774   uint32_t sirpd_adclo_irled; 
00775   uint32_t sirpd_adclo_whled; 
00776   uint32_t vispd_adchi_whled; 
00777   uint32_t vispd_adclo_whled; 
00778   uint32_t lirpd_adchi_irled; 
00779   uint32_t ledi_65ma;         
00780   uint8_t  ucoef[4];          
00781 };
00782 
00783 /***************************************************************************/
00787 struct cal_ref_t calref[2] =
00788 {
00789   {
00790     FLT_TO_FX20( 4.021290),  // sirpd_adchi_irled
00791     FLT_TO_FX20(57.528500),  // sirpd_adclo_irled
00792     FLT_TO_FX20( 2.690010),  // sirpd_adclo_whled
00793     FLT_TO_FX20( 0.042903),  // vispd_adchi_whled
00794     FLT_TO_FX20( 0.633435),  // vispd_adclo_whled
00795     FLT_TO_FX20(23.902900),  // lirpd_adchi_irled
00796     FLT_TO_FX20(56.889300),  // ledi_65ma
00797     {0x7B, 0x6B, 0x01, 0x00} // default ucoef
00798   },
00799   {
00800     FLT_TO_FX20( 2.325484),  // sirpd_adchi_irled
00801     FLT_TO_FX20(33.541500),  // sirpd_adclo_irled
00802     FLT_TO_FX20( 1.693750),  // sirpd_adclo_whled
00803     FLT_TO_FX20( 0.026775),  // vispd_adchi_whled
00804     FLT_TO_FX20( 0.398443),  // vispd_adclo_whled
00805     FLT_TO_FX20(12.190900),  // lirpd_adchi_irled
00806     FLT_TO_FX20(56.558200),  // ledi_65ma
00807     {0xdb, 0x8f, 0x01, 0x00} // default ucoef
00808   }
00809 };
00810 
00811 /***************************************************************************/
00816 static uint32_t decode(uint32_t input)
00817 {
00818   int32_t  exponent, exponent_bias9;
00819   uint32_t mantissa;
00820 
00821   if(input==0) return 0.0;
00822 
00823   exponent_bias9 = (input & 0x0f00) >> 8;
00824   exponent       = exponent_bias9 - 9;
00825 
00826   mantissa       = input & 0x00ff; // fraction
00827   mantissa       |=        0x0100; // add in integer
00828 
00829   // representation in 12 bit integer, 20 bit fraction 
00830   mantissa       = mantissa << (12+exponent); 
00831   return mantissa;
00832 }
00833 
00834 /***************************************************************************/
00841 static uint32_t collect(uint8_t* buffer, 
00842                         uint8_t msb_addr, 
00843                         uint8_t lsb_addr, 
00844                         uint8_t alignment)
00845 {
00846   uint16_t value;
00847   uint8_t  msb_ind = msb_addr - 0x22;
00848   uint8_t  lsb_ind = lsb_addr - 0x22;
00849 
00850   if(alignment == 0)   
00851   {
00852     value =  buffer[msb_ind]<<4;
00853     value += buffer[lsb_ind]>>4;       
00854   }
00855   else
00856   {
00857     value =  buffer[msb_ind]<<8;
00858     value += buffer[lsb_ind];
00859     value &= 0x0fff;
00860   }
00861 
00862   if(   ( value == 0x0fff ) 
00863      || ( value == 0x0000 ) ) return FX20_BAD_VALUE;
00864   else return decode( value );
00865 }
00866 
00867 /***************************************************************************/
00873 static void shift_left(uint32_t* value_p, int8_t shift)
00874 {
00875   if(shift > 0) 
00876     *value_p = *value_p<<shift ;
00877   else 
00878     *value_p = *value_p>>(-shift) ;
00879 }
00880 
00882 #define ALIGN_LEFT   1
00883 #define ALIGN_RIGHT -1
00884 
00885 /***************************************************************************/
00891 static int8_t align( uint32_t* value_p, int8_t direction )
00892 {
00893   int8_t   local_shift, shift ;
00894   uint32_t mask;
00895   
00896   // Check invalid value_p and *value_p, return without shifting if bad.
00897   if( value_p  == NULL )  return 0;
00898   if( *value_p == 0 )     return 0;
00899 
00900   // Make sure direction is valid
00901   switch( direction )
00902   {
00903     case ALIGN_LEFT:
00904       local_shift =  1 ;
00905       mask  = 0x80000000L;
00906       break;
00907 
00908     case ALIGN_RIGHT:
00909       local_shift = -1 ;
00910       mask  = 0x00000001L;
00911       break;
00912 
00913     default:
00914       // Invalid direction, return without shifting
00915       return 0;
00916   }
00917 
00918   shift = 0;
00919   while(1)
00920   {
00921     if(*value_p & mask ) break;
00922     shift++;
00923     shift_left( value_p, local_shift );
00924   }
00925   return shift;
00926 }
00927 
00928 /***************************************************************************/
00934 #define FORCE_ROUND_16 1
00935 
00936 /***************************************************************************/
00946 static void fx20_round
00947 ( 
00948   uint32_t *value_p 
00949   #if !FORCE_ROUND_16
00950   , int8_t round 
00951   #endif
00952 )
00953 {
00954   int8_t  shift;
00955   
00956   #if FORCE_ROUND_16
00957     // Use the following to force round = 16
00958     uint32_t mask1  = 0xffff8000;
00959     uint32_t mask2  = 0xffff0000;
00960     uint32_t lsb    = 0x00008000;
00961   #else
00962     // Use the following if you want to routine to be
00963     // capable of rounding to something other than 16.
00964     uint32_t mask1  = ((2<<(round))-1)<<(31-(round));
00965     uint32_t mask2  = ((2<<(round-1))-1)<<(31-(round-1));
00966     uint32_t lsb    = mask1-mask2;
00967   #endif
00968   
00969   shift = align( value_p, ALIGN_LEFT );
00970   if( ( (*value_p)&mask1 ) == mask1 )
00971   {
00972     *value_p = 0x80000000;
00973     shift -= 1;
00974   }
00975   else
00976   {
00977     *value_p += lsb;
00978     *value_p &= mask2;
00979   }
00980 
00981   shift_left( value_p, -shift );
00982 }
00983 
00984 /***************************************************************************/
00989 struct operand_t
00990 {
00991   uint32_t op1;  
00992   uint32_t op2;  
00993 };
00994 
00995 /***************************************************************************/
00999 static uint32_t fx20_divide( struct operand_t* operand_p ) 
01000 {
01001   int8_t    numerator_sh=0, denominator_sh=0;
01002   uint32_t  result;
01003   uint32_t* numerator_p;
01004   uint32_t* denominator_p;
01005 
01006   if( operand_p == NULL ) return FX20_BAD_VALUE;
01007 
01008   numerator_p   = &operand_p->op1;
01009   denominator_p = &operand_p->op2;
01010 
01011   if(   (*numerator_p   == FX20_BAD_VALUE) 
01012       || (*denominator_p == FX20_BAD_VALUE) 
01013       || (*denominator_p == 0             ) ) return FX20_BAD_VALUE;
01014   
01015   fx20_round  ( numerator_p   );
01016   fx20_round  ( denominator_p );
01017   numerator_sh   = align ( numerator_p,   ALIGN_LEFT  );
01018   denominator_sh = align ( denominator_p, ALIGN_RIGHT );
01019 
01020   result = *numerator_p / ( (uint16_t)(*denominator_p) );
01021   shift_left( &result , 20-numerator_sh-denominator_sh );
01022 
01023   return result;
01024 } 
01025 
01026 /***************************************************************************/
01030 static uint32_t fx20_multiply( struct operand_t* operand_p )
01031 {
01032   uint32_t  result;
01033   int8_t    val1_sh, val2_sh;
01034   uint32_t* val1_p;
01035   uint32_t* val2_p;
01036 
01037   if( operand_p == NULL ) return FX20_BAD_VALUE;
01038 
01039   val1_p = &(operand_p->op1);
01040   val2_p = &(operand_p->op2);
01041 
01042   fx20_round( val1_p );
01043   fx20_round( val2_p );
01044 
01045   val1_sh = align( val1_p, ALIGN_RIGHT );
01046   val2_sh = align( val2_p, ALIGN_RIGHT );
01047 
01048 
01049   result = (uint32_t)( ( (uint32_t)(*val1_p) ) * ( (uint32_t)(*val2_p) ) );
01050   shift_left( &result, -20+val1_sh+val2_sh );
01051 
01052   return result;
01053 }
01054 
01055 /***************************************************************************/
01062 static int16_t find_cal_index( uint8_t* buffer )
01063 {
01064   int16_t index;
01065   uint8_t size;
01066 
01067   // buffer[12] is the LSB, buffer[13] is the MSB
01068   index = ( int16_t )( buffer[12] + ( (uint16_t)( buffer[13] ) << 8 ) );
01069 
01070   switch( index )
01071   {
01072     case -1:
01073       index = 0;
01074       break;
01075     case -2:
01076       index = 0;
01077       break;
01078     case -3:
01079       index = 1;
01080     default:
01081       index = -(4+index) ;
01082   }
01083 
01084   size = sizeof(calref)/sizeof(calref[0]);
01085 
01086   if( index < size )
01087   {
01088     return  index;
01089   }
01090   else
01091   {
01092     return -1;
01093   }
01094 }
01095 
01096 /***************************************************************************/
01100 static uint32_t vispd_correction(uint8_t* buffer)
01101 {
01102 
01103   struct operand_t op;
01104   uint32_t         result;
01105   int16_t          index = find_cal_index( buffer );
01106 
01107   if( index < 0 ) result = FX20_ONE;
01108 
01109   op.op1 = calref[ index ].vispd_adclo_whled; 
01110   op.op2 = VISPD_ADCLO_WHLED;
01111   result = fx20_divide( &op );
01112 
01113   if( result == FX20_BAD_VALUE ) result = FX20_ONE;
01114 
01115   return result;
01116 }
01117 
01118 /***************************************************************************/
01122 static uint32_t irpd_correction(uint8_t* buffer)
01123 {
01124   struct operand_t op;
01125   uint32_t         result;
01126   int16_t          index = find_cal_index( buffer );
01127 
01128   if( index < 0 ) result = FX20_ONE;
01129 
01130   // op.op1 = SIRPD_ADCLO_IRLED_REF; op.op2 = SIRPD_ADCLO_IRLED;
01131   op.op1 = calref[ index ].sirpd_adclo_irled; 
01132   op.op2 = SIRPD_ADCLO_IRLED;
01133   result = fx20_divide( &op );
01134 
01135   if( result == FX20_BAD_VALUE ) result = FX20_ONE;
01136 
01137   return result;
01138 }
01139 
01140 /***************************************************************************/
01146 static uint32_t adcrange_ratio(uint8_t* buffer)
01147 {
01148   struct operand_t op;
01149   uint32_t         result;
01150   
01151   op.op1 = SIRPD_ADCLO_IRLED  ; op.op2 = SIRPD_ADCHI_IRLED  ;
01152   result = fx20_divide( &op );
01153 
01154   if( result == FX20_BAD_VALUE ) result = FLT_TO_FX20( 14.5 ); 
01155 
01156   return result;
01157 }
01158 
01159 /***************************************************************************/
01164 static uint32_t irsize_ratio(uint8_t* buffer)
01165 {
01166   struct operand_t op;
01167   uint32_t         result;
01168 
01169   op.op1 = LIRPD_ADCHI_IRLED  ; op.op2 = SIRPD_ADCHI_IRLED  ;
01170 
01171   result = fx20_divide( &op );
01172 
01173   if( result == FX20_BAD_VALUE ) result = FLT_TO_FX20(  6.0 ); 
01174 
01175   return  result;
01176 }
01177 
01178 /***************************************************************************/
01183 static uint32_t ledi_ratio(uint8_t* buffer)
01184 {
01185 
01186   struct operand_t op;
01187   uint32_t         result;
01188   int16_t          index;
01189 
01190   index = find_cal_index( buffer );
01191   
01192   if( index < 0 ) result = FX20_ONE;
01193 
01194   // op.op1 = LED_DRV65_REF; op.op2 = LED_DRV65;
01195   op.op1 = calref[ index ].ledi_65ma; 
01196   op.op2 = LED_DRV65;
01197   result = fx20_divide( &op );
01198 
01199   if( result == FX20_BAD_VALUE ) result = FX20_ONE;
01200 
01201   return result;
01202 }
01203 
01204 /***************************************************************************/
01210 static int16_t si114x_get_cal_index( HANDLE si114x_handle, uint8_t* buffer )
01211 {
01212   int16_t retval;
01213   uint8_t response; 
01214 
01215   if( ( si114x_handle == NULL ) || ( buffer == NULL ) )
01216     return -1;
01217 
01218   // Check to make sure that the device is ready to receive commands
01219   do 
01220   {
01221     retval = Si114xNop( si114x_handle );
01222     if( retval != 0 ) return -1;
01223 
01224 
01225     response = Si114xReadFromRegister( si114x_handle, REG_RESPONSE );
01226     if (response != 0)
01227     {
01228       delay_1ms();
01229     }
01230   } while( response != 0 );
01231 
01232   // Retrieve the index
01233   retval = Si114xWriteToRegister( si114x_handle, REG_COMMAND, 0x11 );
01234   _waitUntilSleep(si114x_handle);
01235 
01236   if( retval != 0 ) return -1; 
01237 
01238   retval = Si114xBlockRead( si114x_handle, REG_PS1_DATA0, 2, &(buffer[12]) );
01239   if( retval != 0 ) return -1;
01240 
01241   return 0;
01242 }
01243 
01244 /***************************************************************************/
01287 /*
01288  * Side-effects:
01289  *     - Writes 0x11 to command reg to retrieve factory calibration values in 
01290  *       buffer[0] to buffer[11]
01291  *
01292  *     - Calls the various helper functions such as vispd_correction()
01293  *       irpd_correction, to populate the SI114X_CAL_S structure
01294  *
01295  *     - Writes 0x12 to command reg to retrieve factory cal_index to 
01296  *       buffer[12] to buffer[13]
01297  ******************************************************************************/
01298 int16_t si114x_get_calibration( HANDLE        si114x_handle, 
01299                                 SI114X_CAL_S* si114x_cal, 
01300                                 uint8_t       security)
01301 {
01302   uint8_t buffer[14];
01303   int16_t retval = 0;
01304   uint8_t response;
01305 
01306   if( si114x_handle == NULL ) { retval = -4; goto error_exit; }
01307     
01308   if( si114x_cal    == NULL ) { retval = -4; goto error_exit; }
01309 
01310   // if requested, check to make sure the interface registers are zero
01311   // as an indication of a device that has not started any autonomous
01312   // operation
01313   if( security == 1 )
01314   {
01315     int8_t i;
01316 
01317     retval = Si114xBlockRead( si114x_handle, REG_ALS_VIS_DATA0, 12, buffer );
01318     if( retval != 0 ) { retval = -2; goto error_exit; }
01319 
01320     for( i=0; i<12; i++) 
01321     {
01322         if( buffer[i] != 0 ) { retval = -1; goto error_exit; }
01323     }
01324 
01325     DEBUG_PRINT_OUTPUT;
01326 
01327   }
01328 
01329   // Check to make sure that the device is ready to receive commands
01330   do 
01331   {
01332     retval = Si114xNop( si114x_handle );
01333     if( retval != 0 ) { retval = -2; goto error_exit; }
01334 
01335 
01336     response = Si114xReadFromRegister( si114x_handle, REG_RESPONSE );
01337     if (response != 0)
01338     {
01339       delay_1ms();
01340     }
01341   } while( response != 0 );
01342 
01343   // Request for the calibration data
01344   retval = Si114xWriteToRegister( si114x_handle, REG_COMMAND, 0x12 );
01345   _waitUntilSleep(si114x_handle);
01346 
01347   if( retval != 0 ) { retval = -2; goto error_exit; }
01348 
01349   // Wait for the response register to increment
01350   do 
01351   {
01352     response = Si114xReadFromRegister( si114x_handle, REG_RESPONSE );
01353     // If the upper nibbles are non-zero, something is wrong
01354     if( response == 0x80 )
01355     {
01356       // calibration code has not been implemented on this device
01357       // leading to command error. So, rather than returning an
01358       // error, handle the error by Nop and set ratios to -1.0
01359       // and return normally.
01360       Si114xNop( si114x_handle );
01361       retval = -3;
01362       goto error_exit;
01363     }
01364     else if( response & 0xfff0 ) 
01365     {
01366       // if upper nibble is anything but 0x80, exit with an error
01367       retval = -2; 
01368       goto error_exit;
01369     }
01370     if (response != 1)
01371     {
01372       delay_1ms();
01373     }
01374   } while( response != 1 );
01375 
01376   // Retrieve the 12 bytes from the interface registers
01377   retval = Si114xBlockRead( si114x_handle, REG_ALS_VIS_DATA0, 12, buffer );
01378   if( retval != 0 ) { retval = -2; goto error_exit; }
01379 
01380   DEBUG_PRINT_OUTPUT;
01381 
01382   retval=si114x_get_cal_index( si114x_handle, buffer );
01383 
01384   if( retval != 0 ) 
01385   {
01386     retval = -2; goto error_exit;
01387   }
01388 
01389   si114x_cal->ledi_ratio       = ledi_ratio(buffer);
01390   si114x_cal->vispd_correction = vispd_correction(buffer);
01391   si114x_cal->irpd_correction  = irpd_correction(buffer);
01392   si114x_cal->adcrange_ratio   = adcrange_ratio(buffer);
01393   si114x_cal->ucoef_p          = calref[find_cal_index(buffer)].ucoef;
01394   si114x_cal->irsize_ratio     = irsize_ratio(buffer);
01395 
01396   DEBUG_PRINT_OUTPUT_2;                                
01397 
01398   return 0; 
01399 
01400 error_exit:
01401   si114x_cal->vispd_correction = FX20_ONE;
01402   si114x_cal->irpd_correction  = FX20_ONE;
01403   si114x_cal->adcrange_ratio   = FLT_TO_FX20( 14.5 );
01404   si114x_cal->irsize_ratio     = FLT_TO_FX20(  6.0 ); 
01405   si114x_cal->ledi_ratio       = FX20_ONE;
01406   si114x_cal->ucoef_p          = NULL;
01407   return retval;
01408 }
01409 
01410 /***************************************************************************/
01438 int16_t si114x_set_ucoef( HANDLE        si114x_handle, 
01439                           uint8_t*      input_ucoef, 
01440                           SI114X_CAL_S* si114x_cal )
01441 {
01442   int8_t           response;
01443   uint8_t          temp;
01444   uint32_t         vc=FX20_ONE, ic=FX20_ONE, long_temp;
01445   struct operand_t op;
01446   uint8_t*         ref_ucoef = si114x_cal->ucoef_p;
01447   uint8_t          out_ucoef[4];
01448   
01449   if( input_ucoef != NULL ) ref_ucoef = input_ucoef;
01450   
01451   if( ref_ucoef == NULL )   return -1 ;
01452 
01453   // retrieve part identification
01454   response = Si114xReadFromRegister( si114x_handle, REG_PART_ID );
01455   switch( response )
01456   {
01457     case 0x32: case 0x45: case 0x46: case 0x47: temp = 1; break;
01458     default:                                    temp = 0; break;
01459   }
01460   if( !temp ) return -1;
01461 
01462   if(si114x_cal != 0)
01463   {
01464     if(si114x_cal->vispd_correction > 0) vc = si114x_cal->vispd_correction; 
01465     if(si114x_cal->irpd_correction  > 0) ic = si114x_cal->irpd_correction;
01466   }
01467 
01468   op.op1 = ref_ucoef[0] + ((ref_ucoef[1])<<8);
01469   op.op2 = vc;
01470   long_temp   = fx20_multiply( &op );
01471   out_ucoef[0] = (long_temp & 0x00ff);
01472   out_ucoef[1] = (long_temp & 0xff00)>>8;
01473 
01474   op.op1 = ref_ucoef[2] + (ref_ucoef[3]<<8);
01475   op.op2 = ic;
01476   long_temp   = fx20_multiply( &op );
01477   out_ucoef[2] = (long_temp & 0x00ff);
01478   out_ucoef[3] = (long_temp & 0xff00)>>8;
01479 
01480   DEBUG_UCOEF
01481 
01482   response = Si114xBlockWrite( si114x_handle, REG_UCOEF0 , 4, out_ucoef);
01483 
01484   return response;
01485 }
01486 #else // INCLUDE_SI114X_CALIBRATION_CODE
01487 
01488 /********************   STUB FUNCTIONS ONLY  **********************************/
01489 
01490 int16_t si114x_get_calibration( HANDLE        si114x_handle, 
01491                                 SI114X_CAL_S* si114x_cal, 
01492                                 uint8_t       security)
01493 {
01494   // although the SI114x_CAL_S structure is not filled up properly, the 
01495   // set_ucoef() function will not use it.
01496   return 0;
01497 }
01498 
01499 int16_t si114x_set_ucoef( HANDLE        si114x_handle, 
01500                           uint8_t*      input_ucoef, 
01501                           SI114X_CAL_S* si114x_cal )
01502 {
01503   int16_t response;
01504   uint8_t code ucoef[4] = { 0x7B, 0x6B, 0x01, 0x00 } ;
01505     
01506   // This will write 4 bytes starting with I2C address 0x13
01507   response = Si114xBlockWrite( si114x_handle, REG_UCOEF0, 4, &ucoef[0] );
01508   return response;
01509 }
01510 #endif // INCLUDE_SI114X_CALIBRATION_CODE
01511 
01512 /******************************************************************************/
01513 /***********   END INCLUDE_SI114x_COMPRESS_CODE  ******************************/
01514 /******************************************************************************/
01515