EFM32 Happy Gecko Software Documentation  efm32hg-doc-5.1.2
si114x_functions.c
Go to the documentation of this file.
1 /*************************************************************************/
28 #include "si114x_functions.h"
29 
30 /*****************************************************************************/
31 /************** Compile Switches ****************************************/
32 /*****************************************************************************/
33 
34 #ifndef INCLUDE_SI114X_CALIBRATIONCODE
35 /***************************************************************************/
41 #define INCLUDE_SI114X_CALIBRATIONCODE 1
42 #endif
43 
44 
45 
46 #ifndef INCLUDE_SI114X_COMPRESS_CODE
47 /***************************************************************************/
54 #define INCLUDE_SI114X_COMPRESS_CODE 1
55 #endif
56 
58 #define LOOP_TIMEOUT_MS 200
59 /***************************************************************************/
63 static int16_t _waitUntilSleep(HANDLE si114x_handle)
64 {
65  int8_t retval = -1;
66  uint8_t count = 0;
67  // This loops until the Si114x is known to be in its sleep state
68  // or if an i2c error occurs
69  while(count < LOOP_TIMEOUT_MS)
70  {
71  retval = Si114xReadFromRegister(si114x_handle, REG_CHIP_STAT);
72  if(retval == 1) break;
73  if(retval < 0) return retval;
74  count++;
75  delay_1ms();
76  }
77  return 0;
78 }
81 /***************************************************************************/
92 int16_t Si114xReset(HANDLE si114x_handle)
93 {
94  int32_t retval = 0;
95 
96  //
97  // Do not access the Si114x earlier than 25 ms from power-up.
98  // Uncomment the following lines if Si114xReset() is the first
99  // instruction encountered, and if your system MCU boots up too
100  // quickly.
101  //
102  delay_10ms();
103  delay_10ms();
104  delay_10ms();
105 
106  retval+=Si114xWriteToRegister(si114x_handle, REG_MEAS_RATE, 0x00);
107  retval+=Si114xWriteToRegister(si114x_handle, REG_ALS_RATE, 0x00);
108  retval+=Si114xPauseAll(si114x_handle);
109 
110  // The clearing of the registers could be redundant, but it is okay.
111  // This is to make sure that these registers are cleared.
112  retval+=Si114xWriteToRegister(si114x_handle, REG_MEAS_RATE, 0x00);
113  retval+=Si114xWriteToRegister(si114x_handle, REG_IRQ_ENABLE, 0x00);
114  retval+=Si114xWriteToRegister(si114x_handle, REG_IRQ_MODE1, 0x00);
115  retval+=Si114xWriteToRegister(si114x_handle, REG_IRQ_MODE2, 0x00);
116  retval+=Si114xWriteToRegister(si114x_handle, REG_INT_CFG , 0x00);
117  retval+=Si114xWriteToRegister(si114x_handle, REG_IRQ_STATUS, 0xFF);
118 
119  // Perform the Reset Command
120  retval+=Si114xWriteToRegister(si114x_handle, REG_COMMAND, 1);
121 
122  // Delay for 10 ms. This delay is needed to allow the Si114x
123  // to perform internal reset sequence.
124  delay_10ms();
125 
126  // Write Hardware Key
127  retval+=Si114xWriteToRegister(si114x_handle, REG_HW_KEY, HW_KEY_VAL0);
128 
129  return retval;
130 }
131 
133 /***************************************************************************/
137 static int16_t _sendCmd(HANDLE si114x_handle, uint8_t command)
138 {
139  int16_t response;
140  int8_t retval;
141  uint8_t count = 0;
142 
143  // Get the response register contents
144  response = Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
145  if(response < 0)
146  return response;
147 
148  // Double-check the response register is consistent
149  while(count < LOOP_TIMEOUT_MS)
150  {
151  if((retval=_waitUntilSleep(si114x_handle)) != 0) return retval;
152 
153  if(command==0) break; // Skip if the command is NOP
154 
155  retval=Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
156  if(retval==response) break;
157  else if(retval<0) return retval;
158  else response = retval;
159  count++;
160  }
161 
162  // Send the Command
163  if((retval=Si114xWriteToRegister(si114x_handle, REG_COMMAND, command)) !=0)
164  return retval;
165 
166  count = 0;
167  // Expect a change in the response register
168  while(count < LOOP_TIMEOUT_MS)
169  {
170  if(command==0) break; // Skip if the command is NOP
171 
172  retval= Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
173  if(retval != response) break;
174  else if(retval<0) return retval;
175  count++;
176  delay_1ms();
177  }
178  return 0;
179 }
182 /***************************************************************************/
190 int16_t Si114xNop(HANDLE si114x_handle)
191 {
192  return _sendCmd(si114x_handle,0x00);
193 }
194 
195 /***************************************************************************/
205 int16_t Si114xPsForce(HANDLE si114x_handle)
206 {
207  return _sendCmd(si114x_handle,0x05);
208 }
209 
210 /***************************************************************************/
220 int16_t Si114xAlsForce(HANDLE si114x_handle)
221 {
222  return _sendCmd(si114x_handle,0x06);
223 }
224 
225 /***************************************************************************/
235 int16_t Si114xPsAlsForce(HANDLE si114x_handle)
236 {
237  return _sendCmd(si114x_handle,0x07);
238 }
239 
240 /***************************************************************************/
250 int16_t Si114xPsAlsAuto (HANDLE si114x_handle)
251 {
252  return _sendCmd(si114x_handle,0x0F);
253 }
254 
255 /***************************************************************************/
267 int16_t Si114xParamRead(HANDLE si114x_handle, uint8_t address)
268 {
269  // returns Parameter[address]
270  int16_t retval;
271  uint8_t cmd = 0x80 + (address & 0x1F);
272 
273  retval=_sendCmd(si114x_handle, cmd);
274  if( retval != 0 ) return retval;
275 
276  retval = Si114xReadFromRegister(si114x_handle, REG_PARAM_RD);
277  return retval;
278 }
279 
280 /***************************************************************************/
300 int16_t Si114xParamSet(HANDLE si114x_handle, uint8_t address, uint8_t value)
301 {
302  int16_t retval;
303  uint8_t buffer[2];
304  int16_t response_stored;
305  int16_t response;
306 
307  if((retval = _waitUntilSleep(si114x_handle))!=0) return retval;
308 
309  response_stored = Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
310 
311  buffer[0]= value;
312  buffer[1]= 0xA0 + (address & 0x1F);
313 
314  retval=Si114xBlockWrite(si114x_handle, REG_PARAM_WR, 2, ( uint8_t* ) buffer);
315  if(retval != 0) return retval;
316 
317  // Wait for command to finish
318  response = Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
319  while(response == response_stored )
320  {
321  response = Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
322  if (response == response_stored)
323  {
324  delay_1ms();
325  }
326  }
327 
328  if(retval < 0)
329  return retval;
330  else
331  return 0;
332 }
333 
335 /***************************************************************************/
339 static int16_t _PsAlsPause (HANDLE si114x_handle)
340 {
341  return _sendCmd(si114x_handle,0x0B);
342 }
345 /***************************************************************************/
355 int16_t Si114xPauseAll(HANDLE si114x_handle)
356 {
357  uint8_t countA, countB;
358  int8_t retval;
359 
360 
361  // After a RESET, if the Si114x receives a command (including NOP) before the
362  // Si114x has gone to sleep, the chip hangs. This first while loop avoids
363  // this. The reading of the REG_CHIPSTAT does not disturb the internal MCU.
364  //
365 
366  retval = 0; //initialize data so that we guarantee to enter the loop
367  while(retval != 0x01)
368  {
369  retval = Si114xReadFromRegister( si114x_handle, REG_CHIP_STAT);
370  if (retval != 0x01)
371  {
372  delay_1ms();
373  }
374  }
375 
376  countA = 0;
377  while(countA < LOOP_TIMEOUT_MS)
378  {
379  countB = 0;
380  // Keep sending nops until the response is zero
381  while(countB < LOOP_TIMEOUT_MS)
382  {
383  retval = Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
384  if( retval == 0 )
385  break;
386  else
387  {
388  // Send the NOP Command to clear any error...we cannot use Si114xNop()
389  // because it first checks if REG_RESPONSE < 0 and if so it does not
390  // perform the cmd. Since we have a saturation REG_RESPONSE will be <0
391  Si114xWriteToRegister(si114x_handle, REG_COMMAND, 0x00);
392  }
393  countB++;
394  delay_1ms();
395  }
396 
397  // Pause the device
398  _PsAlsPause(si114x_handle);
399 
400  countB = 0;
401  // Wait for response
402  while(countB < LOOP_TIMEOUT_MS)
403  {
404  retval = Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
405  if( retval !=0 )
406  break;
407  countB++;
408  delay_1ms();
409  }
410 
411  // When the PsAlsPause() response is good, we expect it to be a '1'.
412  retval = Si114xReadFromRegister(si114x_handle, REG_RESPONSE);
413  if( retval == 1 )
414  break; // otherwise, start over.
415  countA++;
416  }
417  return 0;
418 }
419 
420 /******************************************************************************/
421 /*********** INCLUDE_SI114x_COMPRESS_CODE **********************************/
422 /******************************************************************************/
423 #if( INCLUDE_SI114X_COMPRESS_CODE == 1 )
424 //
425 // The goal of uncompress is to arrive at a 16-bit value, when the input is a
426 // single byte of information.
427 //
428 // The approach taken here is to reuse the floating point concept, but apply it
429 // to this. Just as it is possible to store relatively large numbers using an
430 // IEEE 754 representation of a 32 bit value, we make use of a similar concept.
431 //
432 // In IEEE 754 representation, there consists of concept of a signed exponent,
433 // and a signed significand. The signed exponent allows representation of
434 // values between 2^127 to 2^-128. The signficand is also signed.
435 //
436 // The term 'significand' is the integer bit plus the fraction. The 'fraction'
437 // is the fractional part of the significand.
438 //
439 // IEEE Single Precision Format
440 //
441 // | b31 | b30 to b23 | bit22 to bit0 |
442 // | Sign | Signed Exponent | Fraction |
443 //
444 // In what we need, we do not need signed exponents nor do we need signed
445 // significands. So, we use an unsigned exponent representation and an unsigned
446 // significand.
447 //
448 // uncompress takes an input byte and interprets the first 4 bits as an
449 // exponent, and the last 4 bits as a fraction, with an implicit integer bit
450 //
451 // The mathematical representation is similar to the concept for floating point
452 // numbers. First off, the bit field 7:4 is the Exponent, and the bit field 3:0
453 // is the fractional part of the significand.
454 //
455 //
456 // | b7 b6 b5 b4 | b3 b2 b1 b0 |
457 // | unsigned | |
458 // | Exponent | Fraction |
459 //
460 // The number representation is:
461 //
462 // ( 2 ^ Exponent ) * 1.Fraction
463 //
464 // Note the 'implicit integer bit'. Normally, the hidden integer is 1. However,
465 // there is an exception. If the Exponent is zero, the representation
466 // becomes the following:
467 //
468 // ( 2 ^ 0 ) * 0.Fraction
469 //
470 // This is the concept called the 'denormalized number' identical to the IEEE
471 // 754 representation of floating point numbers. Concept isn't new... this
472 // allows us to represent the value 0.
473 //
474 // Let's go through one example...
475 //
476 // Let's say input is 0x9A.
477 //
478 // Exponent = 9
479 // Fraction = A
480 //
481 // Since the Exponent is non-zero, the number representation is:
482 //
483 // 2 ^ 9 * (1.1010)
484 //
485 // So, we take 1.1010 and shift left by 9 positions. It is best illustrated in
486 // binary...
487 //
488 // 1.1010 << 9 = 1 1010 00000 = 0x340
489 //
490 // The main advantage is that it allows a very large range dynamic range
491 // to be represented in 8 bits. The largest number that can be represented
492 // is 0xFF, and this translates to:
493 //
494 // 2 ^ 15 * 1.1111
495 //
496 // 1.1111 << 15 = 1111 1000 0000 0000 = 0xF800
497 //
498 // When the exponent is less than 4, notice that the fraction bits are
499 // truncated. What this means is that there can be multiple ways of getting an
500 // output from 0 to
501 // the value '0x0000' to 0x000F
502 //
503 // To illustrate the case where exponents are less than 4:
504 // Input Output
505 // 00 0000
506 // 02 0000
507 // 08 0001
508 // 0A 0001
509 // 10 0002
510 // 14 0002
511 // 18 0003
512 // 1A 0003
513 // 20 0004
514 // 24 0005
515 // 28 0006
516 // 2c 0007
517 // 30 0008
518 // 32 0009
519 // 34 000a
520 // 36 000b
521 // 38 000c
522 // 3c 000e
523 // 3e 000f
524 //
525 // At exponent of 4 or greater, the fraction bits are no longer being thrown
526 // away, so, we now have linear values
527 // 40 0010
528 // 41 0011
529 // 42 0012
530 // 43 0013
531 // 44 0014
532 //
533 // But alas, once the exponent is greater than 4, we now stuff the lower
534 // fractional bits with zero, and we begin to skip numbers...
535 // 50 0020
536 // 51 0022
537 // 52 0024
538 // 53 0026
539 // 54 0028
540 //
541 // Well...strictly speaking, the IEEE format treats the largest possible
542 // exponent as 'infinity' or NAN. Let's not go there... Denorm concept is useful
543 // for us since it allows us to represent zero. However, infinity or NAN
544 // concepts are not useful for us.
545 //
546 
547 /***************************************************************************/
557 uint16_t Uncompress(uint8_t input) // It is important for the input to be
558  // unsigned 8-bit.
559 {
560  uint16_t output = 0;
561  uint8_t exponent = 0;
562 
563  // Handle denorm case where exponent is zero. In this case, we are
564  // evaluating the value with the integer bit is zero (0.F). So, we round up
565  // if the fraction represents a value of 1/2 or greater. Since the fraction
566  // is 4 bits, an input of less than 8/16 is less than half. If less than
567  // half, return zero. Otherwise, we know that we will return a 1 later.
568  //
569  if( input < 8 ) return 0;
570 
571  //
572  // At this point, the exponent is non-zero, so, put in the implicit
573  // fraction. Note that when we get the input, it comes in already shifted
574  // by 4. So, we are dealing with a value already 4 times larger than the
575  // actual starting point.
576  //
577  // Never fear... we just make an adjustment to the exponent and shift
578  // left/right accordingly. The result will be the same as the floating
579  // point concept described above.
580  //
581 
582  exponent = (input & 0xF0 ) >> 4; // extracts the exponent
583  output = 0x10 | (input & 0x0F); // extracts the fraction and adds
584  // in the implicit integer
585 
586  if( exponent >= 4 ) return ( output << (exponent-4) );
587  return( output >> (4-exponent) );
588 }
589 
590 
591 // --------------------------------------------------------------------
592 // What if someone wants to do the inverse function?
593 //
594 // Let's say we want to figure out what byte value best represents the number
595 // of 32 KHz timer ticks for 500 ms.
596 //
597 // We start of by knowing how many 32 KHz cycles are in that given time period.
598 // Let's say that we want to have the RTC wake up every 500 ms.
599 //
600 // 500 ms * 32 KHz = 16000 cycles
601 //
602 // Then, we take the calculator, and find out what 64 looks like from a binary
603 // value viewpoint. Using a hex calculator, we see that:
604 //
605 // 16000 = 11111010000000
606 //
607 // ... in floating point representation...
608 //
609 // = 11111010000000.00000
610 //
611 // The next step is to normalize the value. Normalizing the value means that
612 // we represent the value in 1.F format. We do this by moving the decimal value
613 // left until we get the 1.F representation. The number of times we move the
614 // decimal point left is the exponent. Since we need to move the decimal point
615 // left before we get to the 1.F represenation...
616 //
617 // 16000 = 2^13 * 1.1111010000000
618 //
619 // The exponent is therefore 13, and the digits to the right hand side of the
620 // decimal point is the fraction. What we need is the the first four fractional
621 // bits. The first four fraction bits is 1111. We truncate the rest,
622 // unfortunately.
623 //
624 // Therefore, the nearest byte representation for 500 ms is 0xDF
625 //
626 // Notice that if you plugged in 0xDF into this uncompress function, you will
627 // get 496 ms. The reason we didn't quite get 500 ms is that we had to throw
628 // away the 6th fractional bit.
629 //
630 // Anyway, this leads us to the following function. This function takes in a
631 // 16-bit value and compresses it.
632 
633 /***************************************************************************/
645 uint8_t Compress(uint16_t input) // input should be a 16-bit unsigned value
646 {
647  uint32_t tmp = 0;
648  uint32_t exponent = 0;
649  uint32_t significand = 0;
650 
651  if(input==0)
652  return 0;
653 
654 
655  // handle denorm cases
656  // There are multiple answers to 0x0000 and 0x0001 input due to rounding
657  // error introduced throught the gradual underflow
658  // Answer for 0x0000 is from 0x00 to 0x07
659  // Answer for 0x0001 is from 0x08 to 0x0F
660  // We will just 'pick one' answer.
661  if(input == 0x0000) return 0x00;
662  if(input == 0x0001) return 0x08;
663 
664  // Now we have the denorm cases out of the way, the exponent should be at
665  // least one at this point.
666  exponent = 0;
667  tmp = input;
668  while(1)
669  {
670  tmp >>= 1; // Shift until there is only the integer in the lease
671  // significant position
672  exponent += 1;
673  if(tmp == 1)
674  {
675  break; // the integer bit has been found. Stop.
676  }
677  }
678 
679  // Once exponent is found, look for the four fractional bits.
680  //
681  // If the exponent is between 1 to 4, we do not need to do any kind of
682  // fractional rounding. Take care of those cases first
683 
684  if(exponent < 5) // shift left to align the significant and return the
685  // result
686  {
687  significand = ( input << (4 - exponent) ) ;
688  return ( (exponent << 4) | (significand & 0xF));
689  }
690 
691  // At this point, we need to calculate the fraction.
692  //
693  // Easiest way is to align the value so that we have the integer and
694  // fraction bits at a known bit position.
695  //
696  // We then round the signficand to the nearest four fractional bits. To do
697  // so, it is best that we also look at the 5th fractional bit and update
698  // the 4th fractional bit as necessary. During rounding, it is possible for
699  // a carry to occur. If this happens, simply add one to the exponent, and
700  // shift the signficand by one to get to the same bit positioning.
701 
702  significand = input >> (exponent - 5);
703 
704  //
705  // After the shift, the significand looks like this since we shift the
706  // value by 5 less than the exponent. This is what we expect at this point:
707  //
708  // bit[15:6] bit5 bit4 bit3 bit2 bit1 bit0
709  //
710  // zeroes 1 2^-1 2^-2 2^-3 2^-4 2^-5
711  //
712  // ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
713  // int fraction
714  //
715 
716  if(significand & 1) // Check if we need to round up
717  {
718  significand += 2; // Increment the 4th fraction (in bit1 position)
719 
720  // We then check if a carry occurred due to the addition. If a carry
721  // did occur, it would have bumped up the number such that bit6 would
722  // be set. Bit6 is 0x0040.
723  if(significand & 0x0040) // Check for a carry
724  {
725  exponent += 1; // A carry occurred. Increment the exponent
726  significand >>= 1; // shift the signficand right by one
727  }
728  }
729 
730  // Rounding is done... Encode value and return.
731  return ( (exponent << 4) | ( (significand >> 1) & 0xF ) );
732 }
733 #endif // INCLUDE_SI114X_COMPRESS_CODE
734 
735 /******************************************************************************/
736 /*********** END INCLUDE_SI114X_COMPRESS_CDOE ******************************/
737 /******************************************************************************/
738 
739 /******************************************************************************/
740 /*********** INCLUDE_SI114x_COMPRESS_CODE **********************************/
741 /******************************************************************************/
743 // Start of Calibration Code addition
744 #define FLT_TO_FX20(x) ((int32_t)((x*1048576)+.5))
745 #define FX20_ONE FLT_TO_FX20( 1.000000)
746 #define FX20_BAD_VALUE 0xffffffff
747 
749 #if(INCLUDE_SI114X_CALIBRATIONCODE == 1)
750 
751 // msb lsb align
752 // i2c i2c ment
753 // addr addr
754 #define SIRPD_ADCHI_IRLED (collect(buffer, 0x23, 0x22, 0))
755 #define SIRPD_ADCLO_IRLED (collect(buffer, 0x22, 0x25, 1))
756 #define SIRPD_ADCLO_WHLED (collect(buffer, 0x24, 0x26, 0))
757 #define VISPD_ADCHI_WHLED (collect(buffer, 0x26, 0x27, 1))
758 #define VISPD_ADCLO_WHLED (collect(buffer, 0x28, 0x29, 0))
759 #define LIRPD_ADCHI_IRLED (collect(buffer, 0x29, 0x2a, 1))
760 #define LED_DRV65 (collect(buffer, 0x2b, 0x2c, 0))
761 
762 // This is for internal Silabs debug only. Keep it as it is as
763 // this automatically defines away the embedded debug code
764 #ifdef SI114x_CAL_DEBUG
765 #include "Si114x_cal_debug.c"
766 #else
767 #define DEBUG_PRINT_OUTPUT
768 #define DEBUG_PRINT_OUTPUT_2
769 #define DEBUG_UCOEF
770 #endif
771 
773 /***************************************************************************/
777 struct cal_ref_t
778 {
779  uint32_t sirpd_adchi_irled;
780  uint32_t sirpd_adclo_irled;
781  uint32_t sirpd_adclo_whled;
782  uint32_t vispd_adchi_whled;
783  uint32_t vispd_adclo_whled;
784  uint32_t lirpd_adchi_irled;
785  uint32_t ledi_65ma;
786  uint8_t ucoef[4];
787 };
788 
789 /***************************************************************************/
793 struct cal_ref_t calref[2] =
794 {
795  {
796  FLT_TO_FX20( 4.021290), // sirpd_adchi_irled
797  FLT_TO_FX20(57.528500), // sirpd_adclo_irled
798  FLT_TO_FX20( 2.690010), // sirpd_adclo_whled
799  FLT_TO_FX20( 0.042903), // vispd_adchi_whled
800  FLT_TO_FX20( 0.633435), // vispd_adclo_whled
801  FLT_TO_FX20(23.902900), // lirpd_adchi_irled
802  FLT_TO_FX20(56.889300), // ledi_65ma
803  {0x7B, 0x6B, 0x01, 0x00} // default ucoef
804  },
805  {
806  FLT_TO_FX20( 2.325484), // sirpd_adchi_irled
807  FLT_TO_FX20(33.541500), // sirpd_adclo_irled
808  FLT_TO_FX20( 1.693750), // sirpd_adclo_whled
809  FLT_TO_FX20( 0.026775), // vispd_adchi_whled
810  FLT_TO_FX20( 0.398443), // vispd_adclo_whled
811  FLT_TO_FX20(12.190900), // lirpd_adchi_irled
812  FLT_TO_FX20(56.558200), // ledi_65ma
813  {0xdb, 0x8f, 0x01, 0x00} // default ucoef
814  }
815 };
816 
818 /***************************************************************************/
823 static uint32_t decode(uint32_t input)
824 {
825  int32_t exponent, exponent_bias9;
826  uint32_t mantissa;
827 
828  if(input==0) return 0.0;
829 
830  exponent_bias9 = (input & 0x0f00) >> 8;
831  exponent = exponent_bias9 - 9;
832 
833  mantissa = input & 0x00ff; // fraction
834  mantissa |= 0x0100; // add in integer
835 
836  // representation in 12 bit integer, 20 bit fraction
837  mantissa = mantissa << (12+exponent);
838  return mantissa;
839 }
843 /***************************************************************************/
850 static uint32_t collect(uint8_t* buffer,
851  uint8_t msb_addr,
852  uint8_t lsb_addr,
853  uint8_t alignment)
854 {
855  uint16_t value;
856  uint8_t msb_ind = msb_addr - 0x22;
857  uint8_t lsb_ind = lsb_addr - 0x22;
858 
859  if(alignment == 0)
860  {
861  value = buffer[msb_ind]<<4;
862  value += buffer[lsb_ind]>>4;
863  }
864  else
865  {
866  value = buffer[msb_ind]<<8;
867  value += buffer[lsb_ind];
868  value &= 0x0fff;
869  }
870 
871  if( ( value == 0x0fff )
872  || ( value == 0x0000 ) ) return FX20_BAD_VALUE;
873  else return decode( value );
874 }
878 /***************************************************************************/
884 static void shift_left(uint32_t* value_p, int8_t shift)
885 {
886  if(shift > 0)
887  *value_p = *value_p<<shift ;
888  else
889  *value_p = *value_p>>(-shift) ;
890 }
894 #define ALIGN_LEFT 1
895 #define ALIGN_RIGHT -1
896 /***************************************************************************/
902 static int8_t align( uint32_t* value_p, int8_t direction )
903 {
904  int8_t local_shift, shift ;
905  uint32_t mask;
906 
907  // Check invalid value_p and *value_p, return without shifting if bad.
908  if( value_p == NULL ) return 0;
909  if( *value_p == 0 ) return 0;
910 
911  // Make sure direction is valid
912  switch( direction )
913  {
914  case ALIGN_LEFT:
915  local_shift = 1 ;
916  mask = 0x80000000L;
917  break;
918 
919  case ALIGN_RIGHT:
920  local_shift = -1 ;
921  mask = 0x00000001L;
922  break;
923 
924  default:
925  // Invalid direction, return without shifting
926  return 0;
927  }
928 
929  shift = 0;
930  while(1)
931  {
932  if(*value_p & mask ) break;
933  shift++;
934  shift_left( value_p, local_shift );
935  }
936  return shift;
937 }
940 /***************************************************************************/
946 #define FORCE_ROUND_16 1
947 
949 /***************************************************************************/
959 static void fx20_round
960 (
961  uint32_t *value_p
962  #if !FORCE_ROUND_16
963  , int8_t round
964  #endif
965 )
966 {
967  int8_t shift;
968 
969  #if FORCE_ROUND_16
970  // Use the following to force round = 16
971  uint32_t mask1 = 0xffff8000;
972  uint32_t mask2 = 0xffff0000;
973  uint32_t lsb = 0x00008000;
974  #else
975  // Use the following if you want to routine to be
976  // capable of rounding to something other than 16.
977  uint32_t mask1 = ((2<<(round))-1)<<(31-(round));
978  uint32_t mask2 = ((2<<(round-1))-1)<<(31-(round-1));
979  uint32_t lsb = mask1-mask2;
980  #endif
981 
982  shift = align( value_p, ALIGN_LEFT );
983  if( ( (*value_p)&mask1 ) == mask1 )
984  {
985  *value_p = 0x80000000;
986  shift -= 1;
987  }
988  else
989  {
990  *value_p += lsb;
991  *value_p &= mask2;
992  }
993 
994  shift_left( value_p, -shift );
995 }
998 /***************************************************************************/
1004 {
1005  uint32_t op1;
1006  uint32_t op2;
1007 };
1008 
1010 /***************************************************************************/
1014 static uint32_t fx20_divide( struct operand_t* operand_p )
1015 {
1016  int8_t numerator_sh=0, denominator_sh=0;
1017  uint32_t result;
1018  uint32_t* numerator_p;
1019  uint32_t* denominator_p;
1020 
1021  if( operand_p == NULL ) return FX20_BAD_VALUE;
1022 
1023  numerator_p = &operand_p->op1;
1024  denominator_p = &operand_p->op2;
1025 
1026  if( (*numerator_p == FX20_BAD_VALUE)
1027  || (*denominator_p == FX20_BAD_VALUE)
1028  || (*denominator_p == 0 ) ) return FX20_BAD_VALUE;
1029 
1030  fx20_round ( numerator_p );
1031  fx20_round ( denominator_p );
1032  numerator_sh = align ( numerator_p, ALIGN_LEFT );
1033  denominator_sh = align ( denominator_p, ALIGN_RIGHT );
1034 
1035  result = *numerator_p / ( (uint16_t)(*denominator_p) );
1036  shift_left( &result , 20-numerator_sh-denominator_sh );
1037 
1038  return result;
1039 }
1043 /***************************************************************************/
1047 static uint32_t fx20_multiply( struct operand_t* operand_p )
1048 {
1049  uint32_t result;
1050  int8_t val1_sh, val2_sh;
1051  uint32_t* val1_p;
1052  uint32_t* val2_p;
1053 
1054  if( operand_p == NULL ) return FX20_BAD_VALUE;
1055 
1056  val1_p = &(operand_p->op1);
1057  val2_p = &(operand_p->op2);
1058 
1059  fx20_round( val1_p );
1060  fx20_round( val2_p );
1061 
1062  val1_sh = align( val1_p, ALIGN_RIGHT );
1063  val2_sh = align( val2_p, ALIGN_RIGHT );
1064 
1065 
1066  result = (uint32_t)( ( (uint32_t)(*val1_p) ) * ( (uint32_t)(*val2_p) ) );
1067  shift_left( &result, -20+val1_sh+val2_sh );
1068 
1069  return result;
1070 }
1074 /***************************************************************************/
1081 static int16_t find_cal_index( uint8_t* buffer )
1082 {
1083  int16_t index;
1084  uint8_t size;
1085 
1086  // buffer[12] is the LSB, buffer[13] is the MSB
1087  index = ( int16_t )( buffer[12] + ( (uint16_t)( buffer[13] ) << 8 ) );
1088 
1089  switch( index )
1090  {
1091  case -1:
1092  index = 0;
1093  break;
1094  case -2:
1095  index = 0;
1096  break;
1097  case -3:
1098  index = 1;
1099  default:
1100  index = -(4+index) ;
1101  }
1102 
1103  size = sizeof(calref)/sizeof(calref[0]);
1104 
1105  if( index < size )
1106  {
1107  return index;
1108  }
1109  else
1110  {
1111  return -1;
1112  }
1113 }
1117 /***************************************************************************/
1121 static uint32_t vispd_correction(uint8_t* buffer)
1122 {
1123 
1124  struct operand_t op;
1125  uint32_t result;
1126  int16_t index = find_cal_index( buffer );
1127 
1128  if( index < 0 ) result = FX20_ONE;
1129 
1130  op.op1 = calref[ index ].vispd_adclo_whled;
1131  op.op2 = VISPD_ADCLO_WHLED;
1132  result = fx20_divide( &op );
1133 
1134  if( result == FX20_BAD_VALUE ) result = FX20_ONE;
1135 
1136  return result;
1137 }
1141 /***************************************************************************/
1145 static uint32_t irpd_correction(uint8_t* buffer)
1146 {
1147  struct operand_t op;
1148  uint32_t result;
1149  int16_t index = find_cal_index( buffer );
1150 
1151  if( index < 0 ) result = FX20_ONE;
1152 
1153  // op.op1 = SIRPD_ADCLO_IRLED_REF; op.op2 = SIRPD_ADCLO_IRLED;
1154  op.op1 = calref[ index ].sirpd_adclo_irled;
1155  op.op2 = SIRPD_ADCLO_IRLED;
1156  result = fx20_divide( &op );
1157 
1158  if( result == FX20_BAD_VALUE ) result = FX20_ONE;
1159 
1160  return result;
1161 }
1165 /***************************************************************************/
1171 static uint32_t adcrange_ratio(uint8_t* buffer)
1172 {
1173  struct operand_t op;
1174  uint32_t result;
1175 
1176  op.op1 = SIRPD_ADCLO_IRLED ; op.op2 = SIRPD_ADCHI_IRLED ;
1177  result = fx20_divide( &op );
1178 
1179  if( result == FX20_BAD_VALUE ) result = FLT_TO_FX20( 14.5 );
1180 
1181  return result;
1182 }
1186 /***************************************************************************/
1191 static uint32_t irsize_ratio(uint8_t* buffer)
1192 {
1193  struct operand_t op;
1194  uint32_t result;
1195 
1196  op.op1 = LIRPD_ADCHI_IRLED ; op.op2 = SIRPD_ADCHI_IRLED ;
1197 
1198  result = fx20_divide( &op );
1199 
1200  if( result == FX20_BAD_VALUE ) result = FLT_TO_FX20( 6.0 );
1201 
1202  return result;
1203 }
1207 /***************************************************************************/
1212 static uint32_t ledi_ratio(uint8_t* buffer)
1213 {
1214 
1215  struct operand_t op;
1216  uint32_t result;
1217  int16_t index;
1218 
1219  index = find_cal_index( buffer );
1220 
1221  if( index < 0 ) result = FX20_ONE;
1222 
1223  // op.op1 = LED_DRV65_REF; op.op2 = LED_DRV65;
1224  op.op1 = calref[ index ].ledi_65ma;
1225  op.op2 = LED_DRV65;
1226  result = fx20_divide( &op );
1227 
1228  if( result == FX20_BAD_VALUE ) result = FX20_ONE;
1229 
1230  return result;
1231 }
1235 /***************************************************************************/
1241 static int16_t si114x_get_cal_index( HANDLE si114x_handle, uint8_t* buffer )
1242 {
1243  int16_t retval;
1244  uint8_t response;
1245 
1246  if( ( si114x_handle == NULL ) || ( buffer == NULL ) )
1247  return -1;
1248 
1249  // Check to make sure that the device is ready to receive commands
1250  do
1251  {
1252  retval = Si114xNop( si114x_handle );
1253  if( retval != 0 ) return -1;
1254 
1255 
1256  response = Si114xReadFromRegister( si114x_handle, REG_RESPONSE );
1257  if (response != 0)
1258  {
1259  delay_1ms();
1260  }
1261  } while( response != 0 );
1262 
1263  // Retrieve the index
1264  retval = Si114xWriteToRegister( si114x_handle, REG_COMMAND, 0x11 );
1265  _waitUntilSleep(si114x_handle);
1266 
1267  if( retval != 0 ) return -1;
1268 
1269  retval = Si114xBlockRead( si114x_handle, REG_PS1_DATA0, 2, &(buffer[12]) );
1270  if( retval != 0 ) return -1;
1271 
1272  return 0;
1273 }
1276 /***************************************************************************/
1319 /*
1320  * Side-effects:
1321  * - Writes 0x11 to command reg to retrieve factory calibration values in
1322  * buffer[0] to buffer[11]
1323  *
1324  * - Calls the various helper functions such as vispd_correction()
1325  * irpd_correction, to populate the SI114X_CAL_S structure
1326  *
1327  * - Writes 0x12 to command reg to retrieve factory cal_index to
1328  * buffer[12] to buffer[13]
1329  ******************************************************************************/
1330 int16_t si114x_get_calibration( HANDLE si114x_handle,
1331  SI114X_CAL_S* si114x_cal,
1332  uint8_t security)
1333 {
1334  uint8_t buffer[14];
1335  int16_t retval = 0;
1336  uint8_t response;
1337 
1338  if( si114x_handle == NULL ) { retval = -4; goto error_exit; }
1339 
1340  if( si114x_cal == NULL ) { retval = -4; goto error_exit; }
1341 
1342  // if requested, check to make sure the interface registers are zero
1343  // as an indication of a device that has not started any autonomous
1344  // operation
1345  if( security == 1 )
1346  {
1347  int8_t i;
1348 
1349  retval = Si114xBlockRead( si114x_handle, REG_ALS_VIS_DATA0, 12, buffer );
1350  if( retval != 0 ) { retval = -2; goto error_exit; }
1351 
1352  for( i=0; i<12; i++)
1353  {
1354  if( buffer[i] != 0 ) { retval = -1; goto error_exit; }
1355  }
1356 
1357  DEBUG_PRINT_OUTPUT;
1358 
1359  }
1360 
1361  // Check to make sure that the device is ready to receive commands
1362  do
1363  {
1364  retval = Si114xNop( si114x_handle );
1365  if( retval != 0 ) { retval = -2; goto error_exit; }
1366 
1367 
1368  response = Si114xReadFromRegister( si114x_handle, REG_RESPONSE );
1369  if (response != 0)
1370  {
1371  delay_1ms();
1372  }
1373  } while( response != 0 );
1374 
1375  // Request for the calibration data
1376  retval = Si114xWriteToRegister( si114x_handle, REG_COMMAND, 0x12 );
1377  _waitUntilSleep(si114x_handle);
1378 
1379  if( retval != 0 ) { retval = -2; goto error_exit; }
1380 
1381  // Wait for the response register to increment
1382  do
1383  {
1384  response = Si114xReadFromRegister( si114x_handle, REG_RESPONSE );
1385  // If the upper nibbles are non-zero, something is wrong
1386  if( response == 0x80 )
1387  {
1388  // calibration code has not been implemented on this device
1389  // leading to command error. So, rather than returning an
1390  // error, handle the error by Nop and set ratios to -1.0
1391  // and return normally.
1392  Si114xNop( si114x_handle );
1393  retval = -3;
1394  goto error_exit;
1395  }
1396  else if( response & 0xfff0 )
1397  {
1398  // if upper nibble is anything but 0x80, exit with an error
1399  retval = -2;
1400  goto error_exit;
1401  }
1402  if (response != 1)
1403  {
1404  delay_1ms();
1405  }
1406  } while( response != 1 );
1407 
1408  // Retrieve the 12 bytes from the interface registers
1409  retval = Si114xBlockRead( si114x_handle, REG_ALS_VIS_DATA0, 12, buffer );
1410  if( retval != 0 ) { retval = -2; goto error_exit; }
1411 
1412  DEBUG_PRINT_OUTPUT;
1413 
1414  retval=si114x_get_cal_index( si114x_handle, buffer );
1415 
1416  if( retval != 0 )
1417  {
1418  retval = -2; goto error_exit;
1419  }
1420 
1421  si114x_cal->ledi_ratio = ledi_ratio(buffer);
1422  si114x_cal->vispd_correction = vispd_correction(buffer);
1423  si114x_cal->irpd_correction = irpd_correction(buffer);
1424  si114x_cal->adcrange_ratio = adcrange_ratio(buffer);
1425  si114x_cal->ucoef_p = calref[find_cal_index(buffer)].ucoef;
1426  si114x_cal->irsize_ratio = irsize_ratio(buffer);
1427 
1428  DEBUG_PRINT_OUTPUT_2;
1429 
1430  return 0;
1431 
1432 error_exit:
1433  si114x_cal->vispd_correction = FX20_ONE;
1434  si114x_cal->irpd_correction = FX20_ONE;
1435  si114x_cal->adcrange_ratio = FLT_TO_FX20( 14.5 );
1436  si114x_cal->irsize_ratio = FLT_TO_FX20( 6.0 );
1437  si114x_cal->ledi_ratio = FX20_ONE;
1438  si114x_cal->ucoef_p = NULL;
1439  return retval;
1440 }
1441 
1442 /***************************************************************************/
1470 int16_t si114x_set_ucoef( HANDLE si114x_handle,
1471  uint8_t* input_ucoef,
1472  SI114X_CAL_S* si114x_cal )
1473 {
1474  int8_t response;
1475  uint8_t temp;
1476  uint32_t vc=FX20_ONE, ic=FX20_ONE, long_temp;
1477  struct operand_t op;
1478  uint8_t* ref_ucoef = si114x_cal->ucoef_p;
1479  uint8_t out_ucoef[4];
1480 
1481  if( input_ucoef != NULL ) ref_ucoef = input_ucoef;
1482 
1483  if( ref_ucoef == NULL ) return -1 ;
1484 
1485  // retrieve part identification
1486  response = Si114xReadFromRegister( si114x_handle, REG_PART_ID );
1487  switch( response )
1488  {
1489  case 0x32: case 0x45: case 0x46: case 0x47: temp = 1; break;
1490  default: temp = 0; break;
1491  }
1492  if( !temp ) return -1;
1493 
1494  if(si114x_cal != 0)
1495  {
1496  if(si114x_cal->vispd_correction > 0) vc = si114x_cal->vispd_correction;
1497  if(si114x_cal->irpd_correction > 0) ic = si114x_cal->irpd_correction;
1498  }
1499 
1500  op.op1 = ref_ucoef[0] + ((ref_ucoef[1])<<8);
1501  op.op2 = vc;
1502  long_temp = fx20_multiply( &op );
1503  out_ucoef[0] = (long_temp & 0x00ff);
1504  out_ucoef[1] = (long_temp & 0xff00)>>8;
1505 
1506  op.op1 = ref_ucoef[2] + (ref_ucoef[3]<<8);
1507  op.op2 = ic;
1508  long_temp = fx20_multiply( &op );
1509  out_ucoef[2] = (long_temp & 0x00ff);
1510  out_ucoef[3] = (long_temp & 0xff00)>>8;
1511 
1512  DEBUG_UCOEF
1513 
1514  response = Si114xBlockWrite( si114x_handle, REG_UCOEF0 , 4, out_ucoef);
1515 
1516  return response;
1517 }
1518 #else // INCLUDE_SI114X_CALIBRATION_CODE
1519 
1520 /******************** STUB FUNCTIONS ONLY **********************************/
1521 
1522 int16_t si114x_get_calibration( HANDLE si114x_handle,
1523  SI114X_CAL_S* si114x_cal,
1524  uint8_t security)
1525 {
1526  // although the SI114x_CAL_S structure is not filled up properly, the
1527  // set_ucoef() function will not use it.
1528  return 0;
1529 }
1530 
1531 int16_t si114x_set_ucoef( HANDLE si114x_handle,
1532  uint8_t* input_ucoef,
1533  SI114X_CAL_S* si114x_cal )
1534 {
1535  int16_t response;
1536  uint8_t code ucoef[4] = { 0x7B, 0x6B, 0x01, 0x00 } ;
1537 
1538  // This will write 4 bytes starting with I2C address 0x13
1539  response = Si114xBlockWrite( si114x_handle, REG_UCOEF0, 4, &ucoef[0] );
1540  return response;
1541 }
1542 #endif // INCLUDE_SI114X_CALIBRATION_CODE
1543 
1544 /******************************************************************************/
1545 /*********** END INCLUDE_SI114x_COMPRESS_CODE ******************************/
1546 /******************************************************************************/
1547 
SI114X_CAL_S Data Structure.
void delay_10ms()
Implements 10ms delay.
Definition: si1147_i2c.c:267
#define FORCE_ROUND_16
This compile switch used only to experiment with various rounding precisions. The flexibility has a s...
int16_t Si114xPsForce(HANDLE si114x_handle)
Sends a PSFORCE command to the Si113x/4x.
int16_t Si114xNop(HANDLE si114x_handle)
Sends a NOP command to the Si113x/4x.
uint32_t lirpd_adchi_irled
Si114x function prototypes, structure and bit definitions.
uint16_t Uncompress(uint8_t input)
Converts an 8-bit compressed value to 16-bit.
uint32_t vispd_correction
int16_t Si114xParamSet(HANDLE si114x_handle, uint8_t address, uint8_t value)
Writes a byte to an Si113x/4x Parameter.
uint8_t Compress(uint16_t input)
Converts a 16-bit value to 8-bit value.
uint8_t ucoef[4]
int16_t Si114xBlockWrite(HANDLE si114x_handle, uint8_t address, uint8_t length, uint8_t *values)
Writes block of Si114x registers.
Definition: si1147_i2c.c:239
Structure Definition for calref array.
uint32_t ledi_ratio
uint32_t irsize_ratio
int16_t Si114xWriteToRegister(HANDLE si114x_handle, uint8_t address, uint8_t data)
Writes to Si114x Register.
Definition: si1147_i2c.c:205
int16_t si114x_set_ucoef(HANDLE si114x_handle, uint8_t *input_ucoef, SI114X_CAL_S *si114x_cal)
Initializes the Si113x/46/47/48 UCOEF Registers.
int16_t Si114xAlsForce(HANDLE si114x_handle)
Sends an ALSFORCE command to the Si113x/4x.
int16_t Si114xParamRead(HANDLE si114x_handle, uint8_t address)
Reads a Parameter from the Si113x/4x.
uint8_t * ucoef_p
void delay_1ms()
Implements 1ms delay.
Definition: si1147_i2c.c:279
struct cal_ref_t calref[2]
Factory Calibration Reference Values.
int16_t Si114xPsAlsForce(HANDLE si114x_handle)
Sends a PSALSFORCE command to the Si113x/4x.
uint32_t vispd_adclo_whled
int16_t Si114xReadFromRegister(HANDLE si114x_handle, uint8_t address)
Reads from Si114x register.
Definition: si1147_i2c.c:219
uint32_t sirpd_adchi_irled
uint32_t adcrange_ratio
int16_t Si114xBlockRead(HANDLE si114x_handle, uint8_t address, uint8_t length, uint8_t *values)
Reads block of Si114x registers.
Definition: si1147_i2c.c:258
int16_t si114x_get_calibration(HANDLE si114x_handle, SI114X_CAL_S *si114x_cal, uint8_t security)
Populates the SI114X_CAL_S structure.
The fx20_divide and fx20_multiply uses this structure to pass values into it.
static void * si114x_handle
Si114x PGM toolkit functions uses a void* to pass hardware parameters through to the lower level i2c ...
int16_t Si114xPsAlsAuto(HANDLE si114x_handle)
Sends a PSALSAUTO command to the Si113x/4x.
uint32_t sirpd_adclo_irled
int16_t Si114xPauseAll(HANDLE si114x_handle)
Pauses autonomous measurements.
uint32_t sirpd_adclo_whled
int16_t Si114xReset(HANDLE si114x_handle)
Resets the Si113x/4x, clears any interrupts and initializes the HW_KEY register.
uint32_t irpd_correction
uint32_t vispd_adchi_whled
uint32_t ledi_65ma