This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Read once from ISL29023



I can't seem to get my read once functions to work for the ISL29023. For some reason I always end up getting data from my previous attempt at reading.

i.e.

n reading(desklamp off): amb=296.493, ir=204.025;

n+1 reading(desklamp on): amb=306.610, ir=210.987;

n+2 reading(desklamp off): amb=999.984, ir=688.118;

n+3 reading(desklamp on): amb=287.597, ir=1297.904;

It seems after I call ISL29023ReadModifyWrite, ISL29023AppCallback is called with a I2CM_STATUS_SUCCESS.

The same occurs after my ISL29023DataRead invocation. Yet when ISL29023LightVisibleGetFloat is called, it's retrieving old data or the data register on the sensor hasn't been updated yet.

Here is the code I've been working on.

#include "isl29023.h"

// TivaWare Library
#include "driverlib/gpio.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "sensorlib/hw_isl29023.h"
#include "sensorlib/isl29023.h"

// Custom Library
#include "customlib/i2c/i2cconfig.h"
#include "customlib/sdcard/sdcard.h"
#include "customlib/stdlib/types.h"
#include "customlib/stdlib/xprintf.h"
#include "customlib/uart/uartprint.h"


/* ISL29023 Part differentiation
 * Receives an interrupt triggered on this GPIO when light values
 * are outside of the thresholds defined in isl29023Configure */
#ifdef PART_TM4C123GH6PM
    #define ISL_PERIPH_GPIO SYSCTL_PERIPH_GPIOE
    #define ISLGPIO_INT INT_GPIOE
    #define ISLGPIO_INT_PORT_BASE GPIO_PORTE_BASE
    #define ISLGPIO_INT_PIN GPIO_PIN_5
#elif PART_TM4C129XNCZAD
    #define ISL_PERIPH_GPIO SYSCTL_PERIPH_GPIOF
    #define ISLGPIO_INT INT_GPIOF
    #define ISLGPIO_INT_PORT_BASE GPIO_PORTF_BASE
    #define ISLGPIO_INT_PIN GPIO_PIN_3
#endif

bool isl29023Enabled = false;

// Global instance structure for the ISL29023 sensor driver.
tISL29023 isl29023Inst;

volatile uint_fast8_t dataFlag; // flag to indicate the data is ready
volatile uint_fast8_t errorFlag; // error flag to store the error condition if encountered
volatile unsigned long intensityFlag; // flag to indicate light intensity is outside of sensor range

/* Constants to hold the floating point version of the thresholds for each
 * range setting. Numbers represent an 81% and 19 % threshold levels. This
 * creates a +/- 1% hysteresis band between range adjustments. */
const float thresholdHigh[4] = {
    810.0f, 3240.0f, 12960.0f, 64000.0f
};
const float thresholdLow[4] = {
    0.0f, 760.0f, 3040.0f, 12160.0f
};

void isl29023Configure(void) {
    /* Configure and Enable the GPIO interrupt. Used for INT signal from
     * the ISL29023 */
    ROM_SysCtlPeripheralEnable(ISL_PERIPH_GPIO);
    ROM_GPIOPinTypeGPIOInput(ISLGPIO_INT_PORT_BASE, ISLGPIO_INT_PIN);
    GPIOIntEnable(ISLGPIO_INT_PORT_BASE, ISLGPIO_INT_PIN);
    ROM_GPIOIntTypeSet(ISLGPIO_INT_PORT_BASE, ISLGPIO_INT_PIN, GPIO_FALLING_EDGE);
    ROM_IntEnable(ISLGPIO_INT);

    ROM_SysCtlPeripheralSleepEnable(ISL_PERIPH_GPIO);

    // Set the interrupt priority - low
    ROM_IntPrioritySet(INT_GPIOE, 0x80);

    // Initialize the ISL29023 Driver.
    ISL29023Init(&isl29023Inst, &g_sI2CInst, ISL29023_I2C_ADDRESS,
                 isl29023AppCallback, &isl29023Inst);

    // Wait for transaction to complete
    isl29023AppI2CWait(__FILE__, __LINE__);

    // TODO: Do we need to do a ReadModifyWrite here before conf. thresholds?

    // Configure the upper threshold to 80% of maximum value
    isl29023Inst.pui8Data[1] = 0xCC;
    isl29023Inst.pui8Data[2] = 0xCC;
    ISL29023Write(&isl29023Inst, ISL29023_O_INT_HT_LSB,
                  isl29023Inst.pui8Data, 2, isl29023AppCallback,
                  &isl29023Inst);

    // Wait for transaction to complete
    isl29023AppI2CWait(__FILE__, __LINE__);

    // Configure the lower threshold to 20% of maximum value
    isl29023Inst.pui8Data[1] = 0x33;
    isl29023Inst.pui8Data[2] = 0x33;
    ISL29023Write(&isl29023Inst, ISL29023_O_INT_LT_LSB,
                  isl29023Inst.pui8Data, 2, isl29023AppCallback,
                  &isl29023Inst);

    // Wait for transaction to complete
    isl29023AppI2CWait(__FILE__, __LINE__);

    // Let's us know the isl29023 is configured for use
    isl29023Enabled = true;

    PRINTF("ISL29023 Configured"); NL;
}

