A lux sensor library

Dependents:   Adafruit_TSL2561

TSL2561.cpp

Committer:
joshuasmth04
Date:
2015-08-13
Revision:
3:5b5911bfc644
Parent:
2:c45d34e9c3f0

File content as of revision 3:5b5911bfc644:

// date 7/6/2015
// author E wendt

//TSL2561 light sensor library

// lux equation approximation without floating point calculations
//////////////////////////////////////////////////////////////////////////////
// Routine: unsigned int CalculateLux(unsigned int ch0, unsigned int ch0, int iType)
//
// Description: Calculate the approximate illuminance (lux) given the raw
// channel values of the TSL2560. The equation if implemented
// as a piece−wise linear approximation.
//
// Arguments: unsigned int iGain − gain, where 0:1X, 1:16X
// unsigned int tInt − integration time, where 0:13.7mS, 1:100mS, 2:402mS,
// 3:Manual
// unsigned int ch0 − raw channel value from channel 0 of TSL2560
// unsigned int ch1 − raw channel value from channel 1 of TSL2560
// unsigned int iType − package type (T or CS)
//
// Return: unsigned int − the approximate illuminance (lux)
//
//////////////////////////////////////////////////////////////////////////////

#include "mbed.h"
#include "TSL2561.h"

TSL2561::TSL2561(PinName sda, PinName scl, char slave_address)
    :
    i2c_p(new I2C(sda, scl)),
    i2c(*i2c_p),
    address(slave_address),
    _integration(TSL2561_INTEGRATIONTIME_402MS),
    _gain(TSL2561_GAIN_0X)
{
    initialize();
}

TSL2561::TSL2561(I2C &i2c_obj, char slave_address)
    :
    i2c_p(NULL),
    i2c(i2c_obj),
    address(slave_address),
    _integration(TSL2561_INTEGRATIONTIME_402MS),
    _gain(TSL2561_GAIN_0X)
{
    initialize();
}

TSL2561::~TSL2561()
{
    if (NULL != i2c_p) {
        delete  i2c_p;
    }
}

//The register addresses are all passed to i2c write via the bottum 4 bits of the command register
//check and see if it is reading the correct id register (should read (0x10) for register value

bool TSL2561::initialize(void)
{
    float idf;    //debug
    uint16_t idu;
    char ID_REGISTER[1] = {TSL2561_REGISTER_ID};
    i2c.write(address, ID_REGISTER, 1, true);
    char DATA[1] = {0};
    i2c.read(address, DATA, 1, false);
    idu = DATA[0];
    idf = (float)idu;    //debug

    if (idu & 0x0A) {
        pc.printf("FOUND TSL2561\r\n");    //debug
    } else {
        pc.printf("NOT FOUND TSL2561\r\n");    //debug
        pc.printf("%4.0f ID\n",idf);    //debug
        return false;
    }

    //set default integration time and gain
    setTimingGain(_integration, _gain);

    disable();

    return true;
}



void TSL2561::enable(void)
{
    //power on device by writing 0x03 to the control register
    char CONTROL_ON[2] = {TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, TSL2561_CONTROL_POWERON};
    i2c.write(address, CONTROL_ON, 2);
    //***//Comment this wait out and the disable function to reduce read time, at the cost of power consumption.
    //wait_ms(400);    //It takes a sec for the TSL to start up.
    //***//
    //This sets everything to default values. (402ms integration time, interupts disabled etc.)
}

void TSL2561::disable(void)
{
    //power off device by writing 0x00 in control register
    //***//
    //char CONTROL_OFF[2] = {TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, TSL2561_CONTROL_POWEROFF};
    //i2c.write(address, CONTROL_OFF, 2);
    //***//
}

void TSL2561::setTimingGain(tsl2561IntegrationTime_t integration, tsl2561Gain_t gain)
{
    enable();
    _integration = integration;
    _gain        = gain;

    char TIME_SETTING[2] = {TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, _integration | _gain};
    i2c.write(address, TIME_SETTING, 2);

    disable();
}










