TEMT6200 - Ambient light sensor library for Wi-Go board

Dependents:   TEMT6200_demo Wi-Go_IOT_Demo MMA8451Q IoT_World_Hackathon_WiGo_NSP_Demo ... more

TEMT6200 library


TEMT6200 specifications

TEMT6200 datasheet

Photo current Ipce4.6uA @ 20lux
7.5 .. 39uA @ 100lux
average = (7.5 + 39)/2 = 23.25uA
Dark currenttypical 3nA
max. 50nA

IMPORTANT NOTE
There are 3 binned groups defining Ipce min/max @ 100lux.
If the BIN group is unknown, we use the overall average Ipce value @ 100lux = 23.25uA

BINminmaxavg
Group AIpce (uA)7.51511.25
Group BIpce (uA)122418
Group CIpce (uA)19.53929.25

Schematic: Wi-Go KL25Z kit - TEMT6200 wiring

/media/uploads/frankvnk/temt6200_wi-go_diagram.jpg

  • U8 - Collector : directly connected to PTD5 - AMBLGHT_EN = 3v3
  • U8 - Emitter : connected to GND through 1k resistor (R42). Wired to non-inverting amplifier (U7 - MAX9636) - amplification = 11.
  • MAX9636 : output wired to PTB0 (ADC0_SE8 analog in) and PTC8 (CMP0_IN2 comparator in) = AMBLGHT_LVL label.

Formulas

AnalogIn.read() returns a float value between 0.0 and 1.0
A/D converter input : full scale = 3v3
VR42 (V) = AnalogIn.read() * 3.3 / 11
IR42 (A) = VR42 / R42 = VR42 / 1000
Since R42 = 1000ohm, IR42 in mA = VR42

Basic theoretical formula

                          100lux * IR42   100 * IR42(mA)
Ev (illuminance in lux) = ------------- = --------------
                           Ipce@100lux      0.02325mA


Complete formula
This formula only applies to the Wi-Go board since we set the amplification and reference voltage to a fixed level.
see further for a generic formula.
Current in mA
Voltage in V
Ipce = value @ 100lux in mA
Ain = AnalogIn.read() value

                       Ain * 3.3
                       ---------
                          11             Ain * 3.3   Ain * 330   Ain * 30
      Ev (lux) = 100 * --------- = 100 * --------- = --------- = --------
                         Ipce            11 * Ipce   11 * Ipce     Ipce


Theoretical values for 1..1000lux
Reference is current @ 100lux.

luxcurrent (uA)
10023.25
10.2325
1000232.5

R42 = 1k => VR42 @ 1lux    = 232.5nA * 1000 = 232.5uV  => AMBLGHT_LVL = 11 x VR42 = 2.5575mV
            VR42 @ 1000lux = 232.5uA * 1000 = 232.5mV  => AMBLGHT_LVL = 11 x VR42 = 2.5575V

Extrapolate to full scale (3.3V) - Max. level = 1290 lux.

NOTE
These values are calculated without taking the dark current into account.
Worst case deviation = 50nA dark current.

1lux    = 232.5nA - 50nA = 182.50nA (21.5% error)
1000lux = 232.5uA - 50nA = 232.45uA


We can measure the actual dark current by taping off the sensor and reading the A/D value using the calibrate() function.

  • The resulting value will also contain the A/D-D/A error, resistor value error and external interferences.
  • Temperature drift is not included.

This value can be used in our formula to even out basic conversion errors.

Calibrate function : We take 10 consecutive samples discarding samples > 0.000050

                     ds   = discarded samples
                     sa   = sum of samples
                     ns   = number of samples     = 10
                     amp  = opamp amplification   = 11
                     Vref = DAC reference voltage = 3.3V

                     If ds = ns : set dark_current = 0

                                         (sa / (ns - ds)) * Vref   (sa / (ns - ds)) * 3.3     sa * 3.3
                     dark_current (mA) = ----------------------- = ---------------------- = ------------
                                                    amp                       11            (ns-ds) * 11


Generic complete formula
Inserting the dark current calculation into the complete formula gives us following generic formula:

Ain  = Analog input value read from input (float) 0.0 .. 1.0
amp  = opamp amplification
Vref = DAC reference voltage (V)
Ipce = theoretical average current @ 100lux (mA)
dc   = averaged dark current obtained from calibrate() function (mA)

                       Ain * Vref
                       ----------
                          amp      100 * Ain * Vref
      Ev (lux) = 100 * --------- = -----------------
                       Ipce - dc   amp * (Ipce - dc)

TEMT6200.h contains defines allowing us to easily modify each parameter:

#define TEMT_AREF          (float)3.3           // Analog reference voltage
#define TEMT_MAXLX         1290                 // Calculated max. lux
#define TEMT_AMP           11                   // Op-amp amplification
#define TEMT_IAVG          (float)0.02325       // Theoretical average Ipce @ 100lux
#define TEMT_DARKC         (float)0.000050      // Max. dark current