bool isl29023IsEnabled(void) {
    return isl29023Enabled;
}

void isl29023AppCallback(void *pvCallbackData, uint_fast8_t status) {
    // If the transaction succeeded set the data flag to indicate to
    // application that this transaction is complete and data may be ready.

    if(status == I2CM_STATUS_SUCCESS) {
        dataFlag = 1;
    }

    // Store the most recent status in case it was an error condition
    errorFlag = status;
}

void isl29023IntGPIO(void) {
    unsigned long status;

    PRINTF("GPIOF Interrupt called"); NL;

    status = GPIOIntStatus(ISLGPIO_INT_PORT_BASE, true);

    // Clear all the pin interrupts that are set
    GPIOIntClear(ISLGPIO_INT_PORT_BASE, status);

    if(status & ISLGPIO_INT_PIN) {
        // ISL29023 has indicated that the light level has crossed outside of
        // the intensity threshold levels set in INT_LT and INT_HT registers.
        intensityFlag = 1;
    }
}

void isl29023AppErrorHandler(char * filename, uint_fast32_t line) {
    // Set terminal color to red and print error status and locations
    PRINT_RED;
    PRINTF("Error: %d, File: %s, Line: %d\n"
           "See I2C status definitions in utils\\i2cm_drv.h\n",
           errorFlag, filename, line);

    // Return terminal color to normal
    PRINT_NORMAL;

    // Go to sleep wait for interventions.  A more robust application could
    // attempt corrective actions here.
    while(1) {
        // Do Nothing
    }
}

void isl29023AppI2CWait(char * filename, uint_fast32_t line) {
    // Put the processor to sleep while we wait for the I2C driver to
    // indicate that the transaction is complete.
    while((dataFlag == 0) && (errorFlag == 0)) {
        // Do Nothing
    }

    // If an error occurred call the error handler immediately.
    if(errorFlag) {
        isl29023AppErrorHandler(filename, line);
    }

    // clear the data flag for next use.
    dataFlag = 0;
}

void isl29023AppAdjustRange(void) {
    float ambient;
    uint8_t newRange;

    PRINTF("Adjusting Range"); NL;

    newRange = isl29023Inst.ui8Range;

    // Get a local floating point copy of the latest light data
    ISL29023DataLightVisibleGetFloat(&isl29023Inst, &ambient);

    // Check if we crossed the upper threshold.
    if(ambient > thresholdHigh[isl29023Inst.ui8Range]) {
        // The current intensity is over our threshold so adjsut the range
        // accordingly
        if(isl29023Inst.ui8Range < ISL29023_CMD_II_RANGE_64K) {
            newRange = isl29023Inst.ui8Range + 1;
        }
    }

    // Check if we crossed the lower threshold
    if(ambient < thresholdLow[isl29023Inst.ui8Range]) {
        // If possible go to the next lower range setting and reconfig the
        // thresholds.
        if(isl29023Inst.ui8Range > ISL29023_CMD_II_RANGE_1K) {
            newRange = isl29023Inst.ui8Range - 1;
        }
    }

    // If the desired range value changed then send the new range to the sensor
    if(newRange != isl29023Inst.ui8Range) {
        ISL29023ReadModifyWrite(&isl29023Inst, ISL29023_O_CMD_II,
                                ~ISL29023_CMD_II_RANGE_M, newRange,
                                isl29023AppCallback, &isl29023Inst);

        // Wait for transaction to complete
        isl29023AppI2CWait(__FILE__, __LINE__);
    }
}

