Library for ISL29125 - I2C RGB ambient light sensor

Dependents:   ISL29125_demo FinalProject Final_NSR 4180FinalProject_copy ... more

Device documentation

Detailed information is available at Intersil

Calling the constructor

The constructor can be instantiaded in 3 ways:

Information

The first two pins (SDA and SCL) always need to be declared.

ISR mode

void RGBsensor_irq(void)    // User ISR
{
    ....
}

ISL29125 RGBsensor(PTE0, PTE1, PTD7, &RGBsensor_irq);          // sda, scl, irq, user isr

The user ISR is called whenever the upper/lower threshold is reached and/or when a conversion cycle is finished. The threshold (Threshold function) and end of conversion (IRQonCnvDone function) interrupts can be enabled separately.
In this mode, PTD7 is an interrupt input.

Limitation!

Calling the Read function at a high rate will result in a I2C failure.
Use a Ticker to call the Read function at regular intervals. Example : ISL29125_demo

SYNC mode

ISL29125 RGBsensor(PTE0, PTE1, PTD7);                        // sda, scl, sync input

In this mode, PTD7 is a digital output. Initially, the sensor is idle (no conversions) and can be started by calling the Run function.

Polling mode

ISL29125 RGBsensor(PTE0, PTE1);                              // sda, scl

In this mode, user defined functions are needed to poll the sensor status, read the sensor data, ....

ISL29125.cpp

Committer:
frankvnk
Date:
2014-05-28
Revision:
4:ae36914adb6d
Parent:
0:59ae5a71c902

File content as of revision 4:ae36914adb6d:

/******************************************************************************************************************************
 *****                                                                                                                    *****
 *****  Name: ISL29125.cpp                                                                                                *****
 *****  Date: 06/04/2014                                                                                                  *****
 *****  Auth: Frank Vannieuwkerke                                                                                         *****
 *****  Func: library for Intersil ISL29125 RGB Ambient light sensor with IR blocking filter                              *****
 *****                                                                                                                    *****
 *****  Additional info is available at                                                                                   *****
 *****  http://www.intersil.com/en/products/optoelectronics/ambient-light-sensors/light-to-digital-sensors/ISL29125.html  *****
 *****                                                                                                                    *****
 ******************************************************************************************************************************/

#include "ISL29125.h"

InterruptIn *_irqpin;
DigitalOut *_syncpin;

// ISL29125 I2C address
#define ISL29125_I2C_ADDR      0x88  // I2C address

// ISL29125 registers
// ------------------
#define ISL29125_REG_WHOAMI    0x00  // Read : device ID - should return 0x7D.
                                     // Write 0x46 : reset all registers to default.
#define ISL29125_REG_CFG1      0x01  // Operating mode, sensing range and start ADC.
#define ISL29125_REG_CFG2      0x02  // Active IR compensation.
#define ISL29125_REG_CFG3      0x03  // Interrupt threshold assignment, IRQ persist control, IRQ on conversion done.
#define ISL29125_REG_ITL_LO    0x04  // Interrupt Treshold Low value, low byte 
#define ISL29125_REG_ITL_HI    0x05  // Interrupt Treshold Low value, high byte 
#define ISL29125_REG_ITH_LO    0x06  // Interrupt Treshold High value, low byte 
#define ISL29125_REG_ITH_HI    0x07  // Interrupt Treshold High value, high byte 
#define ISL29125_REG_STATUS    0x08  // Status register : Interrupt, conversion done, brownout, R/G/B conversion busy.
#define ISL29125_REG_DATA_GLO  0x09  // Green data low byte.
#define ISL29125_REG_DATA_GHI  0x0A  // Green data high byte.
#define ISL29125_REG_DATA_RLO  0x0B  // Red data low byte.
#define ISL29125_REG_DATA_RHI  0x0C  // RED data high byte.
#define ISL29125_REG_DATA_BLO  0x0D  // Blue data low byte.
#define ISL29125_REG_DATA_BHI  0x0E  // Blue data high byte.

// ISL29125 register fields
// ------------------------
// Device register 0x00
#define ISL29125_RESET         0x46  // Software reset command.
#define ISL29125_WHOAMI        0x7D  // Device ID