uint32_t TSL2561::calculateLux(uint16_t ch0, uint16_t ch1)
{
    unsigned long chScale;
    unsigned long channel1;
    unsigned long channel0;

    //scale the measurment if the integration time is not 402 ms
    switch (_integration) {
        case TSL2561_INTEGRATIONTIME_13MS:
            chScale = CHSCALE_TINT0;
            break;

        case TSL2561_INTEGRATIONTIME_101MS:
            chScale = CHSCALE_TINT1;
            break;

        default: //No scaling when integration time is set to 402 ms
            chScale = (1 << CH_SCALE);
            break;
    }

    //scale the measurment if gain is NOT x16
    if(!_gain) {
        chScale = chScale << 4;
    }

    //scale the channel values
    channel0 = (ch0 * chScale) >> CH_SCALE;
    channel1 = (ch1 * chScale) >> CH_SCALE;

    //Find the ratio of the channel values (Channel 11/Channel 10) to set coeficients
    unsigned long ratio1 = 0;
    if (channel0 != 0) {
        ratio1 = (channel1 << (RATIO_SCALE+1))/channel0;
    }

    //round the ratio value
    unsigned long ratio = (ratio1 + 1) >> 1;

    unsigned int b=0, m=0;

    //check which coefficients the ratio dictates

#ifdef TSL2561_PACKAGE_CS //Adafruit sells the T package but these coefficients are for the CS package
    if ((ratio >= 0) && (ratio <= K1C)) {
        b = B1C;
        m = M1C;
    } else if (ratio <= K2C) {
        b = B2C;
        m = M2C;
    } else if (ratio <= K3C) {
        b = B3C;
        m = M3C;
    } else if (ratio <= K4C) {
        b = B4C;
        m = M4C;
    } else if (ratio <= K5C) {
        b = B5C;
        m = M5C;
    } else if (ratio <= K6C) {
        b = B6C;
        m = M6C;
    } else if (ratio <= K7C) {
        b = B7C;
        m = M7C;
    } else if (ratio <= K8C) {
        b = B8C;
        m = M8C;
    }
#else
    if ((ratio >= 0) && (ratio <= K1T)) { //Coefficients for all other packages, will probably use these
        b = B1T;
        m = M1T;
    } else if (ratio <= K2T) {
        b = B2T;
        m = M2T;
    } else if (ratio <= K3T) {
        b = B3T;
        m = M3T;
    } else if (ratio <= K4T) {
        b = B4T;
        m = M4T;
    } else if (ratio <= K5T) {
        b = B5T;
        m = M5T;
    } else if (ratio <= K6T) {
        b = B6T;
        m = M6T;
    } else if (ratio <= K7T) {
        b = B7T;
        m = M7T;
    } else if (ratio <= K8T) {
        b = B8T;
        m = M8T;
    }
#endif

    unsigned long temp_val;
    temp_val = ((channel0*b) - (channel1*m));

    //do not allow negative lux value
    if (temp_val < 0) {
        temp_val = 0;
    }

    //round lsb (2^(LUX_SCALE-1))
    temp_val += (1 << (LUX_SCALE-1));

    //strip off fractional portion
    uint32_t lux = temp_val >> LUX_SCALE;

    //signal I2C had no errors
    return lux;
}

uint32_t TSL2561::getFullLuminosity (void)
{
    // Enable the device by setting the control bit to 0x03
    enable();

    // Wait x ms for ADC to complete
    switch (_integration) {
        case TSL2561_INTEGRATIONTIME_13MS:
            wait(14/1000);
            break;
        case TSL2561_INTEGRATIONTIME_101MS:
            wait(102/1000);
            break;
        default:
            wait(403/1000);
            break;
    }

    uint32_t x; //will store data for luminosity
    //uint16_t x1; //will store data from channel 1 (optional to store data in steps)
    //uint16_t x0; //will store data from channel 0 (I just modified x)


    //Address the Ch1 lower data register and configure for Read Word
    char ch1_reg[1] = {TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN1_LOW};
    //Create array to store data
    char ch1_data[2] = {0,0};
    i2c.write(address, ch1_reg, 1);
    i2c.read(address, ch1_data, 2);
    // Merge bytes
    x = ch1_data[0] | (ch1_data[1] << 8);    // int
    x <<= 16;

    //Address the Ch1 lower data register and configure for Read Word
    char ch0_reg[1] = {TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN0_LOW};
    //Create array to store data
    char ch0_data[2] = {0,0};
    i2c.write(address, ch0_reg, 1);
    i2c.read(address, ch0_data, 2);
    // Merge bytes of channel 0 and channel 1
    x |= ch0_data[0] | (ch0_data[1] << 8);    // int

    disable();

    return x;
}

uint16_t TSL2561::getLuminosity (uint8_t channel)
{
    uint32_t x = getFullLuminosity();

    if (channel == 0) {
        // Reads two byte value from channel 0 (visible + infrared)
        return (x & 0xFFFF);
    } else if (channel == 1) {
        // Reads two byte value from channel 1 (infrared)
        return (x >> 16);
    } else if (channel == 2) {
        // Reads all and subtracts out just the visible
        return ( (x & 0xFFFF) - (x >> 16));
    }
    
    // unknown channel
    return 0;
}

void TSL2561::read_lux(void)
{
    uint32_t full_lums;
    //set to default gain (Note: going high gain to low is slower but more reliable, low gain to high gain is faster but less reliable)
    setTimingGain(TSL2561_INTEGRATIONTIME_13MS, TSL2561_GAIN_16X); // start at high gain and long readings
    wait(0.1);// give the TSL time to adjust, more time is more reliable but slower, less time lessreliable but faster
    //decreas gain/time if reading is to high (diable this block and change that^ line for fixed gain)
    //take reading
read_again:
    full_lums = getFullLuminosity();
    channal0 = (full_lums & 0xFFFF);
    channal1  = (full_lums >> 16);
    //If ir, or vis and ir, are within 5% of max then decreas the gain/time and repeat
    //Pick the corect time to match ^
    if(((channal0 > 4794)  | (channal1 > 4792))  & (_gain != TSL2561_GAIN_0X)){ //13ms
    //if(((channal0 > 35318) | (channal1 > 35318)) & (_gain != TSL2561_GAIN_0X)){ //101ms
    //if(((channal0 > 62258) | (channal1 > 62258)) & (_gain != TSL2561_GAIN_0X)){ //402ms
        setTimingGain(_integration, TSL2561_GAIN_0X); 
        wait(0.1);  // give the TSL time to adjust, more time is more reliable but slower, less time lessreliable but faster
        goto read_again;
    }
    
    //calculate lux from reading
    lux = calculateLux(channal0, channal1);
    
    //pc.printf("gain: %d time: %d, lux:%d\r\n", _gain, _integration, lux);
    //pc.printf("ch0:%d, ch1: %d\r\n", channal0, channal1);
}