/* * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ /*---------------------------------------------------------------------------*/ /** * \addtogroup sensortag-cc26xx-opt-sensor * @{ * * \file * Driver for the Sensortag Opt3001 light sensor */ /*---------------------------------------------------------------------------*/ #include "contiki.h" #include "lib/sensors.h" #include "sys/ctimer.h" #include "opt-3001-sensor.h" /*---------------------------------------------------------------------------*/ #include #include /*---------------------------------------------------------------------------*/ #include #include #include #include /*---------------------------------------------------------------------------*/ #define DEBUG 0 #if DEBUG #define PRINTF(...) printf(__VA_ARGS__) #else #define PRINTF(...) #endif /*---------------------------------------------------------------------------*/ #ifndef Board_OPT3001_ADDR # error "Board file doesn't define I2C address Board_OPT3001_ADDR" #endif /* Slave address */ #define OPT_3001_I2C_ADDRESS Board_OPT3001_ADDR /*---------------------------------------------------------------------------*/ /* Register addresses */ #define REG_RESULT 0x00 #define REG_CONFIGURATION 0x01 #define REG_LOW_LIMIT 0x02 #define REG_HIGH_LIMIT 0x03 #define REG_MANUFACTURER_ID 0x7E #define REG_DEVICE_ID 0x7F /*---------------------------------------------------------------------------*/ /* * Configuration Register Bits and Masks. * We use uint16_t to read from / write to registers, meaning that the * register's MSB is the variable's LSB. */ #define CFG_RN 0x00F0 /* [15..12] Range Number */ #define CFG_CT 0x0008 /* [11] Conversion Time */ #define CFG_M 0x0006 /* [10..9] Mode of Conversion */ #define CFG_OVF 0x0001 /* [8] Overflow */ #define CFG_CRF 0x8000 /* [7] Conversion Ready Field */ #define CFG_FH 0x4000 /* [6] Flag High */ #define CFG_FL 0x2000 /* [5] Flag Low */ #define CFG_L 0x1000 /* [4] Latch */ #define CFG_POL 0x0800 /* [3] Polarity */ #define CFG_ME 0x0400 /* [2] Mask Exponent */ #define CFG_FC 0x0300 /* [1..0] Fault Count */ /* Possible Values for CT */ #define CFG_CT_100 0x0000 #define CFG_CT_800 CFG_CT /* Possible Values for M */ #define CFG_M_CONTI 0x0004 #define CFG_M_SINGLE 0x0002 #define CFG_M_SHUTDOWN 0x0000 /* Reset Value for the register 0xC810. All zeros except: */ #define CFG_RN_RESET 0x00C0 #define CFG_CT_RESET CFG_CT_800 #define CFG_L_RESET 0x1000 #define CFG_DEFAULTS (CFG_RN_RESET | CFG_CT_100 | CFG_L_RESET) /* Enable / Disable */ #define CFG_ENABLE_CONTINUOUS (CFG_M_CONTI | CFG_DEFAULTS) #define CFG_ENABLE_SINGLE_SHOT (CFG_M_SINGLE | CFG_DEFAULTS) #define CFG_DISABLE CFG_DEFAULTS /*---------------------------------------------------------------------------*/ /* Register length */ #define REGISTER_LENGTH 2 /*---------------------------------------------------------------------------*/ /* Sensor data size */ #define DATA_LENGTH 2 /*---------------------------------------------------------------------------*/ /* Byte swap of 16-bit register value */ #define HI_UINT16(a) (((a) >> 8) & 0xFF) #define LO_UINT16(a) ((a) & 0xFF) #define SWAP16(v) ((LO_UINT16(v) << 8) | HI_UINT16(v)) #define LSB16(v) (LO_UINT16(v)), (HI_UINT16(v)) #define MSB16(v) (HI_UINT16(v)), (LO_UINT16(v)) /*---------------------------------------------------------------------------*/ typedef struct { volatile OPT_3001_STATUS status; } OPT_3001_Object; static OPT_3001_Object opt_3001; /*---------------------------------------------------------------------------*/ /* Wait SENSOR_STARTUP_DELAY for the sensor to be ready - 125ms */ #define SENSOR_STARTUP_DELAY (CLOCK_SECOND >> 3) static struct ctimer startup_timer; /*---------------------------------------------------------------------------*/ static I2C_Handle i2cHandle; /*---------------------------------------------------------------------------*/ static bool i2c_write_read(void *writeBuf, size_t writeCount, void *readBuf, size_t readCount) { I2C_Transaction i2cTransaction = { .writeBuf = writeBuf, .writeCount = writeCount, .readBuf = readBuf, .readCount = readCount, .slaveAddress = OPT_3001_I2C_ADDRESS, }; return I2C_transfer(i2cHandle, &i2cTransaction); } #define i2c_write(writeBuf, writeCount) i2c_write_read(writeBuf, writeCount, NULL, 0) #define i2c_read(readBuf, readCount) i2c_write_read(NULL, 0, readBuf, readCount) /*---------------------------------------------------------------------------*/ static bool sensor_init(void) { if (i2cHandle) { return true; } I2C_Params i2cParams; I2C_Params_init(&i2cParams); i2cParams.transferMode = I2C_MODE_BLOCKING; i2cParams.bitRate = I2C_400kHz; i2cHandle = I2C_open(Board_I2C0, &i2cParams); if (i2cHandle == NULL) { return false; } opt_3001.status = OPT_3001_STATUS_DISABLED; return true; } /*---------------------------------------------------------------------------*/ /** * \brief Turn the sensor on/off * \param enable TRUE: on, FALSE: off */ static bool sensor_enable(bool enable) { uint16_t data = (enable) ? CFG_ENABLE_SINGLE_SHOT : CFG_DISABLE; uint8_t cfg_data[] = { REG_CONFIGURATION, LSB16(data) }; return i2c_write(cfg_data, sizeof(cfg_data)); } /*---------------------------------------------------------------------------*/ static void notify_ready_cb(void *not_used) { /* * Depending on the CONFIGURATION.CONVERSION_TIME bits, a conversion will * take either 100 or 800 ms. Here we inspect the CONVERSION_READY bit and * if the reading is ready we notify, otherwise we just reschedule ourselves */ uint8_t cfg_data[] = { REG_CONFIGURATION }; uint16_t cfg_value = 0; bool spi_ok = i2c_write_read(cfg_data, sizeof(cfg_data), &cfg_value, sizeof(cfg_value)); if (!spi_ok) { opt_3001.status = OPT_3001_STATUS_I2C_ERROR; return; } if (cfg_value & CFG_CRF) { opt_3001.status = OPT_3001_STATUS_DATA_READY; sensors_changed(&opt_3001_sensor); } else { ctimer_set(&startup_timer, SENSOR_STARTUP_DELAY, notify_ready_cb, NULL); } } /*---------------------------------------------------------------------------*/ /** * \brief Returns a reading from the sensor * \param type Ignored * \return Illuminance in centilux */ static int value(int type) { if (opt_3001.status != OPT_3001_STATUS_DATA_READY) { return MPU_9250_READING_ERROR; } uint8_t cfg_data[] = { REG_CONFIGURATION }; uint16_t cfg_value = 0; bool spi_ok = i2c_write_read(cfg_data, sizeof(cfg_data), &cfg_value, sizeof(cfg_value)); if (!spi_ok) { opt_3001.status = OPT_3001_STATUS_I2C_ERROR; return MPU_9250_READING_ERROR; } uint8_t result_data[] = { REG_RESULT }; uint16_t result_value = 0; spi_ok = i2c_write_read(result_data, sizeof(result_data), &result_value, sizeof(result_value)); if (!spi_ok) { opt_3001.status = OPT_3001_STATUS_I2C_ERROR; return MPU_9250_READING_ERROR; } result_value = SWAP16(result_value); uint32_t e = (result_value & 0x0FFF) >> 0; uint32_t m = (result_value & 0xF000) >> 12; uint32_t converted = m * 100 * (1 << e); PRINTF("OPT: %04X r=%d (centilux)\n", result_value, (int)(converted)); return (int)converted; } /*---------------------------------------------------------------------------*/ /** * \brief Configuration function for the OPT3001 sensor. * * \param type Activate, enable or disable the sensor. See below * \param enable * * When type == SENSORS_HW_INIT we turn on the hardware * When type == SENSORS_ACTIVE and enable==1 we enable the sensor * When type == SENSORS_ACTIVE and enable==0 we disable the sensor */ static int configure(int type, int enable) { int rv = 0; switch(type) { case SENSORS_HW_INIT: if (sensor_init()) { opt_3001.status = OPT_3001_STATUS_STANDBY; } else { opt_3001.status = OPT_3001_STATUS_DISABLED; rv = MPU_9250_READING_ERROR; } break; case SENSORS_ACTIVE: if(enable) { sensor_enable(true); ctimer_set(&startup_timer, SENSOR_STARTUP_DELAY, notify_ready_cb, NULL); opt_3001.status = OPT_3001_STATUS_BOOTING; } else { ctimer_stop(&startup_timer); sensor_enable(false); } break; default: break; } return rv; } /*---------------------------------------------------------------------------*/ /** * \brief Returns the status of the sensor * \param type ignored * \return The state of the sensor SENSOR_STATE_xyz */ static int status(int type) { (void)type; return opt_3001.status; } /*---------------------------------------------------------------------------*/ SENSORS_SENSOR(opt_3001_sensor, "OPT3001", value, configure, status); /*---------------------------------------------------------------------------*/ /** @} */