void isl29023UpdateAmbient(int32_t * integerPart, int32_t * fractionPart) {
    float ambient;
    uint8_t mask;

    dataFlag = 0; // Reset the data flag

    PRINTF("Getting Ambient Light Reading"); NL;

    /* Configure the ISL29023 to measure ambient light once. */
    mask = (ISL29023_CMD_I_OP_MODE_M
            | ISL29023_CMD_I_INT_PERSIST_1
            | ISL29023_CMD_I_INT_FLAG_M);
    ISL29023ReadModifyWrite(&isl29023Inst,
                            ISL29023_O_CMD_I,
                            ~mask,
                            ISL29023_CMD_I_OP_MODE_ALS_LOW,
                            isl29023AppCallback,
                            &isl29023Inst);

    // Wait for transaction to complete
    isl29023AppI2CWait(__FILE__, __LINE__);

    // Go get the latest data from the sensor.
    ISL29023DataRead(&isl29023Inst, isl29023AppCallback, &isl29023Inst);

    // Wait for transaction to complete
    isl29023AppI2CWait(__FILE__, __LINE__);

    // Get a local floating point copy of the latest light data
    ISL29023DataLightVisibleGetFloat(&isl29023Inst, &ambient);

    /* Check if the intensity of light has crossed a threshold. If so
     * then adjust lux range and get another reading. */
    while(intensityFlag) {
        /* Disable the low priority interrupts leaving only the I2C
         * interrupt enabled. */
        ROM_IntPriorityMaskSet(0x40);

        // Reset the intensity trigger flag.
        intensityFlag = 0;

        // Adjust the lux range.
        isl29023AppAdjustRange();

        // Now we must manually clear the flag in the ISL29023 register.
        ISL29023Read(&isl29023Inst, ISL29023_O_CMD_I,
                     isl29023Inst.pui8Data, 1, isl29023AppCallback,
                     &isl29023Inst);

        // Wait for transaction to complete
        isl29023AppI2CWait(__FILE__, __LINE__);

        // Disable priority masking so all interrupts are enabled.
        ROM_IntPriorityMaskSet(0);

        /* Configure the ISL29023 to measure infrared light once. */
        ISL29023ReadModifyWrite(&isl29023Inst,
                                ISL29023_O_CMD_I,
                                ~mask,
                                ISL29023_CMD_I_OP_MODE_ALS_LOW,
                                isl29023AppCallback,
                                &isl29023Inst);

        // Wait for transaction to complete
        isl29023AppI2CWait(__FILE__, __LINE__);

        // Go get the latest data from the sensor.
        ISL29023DataRead(&isl29023Inst, isl29023AppCallback, &isl29023Inst);

        // Wait for transaction to complete
        isl29023AppI2CWait(__FILE__, __LINE__);
    }

    // Split the float value into an integer and a fraction part for printing/logging
    typesFloatTo2Ints(ambient, integerPart, fractionPart);

    TAB; PRINTF("Ambient (lux): %05d.%03d", *integerPart, *fractionPart); NL;
}

void isl29023UpdateInfrared(int32_t * integerPart, int32_t * fractionPart) {
    float infrared;
    uint8_t mask;

    dataFlag = 0; // Reset the data flag

    PRINTF("Getting Infrared Light Reading"); NL;

    /* Configure the ISL29023 to measure infrared light once. */
    mask = (ISL29023_CMD_I_OP_MODE_M
            | ISL29023_CMD_I_INT_PERSIST_1
            | ISL29023_CMD_I_INT_FLAG_M);
    ISL29023ReadModifyWrite(&isl29023Inst,
                            ISL29023_O_CMD_I,
                            ~mask,
                            ISL29023_CMD_I_OP_MODE_IR_ONCE,
                            isl29023AppCallback,
                            &isl29023Inst);

    // Wait for transaction to complete
    isl29023AppI2CWait(__FILE__, __LINE__);

    // Go get the latest data from the sensor.
    ISL29023DataRead(&isl29023Inst, isl29023AppCallback, &isl29023Inst);

    // Wait for transaction to complete
    isl29023AppI2CWait(__FILE__, __LINE__);

    /* Check if the intensity of light has crossed a threshold. If so
     * then adjust lux range and get another reading. */
    while(intensityFlag) {
        /* Disable the low priority interrupts leaving only the I2C
         * interrupt enabled. */
        ROM_IntPriorityMaskSet(0x40);

        // Reset the intensity trigger flag.
        intensityFlag = 0;

        // Adjust the lux range.
        isl29023AppAdjustRange();

        // Now we must manually clear the flag in the ISL29023 register.
        ISL29023Read(&isl29023Inst, ISL29023_O_CMD_I,
                     isl29023Inst.pui8Data, 1, isl29023AppCallback,
                     &isl29023Inst);

        // Wait for transaction to complete
        isl29023AppI2CWait(__FILE__, __LINE__);

        // Disable priority masking so all interrupts are enabled.
        ROM_IntPriorityMaskSet(0);

        /* Configure the ISL29023 to measure infrared light once. */
        ISL29023ReadModifyWrite(&isl29023Inst,
                                ISL29023_O_CMD_I,
                                ~mask,
                                ISL29023_CMD_I_OP_MODE_IR_ONCE,
                                isl29023AppCallback,
                                &isl29023Inst);

        // Wait for transaction to complete
        isl29023AppI2CWait(__FILE__, __LINE__);

        // Go get the latest data from the sensor.
        ISL29023DataRead(&isl29023Inst, isl29023AppCallback, &isl29023Inst);

        // Wait for transaction to complete
        isl29023AppI2CWait(__FILE__, __LINE__);
    }

    // Get a local floating point copy of the latest light data
    ISL29023DataLightIRGetFloat(&isl29023Inst, &infrared);

    // Split the float value into an integer and a fraction part for printing/logging
    typesFloatTo2Ints(infrared, integerPart, fractionPart);

    TAB; PRINTF("Infrared (lux): %05d.%03d", *integerPart, *fractionPart); NL;
}