// Config register 0x01
// RGB conversion operating modes (bits 0..2) - see header file for values
#define ISL29125_MODE_MASK     0xF8
// RGB data sensing range (bit 3)
// 0 : full scale range = 375 lux
// 1 : full scale range = 10000 lux
#define ISL29125_RNG_MASK      0xF7 
// ADC resolution (bit 4)
// 0 : 16-bit resolution
// 1 : 12-bit resolution
#define ISL29125_BITS_MASK     0xEF
// INT pin mode (bit 5)
// 0 : INT pin is SYNC input - ADC starts at rising edge of INT.
// 1 : INT pin is IRQ output.
#define ISL29125_SYNC_MASK     0xDF
#define ISL29125_SYNC_SHIFT       5


// Config register 0x02 - Active IR compensation (bit 0..5 = fine adjust, bit 7 = coarse adjust)
#define ISL29125_IR_CMP_MASK   0xBF
#define ISL29125_IRC_MAX       0xBF  // max. out IR compensation value (as per the datasheet specs)


// Config register 0x03 - IRQ
// IRQ assignment (bits 0,1) - see header file for values
#define ISL29125_INTSEL_MASK   0xFC
// IRQ persist control (bits 2,3) - activate IRQ after n consecutive transients(register value) : 1(0), 2(1), 4(2) or 8(3)
#define ISL29125_PRST_MASK     0xF3
// IRQ when conversion is done (bit 4)
// Enable conversion done IRQ
// Clear this bit to disable conversion done IRQ
#define ISL29125_CONVEN_MASK   0xEF
#define ISL29125_CONVEN_SHIFT     4


// Status register 0x08
// Interrupt status (1 when triggered)
#define ISL29125_RGBTHF_SHIFT  0
#define ISL29125_RGBTHF        (1 << ISL29125_RGBTHF_SHIFT)
// Conversion status (1 when done)
#define ISL29125_CONVENF_SHIFT 1
#define ISL29125_CONVENF       (1 << ISL29125_CONVENF_SHIFT)
// Brownout status (1 when brownout occured)
#define ISL29125_BOUTF_SHIFT   2
#define ISL29125_BOUTF         (1 << ISL29125_BOUTF_SHIFT)
// RGB conversion progress - see header file for values
#define ISL29125_RGBCF_MASK    0x30
#define ISL29125_RGBCF_SHIFT   4

ISL29125::ISL29125(PinName sda, PinName scl, PinName irqsync, void (*fptr)(void)) : _i2c(sda, scl)
{
    uint8_t cmd[2]; // cmd[0] = register address, cmd[1] = data
    _i2c.frequency(400000);
    _ismode = 0;
    // Set the ISL29125 in a known state : perform Software Reset
    cmd[0] = ISL29125_REG_WHOAMI;
    cmd[1] = ISL29125_RESET;
    writeRegs(cmd, 2);
    // Init the ISL29125 : Enable RGB operating mode, sensing range = 10000 lux, 16 bit resolution
    // Following registers remain at the default reset values :
    // Register 0x03 - Interrupt source (none), persist control (1) , INT when conversion (0).
    // Register 0x04, 0x05 - Low threshold interrupt : 0x0000
    // Register 0x06, 0x07 - High threshold interrupt : 0xFFFF
    cmd[0] = ISL29125_REG_CFG1;
    cmd[1] = (uint8_t)(ISL29125_RGB | ~ISL29125_RNG_MASK);
    // When irqsync is nonzero and no fptr is declared, use Sync mode (only start ADC on rising edge at sync output)
    if((irqsync != NC) && (fptr == NULL))
    {
        _ismode = 2;
        cmd[1] |= ~ISL29125_SYNC_MASK;
        _syncpin = new DigitalOut(irqsync);             // Create DigitalOut pin
        _syncpin->write(0);
    }
    writeRegs(cmd, 2);
    // Max. out IR compensation value (as per the datasheet specs)
    cmd[0] = ISL29125_REG_CFG2;
    cmd[1] = ISL29125_IRC_MAX;
    writeRegs(cmd, 2);
    // When both irqsync and fptr are nonzero, we use InterruptIn: irqsync pin is interrupt input and Attach local ISR (calls user-ISR).
    if((irqsync != NC) && (fptr != NULL))
    {
        _ismode = 1;
        _irqpin = new InterruptIn(irqsync);             // Create InterruptIn pin
        _irqpin->fall(this, &ISL29125::_alsISR);        // Attach falling interrupt to local ISR
        _fptr.attach(fptr);                             // Attach function pointer to user function
    }
}

