diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 6a3de6c93dc87d5bbaa1defff51448a8208deb05..a3c70435384ad222065a6f0747ee03b0d01413ca 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -446,6 +446,11 @@ ifneq (,$(filter tsl2561,$(USEMODULE))) USEMODULE += xtimer endif +ifneq (,$(filter tsl4531x,$(USEMODULE))) + FEATURES_REQUIRED += periph_i2c + USEMODULE += xtimer +endif + ifneq (,$(filter uart_half_duplex,$(USEMODULE))) FEATURES_REQUIRED += periph_gpio FEATURES_REQUIRED += periph_uart diff --git a/drivers/Makefile.include b/drivers/Makefile.include index 19bb780f39d6d8294f5aded1df906dd3f33bf8f3..1c2c0e61b289034854448584f815a7856dfe6676 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -242,6 +242,10 @@ ifneq (,$(filter tsl2561,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/tsl2561/include endif +ifneq (,$(filter tsl4531x,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/tsl4531x/include +endif + ifneq (,$(filter uart_half_duplex,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/uart_half_duplex/include endif diff --git a/drivers/include/tsl4531x.h b/drivers/include/tsl4531x.h new file mode 100644 index 0000000000000000000000000000000000000000..cc799734675e283fe7bc13165bafa46f980b8ec3 --- /dev/null +++ b/drivers/include/tsl4531x.h @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2018 Freie Universität Berlin + * + * 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_tsl4531x TSL4531x Illuminance sensor + * @ingroup drivers_sensors + * @brief Device driver for the AMS TSL4531 sensor + * @{ + * + * # Power modes + * + * This driver has two power modes: high and low. Its startup mode is configured + * in the initialization parameters, and it can also be changed during runtime. + * + * In high power mode, the user application can read from the device using the + * tsl4531x_simple_read function, and it will return immediately. + * + * In low power mode, the user application can interact with the driver in a + * synchronous or asynchronous manner. For synchronous operation, the application + * can call tsl4531x_simple_read, and the driver will block for the integration + * time defined in the initialization parameters (plus a small margin, to encompass + * the max times indicated in the datasheet). For asyncronous operation, the + * application needs to use the functions tsl4531x_start_sample, + * tsl4531x_time_until_sample_ready and tsl4531x_get_sample, as described in those + * functions' descriptions. + * + * Both modes will work through SAUL, with the low-power mode being synchronous. + * + * @file + * @brief Device driver for the AMS TSL4531 sensor + * + * @author Juan I Carrano <j.carrano@fu-berlin.de> + * @author Daniel Petry <daniel.petry@fu-berlin.de> + */ + +#ifndef TSL4531X_H +#define TSL4531X_H + +#include <stdint.h> + +#include "periph/i2c.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Integration times + */ +typedef enum { + TSL4531X_INTEGRATE_400MS = 0, /* 0b00 */ + TSL4531X_INTEGRATE_200MS = 1, /* 0b01 */ + TSL4531X_INTEGRATE_100MS = 2, /* 0b10 */ +}tsl4531x_intgn_time_t; + +/** + * @name Fixed values for different members of the TSL4531x series + * @{ + * + * @brief Part numbers + */ +#define TSL45311_PARTNO (0x8) +#define TSL45313_PARTNO (0x9) +#define TSL45315_PARTNO (0xA) +#define TSL45317_PARTNO (0xB) + +/** + * @brief TSL4531x I2C addresses + */ +#define TSL45311_ADDR (0x39) +#define TSL45313_ADDR (0x39) +#define TSL45315_ADDR (0x29) +#define TSL45317_ADDR (0x29) +/** @} */ + +/** + * @brief Device initialization parameters + */ +typedef struct { + i2c_t i2c_dev; /**< I2C device which is used */ + i2c_t i2c_addr; /**< I2C address of sensor */ + tsl4531x_intgn_time_t integration_time; /**< integration time */ + uint8_t low_power_mode : 1; /**< low power mode */ + uint8_t part_number; /**< part number, according to variant */ +} tsl4531x_params_t; + +/** + * @brief Device descriptor + */ +typedef struct { + i2c_t i2c_dev; /**< I2C device which is used */ + i2c_t i2c_addr; /**< I2C address of sensor */ + tsl4531x_intgn_time_t integration_time; /**< integration time */ + uint8_t low_power_mode : 1; /**< low power mode */ + uint8_t high_power_mode_started_up : 1; /**< high power mode started up flag */ + uint32_t sample_start_time; /**< sample start time */ +} tsl4531x_t; + +/** + * Initialize the TSL4531x device. + * + * @param[out] dev Initialized device descriptor + * @param[in] params Device initialization parameters + * + * @return Zero on success + * @return -ENODEV if I2C bus can't be acquired + * @return -ENXIO if device can't be read or configured + * @return -ENOTSUP if ID, once read, is wrong + */ +int tsl4531x_init(tsl4531x_t *dev, const tsl4531x_params_t *params); + +/** + * Set the low power mode of the driver on or off. + * + * @param dev Initialized device descriptor + * @param low_power_on Bool indicating whether low power mode is on or off + * + * @return Zero + */ +int tsl4531x_set_low_power_mode(tsl4531x_t *dev, uint8_t low_power_on); + +/** + * Start collecting sample in low power mode. This provides asynchronous operation + * along with tsl4531x_time_until_sample_ready and tsl4531x_get_sample. It does + * nothing in high power mode. + * + * @param dev Initialized device descriptor + * + * @return Zero + */ +int tsl4531x_start_sample(tsl4531x_t *dev); + +/** + * Deliver time in microseconds until sample is ready, or zero if it is ready. + * In low power mode, this counts down from the point at which tsl4531x_start_sample + * is called, and along with that function and tsl4531x_get_sample, provides + * asynchronous operation. In high power mode, this counts down from the point + * at which the driver is switched into high power mode or started up, and + * indicates whether enough time has passed for a full sample to be collected. + * + * Note that for low power mode this rolls over and repeats its behaviour every + * 1.2 hours. The sample should have been collected well before this, however. + * + * The countdown time equals the integration time, which can be set in the + * device initialisation parameters, plus 5% margin to encompass the max times + * indicated in the datasheet. + * + * @param dev Initialized device descriptor + * + * @return Time in microseconds until sample is ready + */ +uint32_t tsl4531x_time_until_sample_ready(tsl4531x_t *dev); + +/** + * Reads the sample from the device immediately. In high power mode, this does + * the same as tsl4531x_simple_read once the device has performed one + * integration cycle. In low power mode, this provides asynchronous operation + * along with tsl4531x_start_sample and tsl4531x_time_until_sample_ready which + * determine whether the device has performed an integration cycle. + * + * Note that this function will always return the value stored in the device's + * internal register, and this value will be sensible physically, representing + * the last time an integration cycle has been performed. However, in order for + * it to be accurate, the start_sample and time_until_sample_ready functions + * need to also be used, or alternatively the simple_read function can be used. + * + * @param dev Initialized device descriptor + * + * @return The sample value in Lux + */ +int tsl4531x_get_sample(const tsl4531x_t *dev); + +/** + * Reads the sample from the device. In low power mode, or in high power mode + * just after startup, this starts collecting the sample, blocks until the + * sample is ready (400/200/100ms depending on the integration time set in the + * initialization parameters), and then reads and returns the sample. + * + * @param dev Initialized device descriptor + * + * @return The sample value in Lux + */ +int tsl4531x_simple_read(tsl4531x_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* TSL4531X_H */ +/** @} */ diff --git a/drivers/tsl4531x/Makefile b/drivers/tsl4531x/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..2d6d86e78ef9301af5336e228ef418abc5e1d678 --- /dev/null +++ b/drivers/tsl4531x/Makefile @@ -0,0 +1,3 @@ +MODULE = tsl4531x + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/tsl4531x/include/tsl4531x_internals.h b/drivers/tsl4531x/include/tsl4531x_internals.h new file mode 100644 index 0000000000000000000000000000000000000000..514f8209887aff35b5a045bc437e205ba3edad1c --- /dev/null +++ b/drivers/tsl4531x/include/tsl4531x_internals.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2017 Inria + * Copyright (C) 2018 Freie Universität Berlin + * + * 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_tsl4531x + * @brief Internal addresses, registers, constants for the TSL4531x sensor. + * @{ + * + * @file + * @brief Internal addresses, registers, constants for the TSL4531x sensor. + * + * @author Alexandre Abadie <alexandre.abadie@inria.fr> + * @author Daniel Petry <danielpetry@cantab.net> + * @author Juan I Carrano <j.carrano@fu-berlin.de> + * + * Derived from the internals.h file for the tsl2561 driver. + */ + +#ifndef TSL4531X_INTERNALS_H +#define TSL4531X_INTERNALS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name TSL4531x internal registers + * @{ + */ +#define TSL4531X_CONTROL_REG (0x0) /**< sets power mode */ +#define TSL4531X_CONFIGURATION_REG (0x01) /**< sets power mode */ +#define TSL4531X_ALSDATA1_REG (0x04) /**< contains DATALOW */ +#define TSL4531X_ALSDATA2_REG (0x05) /**< contains DATAHIGH */ +#define TSL4531X_ID_REG (0x0A) /**< contains part no above*/ +/** @} */ + +/** + * @name TSL4531x power modes + * @{ + */ +#define TSL4531X_MODE_POWER_DOWN (0x00) +#define TSL4531X_MODE_RESERVED (0x01) +#define TSL4531X_MODE_SINGLE_ADC_CYCLE (0x02) /**< Runs a single ADC cycle then + returns to power down. */ +#define TSL4531X_MODE_NORMAL (0x03) + +/* PowerSave saves some power in full power mode. PowerSave skip turns this off. + * Currently PowerSave skip is hard-coded to be on for simplicity, as it's just + * an intermediate between normal mode and low-power mode. */ +#define TSL4531X_PSAVESKIP_OFF (0) +#define TSL4531X_PSAVESKIP_ON (1) +/** @} */ + +/** + * @name TSL4531x macros + * @{ + */ +/* Assemble the Command register */ +#define TSL4531X_COMMAND(addr) ((1 << 7) | (addr)) + +/* Assemble the Configuration register */ +#define TSL4531X_CONFIG(psaveskip, tcntrl) (((!!(psaveskip)) << 3) | (tcntrl)) + +/* Assemble the Control register */ +#define TSL4531X_CONTROL(mode) (mode) + +#define TSL4531X_GET_PARTNO(id_reg) ((id_reg) >> 4) + +/* Data multiplier according to integration time. + * + * From the manual: + * MULTIPLIER = 1 for TCNTRL = 00 (Tint = 400 ms) + * MULTIPLIER = 2 for TCNTRL = 01 (Tint = 200 ms) + * MULTIPLIER = 4 for TCNTRL = 10 (Tint = 100 ms) + */ +#define MULTIPLY_DATA(data_raw, integration_time) ((data_raw) << (integration_time)) + +/* This allows either full power mode or power down mode. */ +#define TSL4531X_POWER_MODE(mode) ((!mode) * (0x03)) + +/* The full integration time is a sum of: + * 1. A worst-case time for each of the integration times, which according to + * the datasheet is 5% more than the nominal time + * 2. 60, 30 or 15 ms, according to the integration time, if PowerSave is on; + * which is the power down period in between integration cycles in this mode. + * Note that in the current implementation, the "PowerSave skip" option is + * hard-coded to be on, as PowerSave only gives slightly less power + * consumption than normal mode. + */ +#define TSL4531X_GET_INTEGRATION_TIME_USEC(int_time_setting, psaveskip) \ + (uint32_t)1000 * (((0x04 >> int_time_setting) * 105) + ((!psaveskip) * (60 >> int_time_setting))) +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* TSL4531X_INTERNALS_H */ +/** @} */ diff --git a/drivers/tsl4531x/include/tsl4531x_params.h b/drivers/tsl4531x/include/tsl4531x_params.h new file mode 100644 index 0000000000000000000000000000000000000000..c6b548b7dd26200e773ca86a92cd9a281c8d5743 --- /dev/null +++ b/drivers/tsl4531x/include/tsl4531x_params.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2017 Inria + * Copyright (C) 2018 Freie Universität Berlin + * + * 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_tsl4531x + * + * @{ + * @file + * @brief Default configuration for tsl4531x light sensor. + * + * @author Alexandre Abadie <alexandre.abadie@inria.fr> + * @author Daniel Petry <danielpetry@cantab.net> + * + * Derived from the default configuration for the tsl2561 driver. + */ + +#ifndef TSL4531X_PARAMS_H +#define TSL4531X_PARAMS_H + +#include "board.h" +#include "saul_reg.h" +#include "tsl4531x.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Set default configuration parameters for the TSL4531x + * @{ + */ +#ifndef TSL4531X_PARAM_I2C_DEV +#define TSL4531X_PARAM_I2C_DEV I2C_DEV(0) +#endif +#ifndef TSL4531X_PARAM_I2C_ADDR +#define TSL4531X_PARAM_I2C_ADDR TSL45315_ADDR +#endif +#ifndef TSL4531X_PARAM_INTEGRATION +#define TSL4531X_PARAM_INTEGRATION TSL4531X_INTEGRATE_400MS +#endif +#ifndef TSL4531X_LOW_POWER_MODE +#define TSL4531X_LOW_POWER_MODE (false) +#endif +#ifndef TSL4531X_PARAM_PARTNO +#define TSL4531X_PARAM_PARTNO TSL45315_PARTNO +#endif +#ifndef TSL4531X_PARAMS +#define TSL4531X_PARAMS { .i2c_dev = TSL4531X_PARAM_I2C_DEV, \ + .i2c_addr = TSL4531X_PARAM_I2C_ADDR, \ + .integration_time = TSL4531X_PARAM_INTEGRATION, \ + .low_power_mode = TSL4531X_LOW_POWER_MODE, \ + .part_number = TSL4531X_PARAM_PARTNO } +#endif + +#ifndef TSL4531X_SAUL_INFO +#define TSL4531X_SAUL_INFO { .name = "tsl4531x" } +#endif +/**@}*/ + +/** + * @brief Configure TSL4531x + */ +static const tsl4531x_params_t tsl4531x_params[] = +{ + TSL4531X_PARAMS +}; + +/** + * @brief Allocate and configure entries to the SAUL registry + */ +saul_reg_info_t tsl4531x_saul_info[] = +{ + TSL4531X_SAUL_INFO +}; + +#ifdef __cplusplus +} +#endif + +#endif /* TSL4531X_PARAMS_H */ +/** @} */ diff --git a/drivers/tsl4531x/tsl4531x.c b/drivers/tsl4531x/tsl4531x.c new file mode 100644 index 0000000000000000000000000000000000000000..13bdaf1f75000600cb63d163117c7dae050e30a8 --- /dev/null +++ b/drivers/tsl4531x/tsl4531x.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2016 Inria + * Copyright (C) 2018 Freie Universität Berlin + * + * 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_tsl4531x + * @{ + * + * @file + * @brief Device driver for the TSL4531x Luminosity sensor. + * + * @author Daniel Petry <daniel.petry@fu-berlin.de> + * @author Juan I Carrano <j.carrano@fu-berlin.de> + * @author Alexandre Abadie <alexandre.abadie@inria.fr> + * + * This driver was derived from the TSL2561 driver. + * + * @} + */ + +#include <errno.h> + +#include "log.h" +#include "tsl4531x.h" +#include "tsl4531x_internals.h" +#include "xtimer.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define _DATALOW 0 +#define _DATAHIGH 1 + +int tsl4531x_init(tsl4531x_t *dev, const tsl4531x_params_t *params) +{ + int r; + uint8_t id; + + /* Initialise I2C bus */ + if ((r = i2c_acquire(params->i2c_dev)) < 0) { + DEBUG("I2C_dev is: %d.", params->i2c_dev); + DEBUG("[Error] Cannot acquire device. I2C error: %d\n", r); + return -ENODEV; + } + + /* Test for connectivity - verify ID and compare against stored value */ + if ((r = i2c_read_reg(params->i2c_dev, + params->i2c_addr, + TSL4531X_COMMAND(TSL4531X_ID_REG), + &id, 0)) < 0) { + DEBUG("[Error] Cannot read ID register. I2C error: %d\n", r); + i2c_release(params->i2c_dev); + return -ENXIO; + } + + DEBUG("[Info] tsl4531x sensor ID ? %d\n", id); + + if (TSL4531X_GET_PARTNO(id) != params->part_number) { + DEBUG("[Error] not a TSL4531 sensor.\n"); + i2c_release(params->i2c_dev); + return -ENOTSUP; + } + + /* Configure device. In low power mode, we initially power the sensor down. */ + if (((r = i2c_write_reg(params->i2c_dev, + params->i2c_addr, + TSL4531X_COMMAND(TSL4531X_CONTROL_REG), + TSL4531X_CONTROL(TSL4531X_POWER_MODE(params->low_power_mode)), + 0)) < 0) + || + ((r = i2c_write_reg(params->i2c_dev, + params->i2c_addr, + TSL4531X_COMMAND(TSL4531X_CONFIGURATION_REG), + TSL4531X_CONFIG(TSL4531X_PSAVESKIP_ON, params->integration_time), + 0)) < 0)) { + + DEBUG("[Error] Cannot configure device. I2C error: %d\n", r); + i2c_release(params->i2c_dev); + return -ENXIO; + } + + /* If device was configured correctly, initialise the device descriptor */ + dev->i2c_dev = params->i2c_dev; + dev->i2c_addr = params->i2c_addr; + dev->integration_time = params->integration_time; + dev->low_power_mode = params->low_power_mode; + dev->high_power_mode_started_up = false; + dev->sample_start_time = 0; /* Device assumed to start up at same time as + microcontroller - i.e. when it hits this line */ + + i2c_release(params->i2c_dev); + + return 0; +} + +int tsl4531x_set_low_power_mode(tsl4531x_t *dev, uint8_t low_power_mode) +{ + assert(dev); + + int r; + + dev->low_power_mode = low_power_mode; + + if ((r = i2c_acquire(dev->i2c_dev)) < 0) { + DEBUG("[Error] Cannot acquire device. I2C error: %d\n", r); + return -ENODEV; + } + + if ((r = i2c_write_reg(dev->i2c_dev, + dev->i2c_addr, + TSL4531X_COMMAND(TSL4531X_CONTROL_REG), + TSL4531X_CONTROL(TSL4531X_POWER_MODE(low_power_mode)), + 0)) < 0) { + DEBUG("[Error] Cannot write power mode. I2C error: %d\n", r); + i2c_release(dev->i2c_dev); + return -ENXIO; + } + + i2c_release(dev->i2c_dev); + + /* In high power mode only, we restart the sample ready timer, because only + in this mode it's used to indicate readiness after startup. */ + if (!dev->low_power_mode) { + dev->sample_start_time = xtimer_now_usec(); + } + + return 0; +} + +int tsl4531x_start_sample(tsl4531x_t *dev) +{ + assert(dev); + + /* Don't change the mode to one-shot if the device is in high power mode. */ + if (dev->low_power_mode) { + + int r; + + if ((r = i2c_acquire(dev->i2c_dev)) < 0) { + DEBUG("[Error] Cannot acquire device. I2C error: %d\n", r); + return -ENODEV; + } + + if ((r = i2c_write_reg(dev->i2c_dev, + dev->i2c_addr, + TSL4531X_COMMAND(TSL4531X_CONTROL_REG), + TSL4531X_CONTROL(TSL4531X_MODE_SINGLE_ADC_CYCLE), + 0)) < 0) { + DEBUG("[Error] Cannot write power mode. I2C error: %d\n", r); + i2c_release(dev->i2c_dev); + return -ENXIO; + } + + i2c_release(dev->i2c_dev); + + dev->sample_start_time = xtimer_now_usec(); + } + + return 0; +} + +uint32_t tsl4531x_time_until_sample_ready(tsl4531x_t *dev) +{ + assert(dev); + + uint32_t t = TSL4531X_GET_INTEGRATION_TIME_USEC(dev->integration_time, TSL4531X_PSAVESKIP_ON) - + (xtimer_now_usec() - dev->sample_start_time); + + /* Clamp t at zero */ + t = (t <= TSL4531X_GET_INTEGRATION_TIME_USEC(dev->integration_time, TSL4531X_PSAVESKIP_ON) ? + t : 0); + + if (!dev->low_power_mode) { + if (t == 0) { + dev->high_power_mode_started_up = true; + } + if (dev->high_power_mode_started_up) { + return 0; + } + } + + return t; +} + +int tsl4531x_get_sample(const tsl4531x_t *dev) +{ + assert(dev); + + int r; + uint8_t als_data[2]; /* = {[DATALOW], [DATAHIGH]} */ + + if ((r = i2c_acquire(dev->i2c_dev)) < 0) { + DEBUG("[Error] Cannot acquire device. I2C error: %d\n", r); + return -ENODEV; + } + + if ((r = i2c_read_regs(dev->i2c_dev, + dev->i2c_addr, + TSL4531X_COMMAND(TSL4531X_ALSDATA1_REG), + als_data, 2, 0)) < 0) { + DEBUG("[Error] Cannot read data register. I2C error: %d\n", r); + i2c_release(dev->i2c_dev); + return -ENXIO; + } + + i2c_release(dev->i2c_dev); + + return MULTIPLY_DATA((((uint16_t)als_data[_DATAHIGH]) << 8) + + als_data[_DATALOW], dev->integration_time); +} + +int tsl4531x_simple_read(tsl4531x_t *dev) +{ + assert(dev); + + if (dev->low_power_mode) { + tsl4531x_start_sample(dev); + } + + xtimer_usleep(tsl4531x_time_until_sample_ready(dev)); + + return tsl4531x_get_sample(dev); +} diff --git a/drivers/tsl4531x/tsl4531x_saul.c b/drivers/tsl4531x/tsl4531x_saul.c new file mode 100644 index 0000000000000000000000000000000000000000..c21b09a62c389dfcce9830f7ea12de893fc69ac7 --- /dev/null +++ b/drivers/tsl4531x/tsl4531x_saul.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 Inria + * Copyright (C) 2018 Freie Universität Berlin + * + * 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_tsl4531x + * @{ + * + * @file + * @brief SAUL interface for TSL4531x Luminosity sensor. + * + * @author Juan I Carrano <j.carrano@fu-berlin.de> + * @author Daniel Petry <daniel.petry@fu-berlin.de> + * + * @} + */ + +#include <string.h> +#include "saul.h" +#include "tsl4531x.h" + +static int _read(const void *dev, phydat_t *data) +{ + memset(data, 0, sizeof(phydat_t)); + data->val[0] = tsl4531x_simple_read(*(tsl4531x_t * const*)dev); + data->unit = UNIT_LUX; + data->scale = 0; + return 1; +} + +const saul_driver_t tsl4531x_saul_driver = { + .read = _read, + .write = saul_notsup, + .type = SAUL_SENSE_LIGHT +}; diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index d81277d478d78782add9ec3e1a06269261ceef8a..2bda5b71935803e57875340c07c65c019e8dec61 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -448,6 +448,10 @@ void auto_init(void) extern void auto_init_tsl2561(void); auto_init_tsl2561(); #endif +#ifdef MODULE_TSL4531X + extern void auto_init_tsl4531x(void); + auto_init_tsl4531x(); +#endif #ifdef MODULE_VCNL40X0 extern void auto_init_vcnl40x0(void); auto_init_vcnl40x0(); diff --git a/sys/auto_init/saul/auto_init_tsl4531x.c b/sys/auto_init/saul/auto_init_tsl4531x.c new file mode 100644 index 0000000000000000000000000000000000000000..0496666df186d61a31ce26d0d55b77399c46f75b --- /dev/null +++ b/sys/auto_init/saul/auto_init_tsl4531x.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2016 Inria + * Copyright (C) 2018 Freie Universität Berlin + * + * 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 sys_auto_init_saul + * @{ + * + * @file + * @brief Auto initialization of TSL4531x driver. + * + * @author Alexandre Abadie <alexandre.abadie@inria.fr> + * @author Daniel Petry <daniel.petry@fu-berlin.de> + * + * Adapted from auto_init for the TSL2561 driver. + * + * @} + */ + +#ifdef MODULE_TSL4531X + +#include "log.h" +#include "saul_reg.h" +#include "tsl4531x.h" +#include "tsl4531x_params.h" + +/** + * @brief Define the number of configured sensors + */ +#define TSL4531X_NUMOF (sizeof(tsl4531x_params) / sizeof(tsl4531x_params[0])) + +/** + * @brief Allocation of memory for device descriptors + */ +static tsl4531x_t tsl4531x_devs[TSL4531X_NUMOF]; +static tsl4531x_t *tsl4531x_devs_p[TSL4531X_NUMOF]; + +/** + * @brief Memory for the SAUL registry entries + */ +static saul_reg_t saul_entries[TSL4531X_NUMOF]; + +/** + * @brief Define the number of saul info + */ +#define TSL4531X_INFO_NUMOF (sizeof(tsl4531x_saul_info) / sizeof(tsl4531x_saul_info[0])) + +/** + * @brief Reference the driver structs. + */ +extern const saul_driver_t tsl4531x_saul_driver; + +void auto_init_tsl4531x(void) +{ + assert(TSL4531X_NUMOF == TSL4531X_INFO_NUMOF); + + for (unsigned i = 0; i < TSL4531X_NUMOF; i++) { + printf("[auto_init_saul] initializing tsl4531x #%u\n", i); + + tsl4531x_devs_p[i] = &tsl4531x_devs[i]; + + if (tsl4531x_init(&tsl4531x_devs[i], + &tsl4531x_params[i]) != 0) { + LOG_ERROR("[auto_init_saul] error initializing tsl4531x #%u\n", i); + continue; + } + + /* Fill the saul_entries struct */ + saul_entries[i].dev = &(tsl4531x_devs_p[i]); + saul_entries[i].name = tsl4531x_saul_info[i].name; + saul_entries[i].driver = &tsl4531x_saul_driver; + + /* register to saul */ + saul_reg_add(&(saul_entries[i])); + } +} +#else +typedef int dont_be_pedantic; +#endif /* MODULE_TSL4531X */ diff --git a/tests/driver_tsl4531x/Makefile b/tests/driver_tsl4531x/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..487030b625962f9f58668b9267ba7b732b6986b2 --- /dev/null +++ b/tests/driver_tsl4531x/Makefile @@ -0,0 +1,6 @@ +include ../Makefile.tests_common + +USEMODULE += tsl4531x +USEMODULE += xtimer + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_tsl4531x/README.md b/tests/driver_tsl4531x/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b6e4b7082600d8aae07f14d2b3d9d4fe39679171 --- /dev/null +++ b/tests/driver_tsl4531x/README.md @@ -0,0 +1,10 @@ +## About + +This is a test application for the TSL4531x Lux sensor series. + +## Usage + +The application first initializes the TSL2561x sensor. + +After initialization, the test application runs through a series of tests to +allow the user to manually verify the correct operation of the sensor driver. diff --git a/tests/driver_tsl4531x/main.c b/tests/driver_tsl4531x/main.c new file mode 100644 index 0000000000000000000000000000000000000000..ae96b592a3e5c84d43d1c9d26722f00398b0ff33 --- /dev/null +++ b/tests/driver_tsl4531x/main.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2016 Inria + * Copyright (C) 2018 Freie Universität Berlin + * + * 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 tests + * @{ + * + * @file + * @brief Test application for the TSL4531x Lux sensor + * + * @author Daniel Petry <daniel.petry@fu-berlin.de> + * + * @} + */ + +#include <stdio.h> +#include <string.h> +#include <stdint.h> + +#include "xtimer.h" +#include "periph/i2c.h" + +#include "tsl4531x.h" +#include "tsl4531x_params.h" + +#define _100ms_in_us (100 * 1000u) /* 1 second delay between printf */ + +int main(void) +{ + tsl4531x_t dev; + int err; + + puts("TSL4531x test application. Initializing..."); + + if ((err = tsl4531x_init(&dev, tsl4531x_params)) < 0) { + printf("[Error] Device not initialised. Error code: %d\n", err); + return 1; + } + + puts("[Info] Initialized, beginning test."); + + while (1) { + + uint16_t lux; + + printf("-------------------------------------------------------------"); + printf("-------------------------\n"); + + /* Set into high power mode */ + tsl4531x_set_low_power_mode(&dev, false); + + /* Test simple read - high power mode */ + lux = tsl4531x_simple_read(&dev); + printf("Illuminance | High power mode | Synchronous read |"); + printf(" [lx] | %u\n", lux); + + /* Determine the actual integration time - how long does it take for a + value to change? + Note that if the sensor value doesn't change between integration + cycles, this will sum the previous integration times. This mostly + won't happen, but it's best to let this run for a few cycles and take + the minimum. */ + uint16_t lux_last = lux; + uint8_t changes = 0; + uint32_t change_times[2]; + while (changes < 2) { + lux = tsl4531x_get_sample(&dev); + if (lux != lux_last) { + lux_last = lux; + change_times[changes] = xtimer_now_usec(); + changes++; + } + } + + printf("Sample ready time | High power mode | From device |"); + printf(" [us] | %lu\n", (unsigned long)(change_times[1] - change_times[0])); + + /* Set into low power mode */ + tsl4531x_set_low_power_mode(&dev, true); + + /* This tests what happens when you read without asking for a sample in + low power mode. */ + lux = tsl4531x_get_sample(&dev); + printf("Illuminance | Low power mode | Immediate read after mode change |"); + printf(" [lx] | %u\n", lux); + xtimer_usleep(tsl4531x_time_until_sample_ready(&dev)); + lux = tsl4531x_get_sample(&dev); + printf("Illuminance | Low power mode | One cycle time after mode change |"); + printf(" [lx] | %u\n", lux); + + /* Test synchronous read - low power mode */ + lux = tsl4531x_simple_read(&dev); + printf("Illuminance | Low power mode | Synchronous read |"); + printf(" [lx] | %u\n", lux); + + /* Test asynchronous read - low power mode */ + tsl4531x_start_sample(&dev); + + /* Verify that the stated time until sample ready is reasonable. */ + uint32_t t = tsl4531x_time_until_sample_ready(&dev); + xtimer_usleep(t); + lux = tsl4531x_get_sample(&dev); + printf("Illuminance | Low power mode | Asynchronous read |"); + printf(" [lx] | %u\n", lux); + printf("Sample ready time | Low power mode | From driver |"); + printf(" [us] | %lu\n", (unsigned long)t); + + xtimer_usleep(_100ms_in_us); + } + + return 0; +}