void isl29023UpdateAll(int32_t * ambIntegerPart,
                       int32_t * ambFractionPart,
                       int32_t * irIntegerPart,
                       int32_t * irFractionPart) {

    isl29023UpdateAmbient(ambIntegerPart, ambFractionPart);
    isl29023UpdateInfrared(irIntegerPart, irFractionPart);
}

void isl29023LogReading(void) {
    int32_t ambIntegerPart, ambFractionPart;
    int32_t irIntegerPart, irFractionPart;

    char islLogString[39];

    uint8_t logErr;

    PRINTF("ISL29023 Logger"); NL;

    isl29023UpdateAll(&ambIntegerPart, &ambFractionPart,
                      &irIntegerPart, &irFractionPart);

    logErr = sdcardFileOpenWrite(ISL29023_LOG_FILE);

    PRINTF("Opening Log %s: %d", ISL29023_LOG_FILE, logErr); NL;

    xsprintf(islLogString, "ISL29023::amb:%05d.%03d,ir:%05d.%03d;\n",
             ambIntegerPart, ambFractionPart,
             irIntegerPart, irFractionPart);

    logErr = sdcardFileAppendLine(islLogString);

    PRINTF("Line to Log:"); NL;
    TAB; PRINTF("%s", islLogString);

    PRINTF("Append: %d", logErr); NL;

    logErr = sdcardFileClose();

    PRINTF("Closing Log %s: %d", ISL29023_LOG_FILE, logErr); NL; NL;
}

note: the while(intensityFlag) loops essentially do nothing because the GPIO interrupt doesn't happen until the program has finished executing (maybe this is when the light readings are actually updated?). I would like to get this working by manually checking the intensity flag pin (PE5/PF3) instead of waiting for an interrupt.

I'm also trying to figure out if I can set it up to read both the ambient and infrared sensors at the same time. i.e.

/* Configure the ISL29023 to measure ambient and infrared light once. */
ISL29023ReadModifyWrite(&isl29023Inst,
                        ISL29023_O_CMD_I,
                        ~mask,
                        ISL29023_CMD_I_OP_MODE_ALS_LOW
                        | ISL29023_CMD_I_OP_MODE_IR_ONCE,
                        isl29023AppCallback,
                        &isl29023Inst);

It seems I can call ISL29023DataLightVisibleGetFloat and ISL29023DataLightIRGetFloat one after the other and get distinct values so I'm assuming they are stored in separate registers.