uint8_t ISL29125::Status(void)
{
    return (readReg( ISL29125_REG_STATUS));
}

uint8_t ISL29125::WhoAmI(void)
{
    return (readReg( ISL29125_REG_WHOAMI));
}

bool ISL29125::Read(uint8_t color, uint16_t * data) {
    uint8_t i, addr = 0, reg_cnt = 2, res[6];
    if(Status() & ISL29125_CONVENF)             // Only return data when a conversion is finished.
    {
        switch (color)
        {
            case ISL29125_R:
                addr = ISL29125_REG_DATA_RLO;
                break;
            case ISL29125_G:
                addr = ISL29125_REG_DATA_GLO;
                break;
            case ISL29125_B:
                addr = ISL29125_REG_DATA_BLO;
                break;
            case ISL29125_RGB:
                addr = ISL29125_REG_DATA_GLO;
                reg_cnt = 6;
                break;
            default:
                return(0);
        }
        readRegs(addr, res, reg_cnt);
        for(i=0 ; i<reg_cnt-1 ; i+=2)
            *(data+(i/2)) = (res[i+1] << 8) | (res[i]);
        return (1);
    }
    else return(0);
}

uint16_t ISL29125::Threshold(uint8_t reg, uint16_t thres) {
    if(reg == ISL29125_LTH_R || reg == ISL29125_HTH_R)
    {
        uint8_t res[2];
        readRegs(reg*2, res, 2);
        return(res[1] << 8 | res[0]);
    }
    else
    {
        uint8_t data[3];
        data[0] = reg;
        data[1] = thres & 0xff;
        data[2] = (thres >> 8) & 0xff;
        writeRegs(data, 3);
    }
    return(thres);
}

uint8_t ISL29125::RGBmode(uint8_t RGBmode) {
    if(RGBmode == 0xff)
    {
        return(readReg( ISL29125_REG_CFG1) & ~ISL29125_MODE_MASK);
    }
    else
    {
        if((RGBmode != ISL29125_G) && (RGBmode != ISL29125_R) && (RGBmode != ISL29125_B) &&
           (RGBmode != ISL29125_RG) && (RGBmode != ISL29125_BG) && (RGBmode != ISL29125_RGB) &&
           (RGBmode != ISL29125_STBY) && (RGBmode != ISL29125_OFF)) return(0xff);
        uint8_t data[2];
        data[0] = ISL29125_REG_CFG1;
        data[1] = (readReg( ISL29125_REG_CFG1) & ISL29125_MODE_MASK) | RGBmode;
        writeRegs(data, 2);
    }
    return(RGBmode);
}

uint8_t ISL29125::Range(uint8_t range) {
    if(range == 0xff)
    {
        return(readReg( ISL29125_REG_CFG1) & ~ISL29125_RNG_MASK);
    }
    else
    {
        uint8_t data[2];
        if((range != ISL29125_375LX) && (range != ISL29125_10KLX)) return(0xff);
        data[0] = ISL29125_REG_CFG1;
        data[1] = (readReg( ISL29125_REG_CFG1) & ISL29125_RNG_MASK) | range;
        writeRegs(data, 2);
    }
    return(range);
}

uint8_t ISL29125::Resolution(uint8_t resol) {
    if(resol == 0xff)
    {
        return(readReg( ISL29125_REG_CFG1) & ~ISL29125_BITS_MASK);
    }
    else
    {
        uint8_t data[2];
        if((resol != ISL29125_16BIT) && (resol != ISL29125_12BIT)) return(0xff);
        data[0] = ISL29125_REG_CFG1;
        data[1] = (readReg( ISL29125_REG_CFG1) & ISL29125_BITS_MASK) | resol;
        writeRegs(data, 2);
    }
    return(resol);
}