Used in our formula : Vref = TEMT_AREF
                      amp  = TEMT_AMP
                      Ipce = TEMT_IAVG

Calibration

The ambient light sensor needs to be completely obscured when we use the calibrate() function.
The function takes 10 consecutive 'dark current' samples and calculates the average value of these samples.
This value is subtracted from the theoretical mean value (23.25uA).
Safeguards are built into this function:

  • Each value larger than the max. dark current specified in the datasheet is discarded.
  • When all values are out of range, the theoretical mean value is used (23.25uA).
    So, a valid result is set even when we forget to obscure the ambient light sensor.

Generating an interrupt when a certain level is reached

The ComparatorIn library can be used to generate an interrupt on a specified level.

We can choose between two D/A channels (input range : 0.0 .. 1.0) to set the threshold level:

  • An internal 6-bit DAC
    range is from Vin / 64 to Vin.
    With Vref = 3.3V, each step = 3.3 / 64 = 51.56mV.
    This corresponds to 20.16 lux / step (very limited).
  • An external 12-bit DAC at PTE30
    range is from Vin / 4096 to Vin.
    With Vref = 3.3V, each step = 3.3 / 4096 = 0.806mV.
    This corresponds to 0.315 lux / step.
    This connection can only be used when no external output is connected to PTE30.

Calculating the comparator threshold level
The lux_pct(nnn) function allows us to translate a lux value to an analog level suitable for setting the ComparatorIn threshold, regardless which D/A converter is used (6-bit or 12-bit).


Sample code

#include "TEMT6200.h"
#include "ComparatorIn.h"
#include "mbed.h"

void ambi_rise(void);       // ISR callback pointer
void ambi_fall(void);       // ISR callback pointer

// RGB LED
DigitalOut cmpled (LED_GREEN);
DigitalOut blinky (LED_BLUE);

// Ambient light sensor to comparator plus input = PTC8, min input = 12-bit DAC0
ComparatorIn compi(PTC8, PTE30);

// Ambient light sensor : PTD5 = enable, PTB0 = analog input
TEMT6200 ambi(PTD5, PTB0);

int main()
{
    // Ambient light detector
    ambi.pwr(AMB_ON);
    printf("\n- Ambient light sensor calibration.\n");
    printf("  Make sure the sensor is obscured .\n");
    printf("  Press any key to calibrate.\n");
    getchar();
    ambi.calibrate();                                 // Calibrate 
    printf("- Calibration finished.\n");

    // Comparator settings
    compi.rising(&ambi_rise);                         // Set comparator rising int callback to ambi_rise
    compi.falling(&ambi_fall);                        // Set comparator falling int callback to ambi_fall
    compi.treshold(ambi.lux_pct(300.0));              // Set ISR treshold to 300lux
    compi.int_en(1);                                  // Enable comparator interrupt

    cmpled = 1;
    
    while (1)
    {
        printf("%7.2f lux\n",ambi.read());
        wait(0.5);
        blinky = !blinky;
    }
}

// Comparator callback
void ambi_rise(void)
{
    cmpled = 0;                       // Green led on.
}

