diff --git a/drivers/Makefile.include b/drivers/Makefile.include index f0e9712f62..9af613263e 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -73,6 +73,9 @@ endif ifneq (,$(filter bme280,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/bme280/include endif +ifneq (,$(filter tsl2561,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/tsl2561/include +endif ifneq (,$(filter cc2420,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/cc2420/include endif diff --git a/drivers/include/tsl2561.h b/drivers/include/tsl2561.h new file mode 100644 index 0000000000..a78c98750c --- /dev/null +++ b/drivers/include/tsl2561.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2016 Inria + * + * 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_tsl2561 TSL2561 + * @ingroup drivers_sensors + * @brief Device driver interface for the illuminance TSL2561 sensor + * @{ + * + * @file + * @brief Device driver interface for the illuminance TSL2561 sensor. + * + * @author Alexandre Abadie + */ + +#ifndef TSL2561_H_ +#define TSL2561_H_ + +#include "saul.h" +#include "periph/i2c.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name TSL2561 I2C addresses + * @{ + */ +#define TSL2561_ADDR_LOW (0x29) +#define TSL2561_ADDR_FLOAT (0x39) +#define TSL2561_ADDR_HIGH (0x49) +/** @} */ + +/** + * @name TSL2561 integration times + * @{ + */ +#define TSL2561_INTEGRATIONTIME_13MS (0x00) /* 13.7ms */ +#define TSL2561_INTEGRATIONTIME_101MS (0x01) /* 101ms */ +#define TSL2561_INTEGRATIONTIME_402MS (0x02) /* 402ms */ +#define TSL2561_INTEGRATIONTIME_NA (0x03) /* N/A */ +/** @} */ + +/** + * @name TSL2561 gains + * @{ + */ +#define TSL2561_GAIN_1X (0x00) +#define TSL2561_GAIN_16X (0x10) +/** @} */ + +/** + * @name tsl2561 driver initialization return codes + * @{ + */ +#define TSL2561_OK (0) +#define TSL2561_NOI2C (-1) +#define TSL2561_BADDEV (-2) +/** @} */ + +/** + * @brief Device descriptor for the TSL2561 sensor + */ +typedef struct { + i2c_t i2c_dev; /**< I2C device which is used */ + uint8_t addr; /**< address on I2C bus */ + uint8_t gain; /**< gain */ + uint8_t integration; /**< integration time */ +} tsl2561_t; + + +/** + * @brief Device initialization parameters + */ +typedef tsl2561_t tsl2561_params_t; + +/** + * @brief Initialize the given TSL2561 device + * + * @param[out] dev Initialized device descriptor of BMP180 device + * @param[in] i2c I2C bus the sensor is connected to + * @param[in] addr I2C address of the sensor on the I2C bus + * @param[in] gain TSL2561 gain + * @param[in] integration TSL2561 integration time + * + * @return 0 on success + * @return -1 if given I2C is not available + * @return -2 if not a TSL2561 sensor + */ +int tsl2561_init(tsl2561_t *dev, i2c_t i2c, uint8_t addr, + uint8_t gain, uint8_t integration); + +/** + * @brief Read illuminance value from the given TSL2561 device, returned in lx. + * + * @param[in] dev Device descriptor of TSL2561 device to read from + * + * @return Illuminance in Lux (lx) + */ +uint16_t tsl2561_read_illuminance(tsl2561_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* TSL2561_H_ */ +/** @} */ diff --git a/drivers/tsl2561/Makefile b/drivers/tsl2561/Makefile new file mode 100644 index 0000000000..9457af6bc6 --- /dev/null +++ b/drivers/tsl2561/Makefile @@ -0,0 +1,3 @@ +MODULE = tsl2561 + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/tsl2561/include/tsl2561_internals.h b/drivers/tsl2561/include/tsl2561_internals.h new file mode 100644 index 0000000000..6d0e1932cd --- /dev/null +++ b/drivers/tsl2561/include/tsl2561_internals.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2016 Inria + * + * 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_tsl2561 + * @brief Internal addresses, registers, constants for the TSL2561 sensor. + * @{ + * + * @file + * @brief Internal addresses, registers, constants for the TSL2561 sensor. + * + * @author Alexandre Abadie + */ + +#ifndef TSL2561_REGS_H_ +#define TSL2561_REGS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name TSL2561 identifier + * @{ + */ +#define TSL2561_ID (0x50) +/** @} */ + +/** + * @name TSL2561 internals registers + * @{ + */ +#define TSL2561_REGISTER_CONTROL (0x00) +#define TSL2561_REGISTER_TIMING (0x01) +#define TSL2561_REGISTER_THRESHOLDLOW (0x02) +#define TSL2561_REGISTER_THRESHOLDHIGH (0x04) +#define TSL2561_REGISTER_INTERRUPT (0x06) +#define TSL2561_REGISTER_ID (0x0A) +#define TSL2561_REGISTER_CHAN0 (0x0C) +#define TSL2561_REGISTER_CHAN1 (0x0E) +/** @} */ + +/** + * @name TSL2561 commands + * @{ + */ +#define TSL2561_COMMAND_MODE (0x80) +#define TSL2561_COMMAND_CLEAR (0x40) +#define TSL2561_COMMAND_WORD (0x20) +#define TSL2561_COMMAND_BLOCK (0x10) +/** @} */ + +/** + * @name TSL2561 controls + * @{ + */ +#define TSL2561_CONTROL_POWERON (0x03) +#define TSL2561_CONTROL_POWEROFF (0x00) +/** @} */ + +/** + * @name Internals constants + * @{ + */ +#define TSL2561_LUXSCALE (14) /* use 2e14 scaling */ +#define TSL2561_RATIOSCALE (9) /* use 2e9 scaling */ +#define TSL2561_CHSCALE (10) /* use 2e10 scaling on + * channel values by */ +#define TSL2561_CHSCALE_TINT0 (0x7517) +#define TSL2561_CHSCALE_TINT1 (0x0FE7) + +#define TSL2561_K1T (0x0040) +#define TSL2561_B1T (0x01f2) +#define TSL2561_M1T (0x01be) +#define TSL2561_K2T (0x0080) +#define TSL2561_B2T (0x0214) +#define TSL2561_M2T (0x02d1) +#define TSL2561_K3T (0x00c0) +#define TSL2561_B3T (0x023f) +#define TSL2561_M3T (0x037b) +#define TSL2561_K4T (0x0100) +#define TSL2561_B4T (0x0270) +#define TSL2561_M4T (0x03fe) +#define TSL2561_K5T (0x0138) +#define TSL2561_B5T (0x016f) +#define TSL2561_M5T (0x01fc) +#define TSL2561_K6T (0x019a) +#define TSL2561_B6T (0x00d2) +#define TSL2561_M6T (0x00fb) +#define TSL2561_K7T (0x029a) +#define TSL2561_B7T (0x0018) +#define TSL2561_M7T (0x0012) +#define TSL2561_K8T (0x029a) +#define TSL2561_B8T (0x0000) +#define TSL2561_M8T (0x0000) +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* TSL2561_REGS_H_ */ +/** @} */ diff --git a/drivers/tsl2561/include/tsl2561_params.h b/drivers/tsl2561/include/tsl2561_params.h new file mode 100644 index 0000000000..7fc4ae2339 --- /dev/null +++ b/drivers/tsl2561/include/tsl2561_params.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2017 Inria + * + * 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_tsl2561 + * + * @{ + * @file + * @brief Default configuration for TSL2561 + * + * @author Alexandre Abadie + */ + +#ifndef TSL2561_PARAMS_H +#define TSL2561_PARAMS_H + +#include "saul_reg.h" +#include "tsl2561.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set default configuration parameters for the TSL2561 + * @{ + */ +#ifndef TSL2561_PARAM_I2C_DEV +#define TSL2561_PARAM_I2C_DEV I2C_DEV(0) +#endif +#ifndef TSL2561_PARAM_ADDR +#define TSL2561_PARAM_ADDR TSL2561_ADDR_FLOAT +#endif +#ifndef TSL2561_PARAM_GAIN +#define TSL2561_PARAM_GAIN TSL2561_GAIN_1X +#endif +#ifndef TSL2561_PARAM_INTEGRATION +#define TSL2561_PARAM_INTEGRATION TSL2561_INTEGRATIONTIME_402MS +#endif + +#define TSL2561_PARAMS_DEFAULT { .i2c_dev = TSL2561_PARAM_I2C_DEV, \ + .addr = TSL2561_PARAM_ADDR, \ + .gain = TSL2561_PARAM_GAIN, \ + .integration = TSL2561_PARAM_INTEGRATION } +/**@}*/ + +/** + * @brief Configure TSL2561 + */ +static const tsl2561_params_t tsl2561_params[] = +{ +#ifdef TSL2561_PARAMS_CUSTOM + TSL2561_PARAMS_CUSTOM, +#else + TSL2561_PARAMS_DEFAULT, +#endif +}; + +/** + * @brief Allocate and configure entries to the SAUL registry + */ +saul_reg_info_t tsl2561_saul_reg_info[] = +{ + { + .name= "tsl2561-illuminance" + } +}; + +#ifdef __cplusplus +} +#endif + +#endif // TSL2561_PARAMS_H +/** @} */ diff --git a/drivers/tsl2561/tsl2561.c b/drivers/tsl2561/tsl2561.c new file mode 100644 index 0000000000..51116baf20 --- /dev/null +++ b/drivers/tsl2561/tsl2561.c @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2016 Inria + * + * 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_tsl2561 + * @{ + * + * @file + * @brief Device driver implementation for the TSL2561 Luminosity sensor. + * + * @author Alexandre Abadie + * + * @} + */ + +#include +#include + +#include "log.h" +#include "tsl2561.h" +#include "tsl2561_internals.h" +#include "periph/i2c.h" +#include "xtimer.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* internal helpers */ +static void _enable(tsl2561_t *dev); +static void _disable(tsl2561_t *dev); +static void _read_data(tsl2561_t *dev, uint16_t *full, uint16_t *ir); +static void _print_init_info(tsl2561_t *dev); + +/*---------------------------------------------------------------------------* + * TSL2561 Core API * + *---------------------------------------------------------------------------*/ + +int tsl2561_init(tsl2561_t *dev, + i2c_t i2c, uint8_t addr, uint8_t gain, uint8_t integration) +{ + dev->i2c_dev = i2c; + dev->addr = addr; + dev->gain = gain; + dev->integration = integration; + _print_init_info(dev); + + /* Initialize I2C interface */ + if (i2c_init_master(dev->i2c_dev, I2C_SPEED_NORMAL)) { + DEBUG("[Error] I2C device not enabled\n"); + return TSL2561_NOI2C; + } + + DEBUG("[Info] I2C device initialized with success!\n"); + + /* Acquire exclusive access */ + i2c_acquire(dev->i2c_dev); + + DEBUG("[Info] Access acquired !\n"); + + /* Verify sensor ID */ + uint8_t id; + i2c_read_reg(dev->i2c_dev, dev->addr, + TSL2561_COMMAND_MODE | TSL2561_REGISTER_ID, &id); + DEBUG("[Info] ID ? %d\n", id); + if (id != TSL2561_ID ) { + DEBUG("[Error] not a TSL2561 sensor\n"); + return TSL2561_BADDEV; + } + + _enable(dev); + + /* configuring gain and integration time */ + i2c_write_reg(dev->i2c_dev, dev->addr, + TSL2561_COMMAND_MODE | TSL2561_REGISTER_TIMING, + dev->integration | dev->gain); + +#if ENABLE_DEBUG + uint8_t timing; + i2c_read_reg(dev->i2c_dev, dev->addr, + TSL2561_COMMAND_MODE | TSL2561_REGISTER_TIMING, &timing); + DEBUG("[Info] Timing ? %d (expected: %d)\n", + timing, dev->integration | dev->gain); +#endif + + _disable(dev); + + return TSL2561_OK; +} + +uint16_t tsl2561_read_illuminance(tsl2561_t *dev) +{ + /* Read IR and full spectrum values */ + uint16_t ir = 0; + uint16_t full = 0; + _read_data(dev, &full, &ir); + + DEBUG("[Info] Full spectrum value: %i\n", (int)full); + DEBUG("[Info] IR spectrum value: %i\n", (int)ir); + + /* Compute illuminance */ + uint32_t channel_scale; + uint32_t channel_1; + uint32_t channel_0; + + switch (dev->integration) { + case TSL2561_INTEGRATIONTIME_13MS: + channel_scale = TSL2561_CHSCALE_TINT0; + break; + + case TSL2561_INTEGRATIONTIME_101MS: + channel_scale = TSL2561_CHSCALE_TINT1; + break; + + default: /* No scaling ... integration time = 402ms */ + channel_scale = (1 << TSL2561_CHSCALE); + break; + } + + /* Scale for gain (1x or 16x) */ + if (!dev->gain) { + channel_scale = channel_scale << 4; + } + + /* scale the channel values */ + channel_0 = (full * channel_scale) >> TSL2561_CHSCALE; + channel_1 = (ir * channel_scale) >> TSL2561_CHSCALE; + + /* find the ratio of the channel values (Channel1/Channel0) */ + uint32_t ratio_1 = 0; + if (channel_0 != 0) { + ratio_1 = (channel_1 << (TSL2561_RATIOSCALE + 1)) / channel_0; + } + + /* round the ratio value */ + uint32_t ratio = (ratio_1 + 1) >> 1; + uint32_t b, m; + + if (ratio <= TSL2561_K1T) { + b = TSL2561_B1T; + m = TSL2561_M1T; + } + else if (ratio <= TSL2561_K2T) { + b = TSL2561_B2T; + m = TSL2561_M2T; + } + else if (ratio <= TSL2561_K3T) { + b = TSL2561_B3T; + m = TSL2561_M3T; + } + else if (ratio <= TSL2561_K4T) { + b = TSL2561_B4T; + m = TSL2561_M4T; + } + else if (ratio <= TSL2561_K5T) { + b = TSL2561_B5T; + m = TSL2561_M5T; + } + else if (ratio <= TSL2561_K6T) { + b = TSL2561_B6T; + m = TSL2561_M6T; + } + else if (ratio <= TSL2561_K7T) { + b = TSL2561_B7T; + m = TSL2561_M7T; + } + else { + b = TSL2561_B8T; + m = TSL2561_M8T; + } + + uint32_t illuminance; + illuminance = ((channel_0 * b) - (channel_1 * m)); + + /* round lsb (2^(TSL2561_SCALE - 1)) */ + illuminance += (1 << (TSL2561_LUXSCALE - 1)); + + /* return strip off fractional portion */ + return (uint16_t)(illuminance >> TSL2561_LUXSCALE); +} + + +static void _enable(tsl2561_t *dev) +{ + /* enabling device */ + i2c_write_reg(dev->i2c_dev, dev->addr, + TSL2561_COMMAND_MODE | TSL2561_REGISTER_CONTROL, + TSL2561_CONTROL_POWERON); +#if ENABLE_DEBUG + uint8_t en; + i2c_read_reg(dev->i2c_dev, dev->addr, + TSL2561_COMMAND_MODE | TSL2561_REGISTER_CONTROL, &en); + DEBUG("[Info] Enabled ? %s\n", en == 3 ? "true" : "false"); +#endif +} + + +static void _disable(tsl2561_t *dev) +{ + /* disabling device */ + i2c_write_reg(dev->i2c_dev, dev->addr, + TSL2561_COMMAND_MODE | TSL2561_REGISTER_CONTROL, + TSL2561_CONTROL_POWEROFF ); + +#if ENABLE_DEBUG + uint8_t dis; + i2c_read_reg(dev->i2c_dev, dev->addr, + TSL2561_COMMAND_MODE | TSL2561_REGISTER_CONTROL, &dis); + DEBUG("[Info] Disabled ? %s\n", dis == 0 ? "true": "false"); +#endif +} + +static void _read_data(tsl2561_t *dev, uint16_t *full, uint16_t *ir) +{ + /* Enable the device */ + _enable(dev); + + /* Wait integration time in ms for ADC to complete */ + switch (dev->integration) { + case TSL2561_INTEGRATIONTIME_13MS: + xtimer_usleep(13700); + break; + + case TSL2561_INTEGRATIONTIME_101MS: + xtimer_usleep(101000); + break; + + default: /* TSL2561_INTEGRATIONTIME_402MS */ + xtimer_usleep(402000); + break; + } + + char buffer[2] = { 0 }; + /* Read full spectrum channel */ + i2c_read_regs(dev->i2c_dev, dev->addr, + TSL2561_COMMAND_MODE | TSL2561_COMMAND_WORD | TSL2561_REGISTER_CHAN0, + buffer, 2); + *full = (buffer[1] << 8) | buffer[0]; + + memset(buffer, 0, sizeof(buffer)); + + /* Read infrared spectrum channel */ + i2c_read_regs(dev->i2c_dev, dev->addr, + TSL2561_COMMAND_MODE | TSL2561_COMMAND_WORD | TSL2561_REGISTER_CHAN1, + buffer, 2); + *ir = (buffer[1] << 8) | buffer[0]; + + /* Turn the device off to save power */ + _disable(dev); +} + +static void _print_init_info(tsl2561_t *dev) +{ + DEBUG("[Info] I2C device: %d\n", dev->i2c_dev); + DEBUG("[Info] Address: %d\n", dev->addr); + switch(dev->gain) { + case TSL2561_GAIN_1X: + DEBUG("[Info] Gain: 1X\n"); + break; + + case TSL2561_GAIN_16X: + DEBUG("[Info] Gain: 16X\n"); + break; + + default: + DEBUG("[Info] Invalid gain %d\n", dev->gain); + break; + } + + switch(dev->integration) { + case TSL2561_INTEGRATIONTIME_13MS: + DEBUG("[Info] Integration time: 13ms\n"); + break; + case TSL2561_INTEGRATIONTIME_101MS: + DEBUG("[Info] Integration time: 101ms\n"); + break; + case TSL2561_INTEGRATIONTIME_402MS: + DEBUG("[Info] Integration time: 402ms\n"); + break; + case TSL2561_INTEGRATIONTIME_NA: + DEBUG("[Info] Integration time: n/a\n"); + break; + default: + DEBUG("[Info] Invalid integration time %d\n", dev->integration); + break; + } +} diff --git a/drivers/tsl2561/tsl2561_saul.c b/drivers/tsl2561/tsl2561_saul.c new file mode 100644 index 0000000000..0e8aebd0df --- /dev/null +++ b/drivers/tsl2561/tsl2561_saul.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 Inria + * + * 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_tsl2561 + * @{ + * + * @file + * @brief SAUL adaption for TSL2561 device + * + * @author Hauke Petersen + * @author Alexandre Abadie + * + * @} + */ + +#include "saul.h" +#include "tsl2561.h" +#include "xtimer.h" + +static int read_illuminance(void *dev, phydat_t *res) +{ + tsl2561_t *d = (tsl2561_t *)dev; + + res->val[0] = tsl2561_read_illuminance(d); + res->unit = UNIT_LUX; + res->scale = 0; + return 1; +} + +const saul_driver_t tsl2561_illuminance_saul_driver = { + .read = read_illuminance, + .write = saul_notsup, + .type = SAUL_SENSE_LIGHT +}; diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index f09d7192ed..c8995e46ef 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -315,6 +315,10 @@ void auto_init(void) extern void auto_init_jc42(void); auto_init_jc42(); #endif +#ifdef MODULE_TSL2561 + extern void auto_init_tsl2561(void); + auto_init_tsl2561(); +#endif #ifdef MODULE_HDC1000 extern void auto_init_hdc1000(void); auto_init_hdc1000(); diff --git a/sys/auto_init/saul/auto_init_tsl2561.c b/sys/auto_init/saul/auto_init_tsl2561.c new file mode 100644 index 0000000000..68a29002ed --- /dev/null +++ b/sys/auto_init/saul/auto_init_tsl2561.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016 Inria + * + * 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 auto_init_saul + * @{ + * + * @file + * @brief Auto initialization of TSL2561 driver. + * + * @author Alexandre Abadie + * + * @} + */ + +#ifdef MODULE_TSL2561 + +#include "log.h" +#include "saul_reg.h" + +#include "tsl2561_params.h" + +/** + * @brief Define the number of configured sensors + */ +#define TSL2561_NUMOF (sizeof(tsl2561_params) / sizeof(tsl2561_params[0])) + +/** + * @brief Allocation of memory for device descriptors + */ +static tsl2561_t tsl2561_devs[TSL2561_NUMOF]; + +/** + * @brief Memory for the SAUL registry entries + */ +static saul_reg_t saul_entries[TSL2561_NUMOF]; + +/** + * @brief Reference the driver structs. + * @{ + */ +extern const saul_driver_t tsl2561_illuminance_saul_driver; +/** @} */ + +void auto_init_tsl2561(void) +{ + for (unsigned i = 0; i < TSL2561_NUMOF; i++) { + LOG_DEBUG("[auto_init_saul] initializing tsl2561 #%u\n", i); + + if (tsl2561_init(&tsl2561_devs[i], + tsl2561_params[i].i2c_dev, + tsl2561_params[i].addr, + tsl2561_params[i].gain, + tsl2561_params[i].integration) != TSL2561_OK) { + LOG_ERROR("[auto_init_saul] error initializing tsl2561 #%u\n", i); + return; + } + + /* illuminance */ + saul_entries[i].dev = &(tsl2561_devs[i]); + saul_entries[i].name = tsl2561_saul_reg_info[i].name; + saul_entries[i].driver = &tsl2561_illuminance_saul_driver; + + /* register to saul */ + saul_reg_add(&(saul_entries[i])); + } +} +#else +typedef int dont_be_pedantic; +#endif /* MODULE_TSL2561 */