diff --git a/drivers/Makefile.include b/drivers/Makefile.include index 23cc3d0007..4a4625ebd7 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -46,3 +46,6 @@ endif ifneq (,$(filter encx24j600,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/encx24j600/include endif +ifneq (,$(filter tcs37727,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/tcs37727/include +endif diff --git a/drivers/include/tcs37727.h b/drivers/include/tcs37727.h new file mode 100644 index 0000000000..ff9bd67497 --- /dev/null +++ b/drivers/include/tcs37727.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2015 PHYTEC Messtechnik GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup drivers_tcs37727 TCS37727 Light-To-Digital Converter + * + * @ingroup drivers + * @brief Driver for the AMS TCS37727 Color Light-To-Digital Converter + * + * + * @{ + * + * @file + * @brief Interface definition for the TCS37727 sensor driver. + * + * @author Felix Siebel + * @author Johann Fischer + */ + +#ifndef TCS37727_H +#define TCS37727_H + +#include +#include +#include "periph/i2c.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef TCS37727_I2C_ADDRESS +#define TCS37727_I2C_ADDRESS 0x29 /**< Default Device Address */ +#endif + +#ifndef TCS37727_ATIME_DEFAULT +#define TCS37727_ATIME_DEFAULT 200000 /**< Default RGBC integration time */ +#endif + +/** + * @brief Struct for storing TCS37727 sensor data + */ +typedef struct { + uint32_t red; /**< IR compensated channels red */ + uint32_t green; /**< IR compensated channels green */ + uint32_t blue; /**< IR compensated channels blue */ + uint32_t clear; /**< channels clear */ + uint32_t lux; /**< Lux */ + uint32_t ct; /**< Color temperature */ +} tcs37727_data_t; + +/** + * @brief Device descriptor for TCS37727 sensors. + */ +typedef struct { + i2c_t i2c; /**< I2C device the sensor is connected to */ + uint8_t addr; /**< the sensor's slave address on the I2C bus */ + bool initialized; /**< sensor status, true if sensor is initialized */ + int atime_us; /**< atime value conveted to microseconds */ + int again; /**< amount of gain */ +} tcs37727_t; + +/** + * @brief Initialise the TCS37727 sensor driver. + * Settings: Gain 4x, Proximity Detection off + * + * @param[out] dev device descriptor of sensor to initialize + * @param[in] i2c I2C bus the sensor is connected to + * @param[in] address sensor's I2C slave address + * @param[in] atime_us rgbc RGBC integration time in microseconds + * + * @return 0 on success + * @return -1 if initialization of I2C bus failed + * @return -2 if sensor test failed + * @return -3 if sensor configuration failed + */ +int tcs37727_init(tcs37727_t *dev, i2c_t i2c, uint8_t address, int atime_us); + +/** + * @brief Set RGBC enable, this activates periodic RGBC measurements. + * + * @param[out] dev device descriptor of sensor + * + * @return 0 on success + * @return -1 on error + */ +int tcs37727_set_rgbc_active(tcs37727_t *dev); + +/** + * @brief Set RGBC disable, this deactivates periodic RGBC measurements. + * Also turns off the sensor when proximity measurement is disabled. + * + * @param[in] dev device descriptor of sensor + * + * @return 0 on success + * @return -1 on error + */ +int tcs37727_set_rgbc_standby(tcs37727_t *dev); + +/** + * @brief Read sensor's data. + * Besides an Autogain routine is called. If a maximum or minimum threshold + * value of the channel clear is reached, then the gain will be changed + * correspond to max or min threshold. + * + * @param[in] dev device descriptor of sensor + * @param[out] data device sensor data + * + * @return 0 on success + * @return -1 on error + */ +int tcs37727_read(tcs37727_t *dev, tcs37727_data_t *data); + +#ifdef __cplusplus +} +#endif + +#endif +/** @} */ diff --git a/drivers/tcs37727/Makefile b/drivers/tcs37727/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/tcs37727/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/tcs37727/include/tcs37727-internal.h b/drivers/tcs37727/include/tcs37727-internal.h new file mode 100644 index 0000000000..5950f774ac --- /dev/null +++ b/drivers/tcs37727/include/tcs37727-internal.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2015 PHYTEC Messtechnik GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + * + */ + +/** + * @ingroup drivers_tcs37727 + * @{ + * + * @file + * @brief Register definitions for the TCS37727 driver. + * + * @author Johann Fischer + * + */ + +#ifndef TCS37727_INTERNAL_H +#define TCS37727_INTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef TCS37727_AG_THRESHOLD_LOW +#define TCS37727_AG_THRESHOLD_LOW 200 +#endif + +#ifndef TCS37727_AG_THRESHOLD_HIGH +#define TCS37727_AG_THRESHOLD_HIGH (65535 - TCS37727_AG_THRESHOLD_LOW) +#endif + +/** + * @brief Register Map + * + * All setting register are defined for repeated byte protocol transaction. + */ +#define TCS37727_ENABLE 0x80 /* Enables states and interrupts */ +#define TCS37727_ATIME 0x81 /* RGBC time */ +#define TCS37727_PTIME 0x82 /* Proximity time */ +#define TCS37727_WTIME 0x83 /* Wait time */ +#define TCS37727_AILTL 0x04 /* Clear interrupt low threshold low byte */ +#define TCS37727_AILTH 0x05 /* Clear interrupt low threshold high byte */ +#define TCS37727_AIHTL 0x06 /* Clear interrupt high threshold low byte */ +#define TCS37727_AIHTH 0x07 /* Clear interrupt high threshold high byte */ +#define TCS37727_PILTL 0x08 /* Proximity interrupt low threshold low byte */ +#define TCS37727_PILTH 0x09 /* Proximity interrupt low threshold high byte */ +#define TCS37727_PIHTL 0x0A /* Proximity interrupt high threshold low byte */ +#define TCS37727_PIHTH 0x0B /* Proximity interrupt high threshold high byte */ +#define TCS37727_PERS 0x8C /* Interrupt persistence filters */ +#define TCS37727_CONFIG 0x8D /* Configuration */ +#define TCS37727_PPULSE 0x8E /* Proximity pulse count */ +#define TCS37727_CONTROL 0x8F /* Gain control register */ +#define TCS37727_ID 0x92 /* Device IDID */ +#define TCS37727_STATUS 0x93 /* Device status */ +#define TCS37727_CDATA 0x14 /* Clear ADC data low byte */ +#define TCS37727_CDATAH 0x15 /* Clear ADC data high byte */ +#define TCS37727_RDATA 0x16 /* Red ADC data low byte */ +#define TCS37727_RDATAH 0x17 /* Red ADC data high byte */ +#define TCS37727_GDATA 0x18 /* Green ADC data low byte */ +#define TCS37727_GDATAH 0x19 /* Green ADC data high byte */ +#define TCS37727_BDATA 0x1A /* Blue ADC data low byte */ +#define TCS37727_BDATAH 0x1B /* Blue ADC data high byte */ +#define TCS37727_PDATA 0x1C /* Proximity ADC data low byte */ +#define TCS37727_PDATAH 0x1D /* Proximity ADC data high byte */ + +/** + * @brief Command Register + */ +#define TCS37727_BYTE_TRANS 0x80 /* Repeated byte protocol transaction */ +#define TCS37727_INC_TRANS 0xA0 /* Auto-increment protocol transaction */ +#define TCS37727_SF_PICLR 0xE5 /* Proximity interrupt clear */ +#define TCS37727_SF_CICLR 0xE6 /* Clear channel interrupt clear */ +#define TCS37727_SF_PCICLR 0xE7 /* Proximity and Clear channel interrupt clear */ + +/** + * @brief Enable Register + */ +#define TCS37727_ENABLE_PIEN (1 << 5) /* Proximity interrupt enable */ +#define TCS37727_ENABLE_AIEN (1 << 4) /* Clear channel interrupt enable */ +#define TCS37727_ENABLE_WEN (1 << 3) /* Wait enable, activates the wait feature */ +#define TCS37727_ENABLE_PEN (1 << 2) /* Proximity enable, activates the proximity function */ +#define TCS37727_ENABLE_AEN (1 << 1) /* RGBC enable, actives the two-channel ADC */ +#define TCS37727_ENABLE_PON (1 << 0) /* Power ON */ + +/** + * @brief Control Register + */ +#define TCS37727_CONTROL_PDRIVE_100 0x00 /* 100 mA LED Drive Strength */ +#define TCS37727_CONTROL_PDRIVE_50 0x04 /* 50 mA LED Drive Strength */ +#define TCS37727_CONTROL_PDRIVE_25 0x08 /* 25 mA LED Drive Strength */ +#define TCS37727_CONTROL_PDRIVE_12 0x0C /* 12.5 mA LED Drive Strength */ +#define TCS37727_CONTROL_PDRIVE_MASK 0x0C /* PDRIVE Mask */ +#define TCS37727_CONTROL_AGAIN_1 0x00 /* 1 × gain RGBC Gain Value */ +#define TCS37727_CONTROL_AGAIN_4 0x01 /* 4 × gain RGBC Gain Value */ +#define TCS37727_CONTROL_AGAIN_16 0x02 /* 16 × gain RGBC Gain Value */ +#define TCS37727_CONTROL_AGAIN_60 0x03 /* 60 × gain RGBC Gain Value */ +#define TCS37727_CONTROL_AGAIN_MASK 0x03 /* AGAIN Mask */ + +/** + * @brief Device ID + */ +#define TCS37727_ID_VALUE 0x49 + +/** + * @brief Predefined ATIME register values. + */ +#define TCS37727_ATIME_MIN 2400 /* 2.4ms integration time, max count 1024 */ +#define TCS37727_ATIME_MAX 614000 /* 614ms integration time, max count 0xffff */ + +#define TCS37727_ATIME_TO_REG(val) (256 - (uint8_t)((val) / 2400)) +#define TCS37727_ATIME_TO_US(reg) ((256 - (uint8_t)(reg)) * 2400) + +/** + * @brief Coefficients for Lux and CT Equations (DN40) + * + * Coefficients in integer format, multiplied by 1000. + */ +#define DGF_IF 310 +#define R_COEF_IF 136 +#define G_COEF_IF 1000 +#define B_COEF_IF -444 +#define CT_COEF_IF 3810 +#define CT_OFFSET_IF 1391 + +#ifdef __cplusplus +} +#endif + +#endif +/** @} */ diff --git a/drivers/tcs37727/tcs37727.c b/drivers/tcs37727/tcs37727.c new file mode 100644 index 0000000000..ca8aba669b --- /dev/null +++ b/drivers/tcs37727/tcs37727.c @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2015 PHYTEC Messtechnik GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + * + */ + +/** + * @ingroup drivers_tcs37727 + * @{ + * + * @file + * @brief Driver for the AMS TCS37727 Color Light-To-Digital Converter + * + * @author Felix Siebel + * @author Johann Fischer + * + * @} + */ + +#include +#include +#include "periph/i2c.h" +#include "tcs37727.h" +#include "tcs37727-internal.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define I2C_SPEED I2C_SPEED_FAST + +static int tcs37727_test(tcs37727_t *dev) +{ + char id; + + i2c_acquire(dev->i2c); + + if (i2c_read_reg(dev->i2c, dev->addr, TCS37727_ID, &id) != 1) { + i2c_release(dev->i2c); + return -1; + } + + i2c_release(dev->i2c); + + if (id != TCS37727_ID_VALUE) { + return -1; + } + + return 0; +} + +int tcs37727_init(tcs37727_t *dev, i2c_t i2c, uint8_t address, int atime_us) +{ + /* write device descriptor */ + dev->i2c = i2c; + dev->addr = address; + dev->initialized = false; + + i2c_acquire(dev->i2c); + + /* initialize the I2C bus */ + if (i2c_init_master(i2c, I2C_SPEED) < 0) { + i2c_release(dev->i2c); + return -1; + } + + i2c_release(dev->i2c); + + if (tcs37727_test(dev)) { + return -2; + } + + i2c_acquire(dev->i2c); + + if (i2c_write_reg(dev->i2c, dev->addr, TCS37727_CONTROL, + TCS37727_CONTROL_AGAIN_4) != 1) { + i2c_release(dev->i2c); + return -3; + } + dev->again = 4; + + if (i2c_write_reg(dev->i2c, dev->addr, + TCS37727_ATIME_TO_REG(atime_us), 0) != 1) { + i2c_release(dev->i2c); + return -3; + } + dev->atime_us = atime_us; + + dev->initialized = true; + + i2c_release(dev->i2c); + return 0; +} + +int tcs37727_set_rgbc_active(tcs37727_t *dev) +{ + uint8_t reg; + + if (dev->initialized == false) { + return -1; + } + + i2c_acquire(dev->i2c); + if (i2c_read_regs(dev->i2c, dev->addr, TCS37727_ENABLE, (char *)®, 1) != 1) { + i2c_release(dev->i2c); + return -1; + } + + reg |= (TCS37727_ENABLE_AEN | TCS37727_ENABLE_PON); + + if (i2c_write_reg(dev->i2c, dev->addr, TCS37727_ENABLE, reg) != 1) { + i2c_release(dev->i2c); + return -1; + } + + i2c_release(dev->i2c); + return 0; +} + +int tcs37727_set_rgbc_standby(tcs37727_t *dev) +{ + uint8_t reg; + + if (dev->initialized == false) { + return -1; + } + + i2c_acquire(dev->i2c); + if (i2c_read_regs(dev->i2c, dev->addr, TCS37727_ENABLE, (char *)®, 1) != 1) { + i2c_release(dev->i2c); + return -1; + } + + reg &= ~TCS37727_ENABLE_AEN; + if (!(reg & TCS37727_ENABLE_PEN)) { + reg &= ~TCS37727_ENABLE_PON; + } + + if (i2c_write_reg(dev->i2c, dev->addr, TCS37727_ENABLE, reg) != 1) { + i2c_release(dev->i2c); + return -1; + } + + i2c_release(dev->i2c); + return 0; +} + +static uint8_t tcs37727_trim_gain(tcs37727_t *dev, int rawc) +{ + uint8_t reg_again = 0; + int val_again = dev->again; + + if (dev->initialized == false) { + return -1; + } + + if (rawc < TCS37727_AG_THRESHOLD_LOW) { + switch (val_again) { + case 1: + reg_again = TCS37727_CONTROL_AGAIN_4; + val_again = 4; + break; + + case 4: + reg_again = TCS37727_CONTROL_AGAIN_16; + val_again = 16; + break; + + case 16: + reg_again = TCS37727_CONTROL_AGAIN_60; + val_again = 60; + break; + + case 60: + default: + return -1; + } + } + else if (rawc > TCS37727_AG_THRESHOLD_HIGH) { + switch (val_again) { + case 60: + reg_again = TCS37727_CONTROL_AGAIN_16; + val_again = 16; + break; + + case 16: + reg_again = TCS37727_CONTROL_AGAIN_4; + val_again = 4; + break; + + case 4: + reg_again = TCS37727_CONTROL_AGAIN_1; + val_again = 1; + break; + + case 1: + default: + return -1; + } + } + else { + return 0; + } + + i2c_acquire(dev->i2c); + uint8_t reg = 0; + if (i2c_read_reg(dev->i2c, dev->addr, TCS37727_CONTROL, (char *)®) != 1) { + i2c_release(dev->i2c); + return -2; + } + reg &= ~TCS37727_CONTROL_AGAIN_MASK; + reg |= reg_again; + if (i2c_write_reg(dev->i2c, dev->addr, TCS37727_CONTROL, reg) != 1) { + i2c_release(dev->i2c); + return -2; + } + i2c_release(dev->i2c); + dev->again = val_again; + + return 0; +} + +int tcs37727_read(tcs37727_t *dev, tcs37727_data_t *data) +{ + char buf[8]; + + if (dev->initialized == false) { + return -1; + } + + i2c_acquire(dev->i2c); + + if (i2c_read_regs(dev->i2c, dev->addr, + (TCS37727_INC_TRANS | TCS37727_CDATA), buf, 8) != 8) { + i2c_release(dev->i2c); + return -1; + } + + i2c_release(dev->i2c); + + int32_t tmpc = ((uint16_t)buf[1] << 8) | buf[0]; + int32_t tmpr = ((uint16_t)buf[3] << 8) | buf[2]; + int32_t tmpg = ((uint16_t)buf[5] << 8) | buf[4]; + int32_t tmpb = ((uint16_t)buf[7] << 8) | buf[6]; + DEBUG("rawr: %"PRIi32" rawg %"PRIi32" rawb %"PRIi32" rawc %"PRIi32"\n", + tmpr, tmpg, tmpb, tmpc); + + /* Remove IR component as described in the DN40. */ + int32_t ir = (tmpr + tmpg + tmpb - tmpc) >> 1; + tmpr -= ir; + tmpg -= ir; + tmpb -= ir; + + /* Color temperature calculation as described in the DN40. */ + int32_t ct = (CT_COEF_IF * tmpb) / tmpr + CT_OFFSET_IF; + + /* Lux calculation as described in the DN40. */ + int32_t gi = R_COEF_IF * tmpr + G_COEF_IF * tmpg + B_COEF_IF * tmpb; + /* TODO: add Glass Attenuation Factor GA compensation */ + int32_t cpl = (dev->atime_us * dev->again) / DGF_IF; + int32_t lux = gi / cpl; + + /* Autogain */ + tcs37727_trim_gain(dev, tmpc); + + data->red = (tmpr < 0) ? 0 : (tmpr * 1000) / cpl; + data->green = (tmpg < 0) ? 0 : (tmpg * 1000) / cpl; + data->blue = (tmpb < 0) ? 0 : (tmpb * 1000) / cpl; + data->clear = (tmpb < 0) ? 0 : (tmpc * 1000) / cpl; + data->lux = (lux < 0) ? 0 : lux; + data->ct = (ct < 0) ? 0 : ct; + + return 0; +}