void ambi_fall(void)
{
    cmpled = 1;                       // Green led off.
}
Committer:
frankvnk
Date:
Sun Aug 25 09:07:40 2013 +0000
Revision:
4:5d1118cb5702
Parent:
0:9e9ed4a5a547
Added function to read raw sensor data.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
frankvnk 0:9e9ed4a5a547 1 /**************************************************************************************************
frankvnk 0:9e9ed4a5a547 2 ***** *****
frankvnk 0:9e9ed4a5a547 3 ***** Name: TEMT6200.cpp *****
frankvnk 0:9e9ed4a5a547 4 ***** Date: 15/06/2013 *****
frankvnk 0:9e9ed4a5a547 5 ***** Auth: Frank Vannieuwkerke *****
frankvnk 0:9e9ed4a5a547 6 ***** Func: library for TEMT6200 ambient light sensor *****
frankvnk 0:9e9ed4a5a547 7 ***** *****
frankvnk 0:9e9ed4a5a547 8 **************************************************************************************************/
frankvnk 0:9e9ed4a5a547 9
frankvnk 0:9e9ed4a5a547 10 #include "TEMT6200.h"
frankvnk 0:9e9ed4a5a547 11
frankvnk 0:9e9ed4a5a547 12 TEMT6200::TEMT6200(PinName en, PinName level) : _en(en), _level(level)
frankvnk 0:9e9ed4a5a547 13 {
frankvnk 0:9e9ed4a5a547 14 _en = AMB_ON; // Enable ambient light sensor
frankvnk 0:9e9ed4a5a547 15 Ipce100 = TEMT_IAVG;
frankvnk 0:9e9ed4a5a547 16 }
frankvnk 0:9e9ed4a5a547 17
frankvnk 4:5d1118cb5702 18 unsigned short TEMT6200::readRaw(void)
frankvnk 4:5d1118cb5702 19 {
frankvnk 4:5d1118cb5702 20 return(_level.read_u16());
frankvnk 4:5d1118cb5702 21 }
frankvnk 4:5d1118cb5702 22
frankvnk 0:9e9ed4a5a547 23 float TEMT6200::read(void)
frankvnk 0:9e9ed4a5a547 24 {
frankvnk 0:9e9ed4a5a547 25 return(100 * (_level.read() * TEMT_AREF) / (TEMT_AMP * Ipce100));
frankvnk 0:9e9ed4a5a547 26 }
frankvnk 0:9e9ed4a5a547 27
frankvnk 0:9e9ed4a5a547 28 void TEMT6200::pwr(bool pwr)
frankvnk 0:9e9ed4a5a547 29 {
frankvnk 0:9e9ed4a5a547 30 _en = pwr;
frankvnk 0:9e9ed4a5a547 31 }
frankvnk 0:9e9ed4a5a547 32
frankvnk 0:9e9ed4a5a547 33 void TEMT6200::calibrate(void)
frankvnk 0:9e9ed4a5a547 34 {
frankvnk 0:9e9ed4a5a547 35 int sa_cnt, ds_cnt = 0;
frankvnk 0:9e9ed4a5a547 36 float tmp_d, dark_current = 0.0;
frankvnk 0:9e9ed4a5a547 37 Ipce100 = TEMT_IAVG; // Set Ipce back to its theoretical mean value
frankvnk 0:9e9ed4a5a547 38 for(sa_cnt = 0 ; sa_cnt < 10 ; sa_cnt++)
frankvnk 0:9e9ed4a5a547 39 {
frankvnk 0:9e9ed4a5a547 40 tmp_d = (_level.read() * TEMT_AREF) / TEMT_AMP;
frankvnk 0:9e9ed4a5a547 41 if(tmp_d < TEMT_DARKC) // We only use valid dark_current values
frankvnk 0:9e9ed4a5a547 42 dark_current += tmp_d;
frankvnk 0:9e9ed4a5a547 43 else
frankvnk 0:9e9ed4a5a547 44 ds_cnt++;
frankvnk 0:9e9ed4a5a547 45 }
frankvnk 0:9e9ed4a5a547 46 /*
frankvnk 0:9e9ed4a5a547 47 Only update Ipce when error count < sample count
frankvnk 0:9e9ed4a5a547 48 This is also a security measure : calibrating while the sensor is not covered
frankvnk 0:9e9ed4a5a547 49 will set Ipce to the theoretical mean value.
frankvnk 0:9e9ed4a5a547 50 */
frankvnk 0:9e9ed4a5a547 51 if(ds_cnt < sa_cnt)
frankvnk 0:9e9ed4a5a547 52 {
frankvnk 0:9e9ed4a5a547 53 dark_current = dark_current / (sa_cnt - ds_cnt);
frankvnk 0:9e9ed4a5a547 54 Ipce100 -= dark_current;
frankvnk 0:9e9ed4a5a547 55 }
frankvnk 0:9e9ed4a5a547 56 }
frankvnk 0:9e9ed4a5a547 57
frankvnk 0:9e9ed4a5a547 58 float TEMT6200::lux_pct(float lux_val)
frankvnk 0:9e9ed4a5a547 59 {
frankvnk 0:9e9ed4a5a547 60 float pct;
frankvnk 0:9e9ed4a5a547 61 pct = lux_val * TEMT_AMP * Ipce100 / (100 * TEMT_AREF);
frankvnk 0:9e9ed4a5a547 62 if(pct < 0.0)
frankvnk 0:9e9ed4a5a547 63 {
frankvnk 0:9e9ed4a5a547 64 return(0.0);
frankvnk 0:9e9ed4a5a547 65 }
frankvnk 0:9e9ed4a5a547 66 else if(pct > 1.0)
frankvnk 0:9e9ed4a5a547 67 {
frankvnk 0:9e9ed4a5a547 68 return(1.0);
frankvnk 0:9e9ed4a5a547 69 }
frankvnk 0:9e9ed4a5a547 70 else
frankvnk 0:9e9ed4a5a547 71 {
frankvnk 0:9e9ed4a5a547 72 return(pct);
frankvnk 0:9e9ed4a5a547 73 }
frankvnk 0:9e9ed4a5a547 74 }
frankvnk 0:9e9ed4a5a547 75
frankvnk 0:9e9ed4a5a547 76
frankvnk 4:5d1118cb5702 77