uint8_t ISL29125::Persist(uint8_t persist) {
    if(persist == 0xff)
    {
        return(readReg( ISL29125_REG_CFG3) & ~ISL29125_PRST_MASK);
    }
    else
    {
        uint8_t data[2];
        if((persist != ISL29125_PERS1) && (persist != ISL29125_PERS2) && (persist != ISL29125_PERS4) && (persist != ISL29125_PERS8)) return(0xff);
        data[0] = ISL29125_REG_CFG3;
        data[1] = (readReg( ISL29125_REG_CFG3) & ISL29125_PRST_MASK) | persist;
        writeRegs(data, 2);
    }
    return(persist);
}

uint8_t ISL29125::IRQonCnvDone(uint8_t irqen) {
    uint8_t tmprgb;
    uint8_t data[2];
    if(irqen == 0xff)
    {
        return((readReg( ISL29125_REG_CFG3) & ~ISL29125_CONVEN_MASK) >> ISL29125_CONVEN_SHIFT);
    }
    else
    {
        if((irqen != true) && (irqen != false)) return(0xff);
        tmprgb = RGBmode();     // Save current ADC operating mode
        RGBmode(ISL29125_OFF);  // Stop ADC conversion (changing IRQonCnvDone while ADC is running results in i2c failure)
        data[0] = ISL29125_REG_CFG3;
        data[1] = (readReg( ISL29125_REG_CFG3) & ISL29125_CONVEN_MASK) | (irqen << ISL29125_CONVEN_SHIFT);
        writeRegs(data, 2);
        RGBmode(tmprgb);        // Restore ADC operating mode
    }
    return(irqen);
}

uint8_t ISL29125::IRQonColor(uint8_t RGBmode) {
    if(RGBmode == 0xff)
    {
        return(readReg( ISL29125_REG_CFG3) & ~ISL29125_INTSEL_MASK);
    }
    else
    {
        uint8_t data[2];
        if((RGBmode != ISL29125_G) && (RGBmode != ISL29125_R) && (RGBmode != ISL29125_B) && (RGBmode != ISL29125_OFF)) return(0xff);
        data[0] = ISL29125_REG_CFG3;
        data[1] = (readReg( ISL29125_REG_CFG3) & ISL29125_INTSEL_MASK) | RGBmode;
        writeRegs(data, 2);
    }
    return(RGBmode);
}

uint8_t ISL29125::IRcomp(uint8_t ircomp) {
    if(ircomp == 0xff)
    {
        return(readReg( ISL29125_REG_CFG2) & ISL29125_IR_CMP_MASK);
    }
    else
    {
        uint8_t data[2];
        if(((ircomp > 63) && (ircomp < 128)) || (ircomp > 191)) return(0xff); // Range must be between 0..63 or 128..191
        data[0] = ISL29125_REG_CFG2;
        data[1] = ircomp & ISL29125_IR_CMP_MASK;
        writeRegs(data, 2);
    }
    return(ircomp);
}

bool ISL29125::Run(void) {
    if(_ismode == 2)   // Only allow write to sync pin when irqsync pin was declared without the ISR pointer.
    {
        _syncpin->write(1);
        return(1);
    }
    return(0);
}

void ISL29125::_alsISR(void)
{
    Status();
    _fptr.call();
}

void ISL29125::i2cfail(void)
{
    printf("I2C fail\r\n");
    while(1);
}

void ISL29125::readRegs(uint8_t addr, uint8_t * data, uint8_t len) {
    char t[1] = {addr};
    if(_i2c.write(ISL29125_I2C_ADDR, t, 1, true)) i2cfail();
    if(_i2c.read(ISL29125_I2C_ADDR, (char *)data, len)) i2cfail();
}

uint8_t ISL29125::readReg(uint8_t addr) {
    char t[1] = {addr};
    if(_i2c.write(ISL29125_I2C_ADDR, t, 1)) i2cfail();
    if(_i2c.read(ISL29125_I2C_ADDR, t, 1)) i2cfail();
    return t[0];
}

void ISL29125::writeRegs(uint8_t * data, uint8_t len) {
    if(_i2c.write(ISL29125_I2C_ADDR, (char *)data, len)) i2cfail();
}