Any glaring mistakes in what I'm doing here?

  • So I've added a little loop to see exactly how long it's taking to get a new reading.

    void isl29023UpdateAmbient(int32_t * integerPart, int32_t * fractionPart) {
        float ambient;
        uint8_t mask;
    
        dataFlag = 0; // Reset the data flag
    
        PRINTF("Getting Ambient Light Reading"); NL;
    
        /* Configure the ISL29023 to measure ambient light once. */
        mask = (ISL29023_CMD_I_OP_MODE_M
                | ISL29023_CMD_I_INT_PERSIST_1
                | ISL29023_CMD_I_INT_FLAG_M);
        ISL29023ReadModifyWrite(&isl29023Inst,
                                ISL29023_O_CMD_I,
                                ~mask,
                                ISL29023_CMD_I_OP_MODE_ALS_LOW,
                                isl29023AppCallback,
                                &isl29023Inst);
    
        // Wait for transaction to complete
        isl29023AppI2CWait(__FILE__, __LINE__);
    
        // Go get the latest data from the sensor.
        ISL29023DataRead(&isl29023Inst, isl29023AppCallback, &isl29023Inst);
    
        // Wait for transaction to complete
        isl29023AppI2CWait(__FILE__, __LINE__);
    
        /* Check if the intensity of light has crossed a threshold. If so
         * then adjust lux range and get another reading. */
        while(intensityFlag) {
            /* Disable the low priority interrupts leaving only the I2C
             * interrupt enabled. */
            ROM_IntPriorityMaskSet(0x40);
    
            // Reset the intensity trigger flag.
            intensityFlag = 0;
    
            // Adjust the lux range.
            isl29023AppAdjustRange();
    
            // Now we must manually clear the flag in the ISL29023 register.
            ISL29023Read(&isl29023Inst, ISL29023_O_CMD_I,
                         isl29023Inst.pui8Data, 1, isl29023AppCallback,
                         &isl29023Inst);
    
            // Wait for transaction to complete
            isl29023AppI2CWait(__FILE__, __LINE__);
    
            // Disable priority masking so all interrupts are enabled.
            ROM_IntPriorityMaskSet(0);
    
            /* Configure the ISL29023 to measure infrared light once. */
            ISL29023ReadModifyWrite(&isl29023Inst,
                                    ISL29023_O_CMD_I,
                                    ~mask,
                                    ISL29023_CMD_I_OP_MODE_ALS_LOW,
                                    isl29023AppCallback,
                                    &isl29023Inst);
    
            // Wait for transaction to complete
            isl29023AppI2CWait(__FILE__, __LINE__);
    
            // Go get the latest data from the sensor.
            ISL29023DataRead(&isl29023Inst, isl29023AppCallback, &isl29023Inst);
    
            // Wait for transaction to complete
            isl29023AppI2CWait(__FILE__, __LINE__);
        }
    
        // Get a local floating point copy of the latest light data
        ISL29023DataLightVisibleGetFloat(&isl29023Inst, &ambient);
    
        // TESTING. How long before we get a new reading?
        uint32_t i = 0;
        float temp;
        while (1) {
            i++;
    
            // Go get the latest data from the sensor.
            ISL29023DataRead(&isl29023Inst, isl29023AppCallback, &isl29023Inst);
    
            // Wait for transaction to complete
            isl29023AppI2CWait(__FILE__, __LINE__);
    
            // Get a local floating point copy of the latest light data
            ISL29023DataLightVisibleGetFloat(&isl29023Inst, &temp);
    
            if (temp != ambient) {
                PRINTF("Round %d:", i); NL;
                typesFloatTo2Ints(ambient, integerPart, fractionPart);
                TAB; PRINTF("ambient: %05d.%03d", *integerPart, *fractionPart); NL;
                typesFloatTo2Ints(temp, integerPart, fractionPart);
                TAB; PRINTF("temp: %05d.%03d", *integerPart, *fractionPart); NL;
                break;
            }
    
        }
    
        // Split the float value into an integer and a fraction part for printing/logging
        //typesFloatTo2Ints(ambient, integerPart, fractionPart);
    
        TAB; PRINTF("Ambient (lux): %05d.%03d", *integerPart, *fractionPart); NL;
    }

    running this the code inside the loop executes ~625 times before getting a new ambient value, with output.

    ISL29023 Logger                                                                 
    Getting Ambient Light Reading                                                   
    GPIOF Interrupt called                                                          
    Round 625:                                                                      
            ambient: 00062.637                                                      
            temp: 00140.426                                                         
            Ambient (lux): 00140.426                                                
    Getting Infrared Light Reading                                                  
    Adjusting Range                                                                 
            Infrared (lux): 00096.631                                               
    Opening Log /LOG/ISL29023.LOG: 0                                                
    Line to Log:                                                                    
            ISL29023::amb:00140.426,ir:00096.631;                                   
    Append: 0                                                                       
    Closing Log /LOG/ISL29023.LOG: 0  

    The "Adjusting Range" in the output also indicates the intensityFlag has been set by the GPIO interrupt at some point before getting the IR reading.

    So this all looks to me as though

    ISL29023ReadModifyWrite(&isl29023Inst,
                                ISL29023_O_CMD_I,
                                ~mask,
                                ISL29023_CMD_I_OP_MODE_ALS_LOW,
                                isl29023AppCallback,
                                &isl29023Inst);

    Is calling isl29023AppCallback well before it's getting the sensor data. Is this the intended functionality? How can I force an appropriate wait between ISL29023ReadModifyWrite and ISL29023DataRead?