diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 12ff91b964..a3c7043538 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 @@ -475,7 +480,3 @@ ifneq (,$(filter xbee,$(USEMODULE))) USEMODULE += xtimer USEMODULE += netif endif - -ifneq (,$(filter tsl4531x,$(USEMODULE))) - FEATURES_REQUIRED += periph_i2c -endif diff --git a/drivers/Makefile.include b/drivers/Makefile.include index 19bb780f39..1c2c0e61b2 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 index 2bcd4b6a42..cc79973467 100644 --- a/drivers/include/tsl4531x.h +++ b/drivers/include/tsl4531x.h @@ -12,49 +12,184 @@ * @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 + * @author Daniel Petry */ -#ifndef TSL4531x_H -#define TSL4531x_H +#ifndef TSL4531X_H +#define TSL4531X_H #include #include "periph/i2c.h" -static const uint16_t TSL4531x_INTEGRATE_400ms = 0; /* 0b00 */ -static const uint16_t TSL4531x_INTEGRATE_200ms = 1; /* 0b01 */ -static const uint16_t TSL4531x_INTEGRATE_100ms = 2; /* 0b10 */ +#ifdef __cplusplus +extern "C" { +#endif /** - * @brief Represents a single TSL4531x device. + * @brief Integration times */ -typedef struct tsl4531x { - i2c_t i2c_dev; - uint8_t integration_time; +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 TSL4513 device. + * Initialize the TSL4531x device. * - * @param dev Device object. - * @param i2c_dev I2C interface to use. - * @param integration_time one of TSL4531x_INTEGRATE_400ms, - * TSL4531x_INTEGRATE_200ms, TSL4531x_INTEGRATE_100ms. + * @param[out] dev Initialized device descriptor + * @param[in] params Device initialization parameters * - * @return Zero on success, negative on error. + * @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, i2c_t i2c_dev, uint8_t integration_time); +int tsl4531x_init(tsl4531x_t *dev, const tsl4531x_params_t *params); /** - * Get the last measurements of illuminance, in lux. + * Set the low power mode of the driver on or off. * - * @return 0 on sucess, negative on error. If there is an error, the value of - * `*result_lux` is undefined. + * @param dev Initialized device descriptor + * @param low_power_on Bool indicating whether low power mode is on or off + * + * @return Zero */ -int tsl4531x_read(const tsl4531x_t *dev, uint16_t *result_lux); +int tsl4531x_set_low_power_mode(tsl4531x_t *dev, uint8_t low_power_on); -#endif /* TSL4531x_H */ +/** + * 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/include/tsl4531x_saul.h b/drivers/include/tsl4531x_saul.h deleted file mode 100644 index 7783b1f469..0000000000 --- a/drivers/include/tsl4531x_saul.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 TSL4531 Luminosity sensor. - * - * @author Juan I Carrano - * - * @} - */ - -#ifndef TSL4531x_SAUL_H -#define TSL4531x_SAUL_H - -#include "saul.h" - -/** SAUL-compatible structure for illuminance sensor - */ -const saul_driver_t tsl4531x_saul_driver; - -#endif /* TSL4531x_SAUL_H */ - diff --git a/drivers/tsl4531x/include/tsl4531x_internals.h b/drivers/tsl4531x/include/tsl4531x_internals.h new file mode 100644 index 0000000000..514f820988 --- /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 + * @author Daniel Petry + * @author Juan I Carrano + * + * 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 0000000000..c6b548b7dd --- /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 + * @author Daniel Petry + * + * 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 index 703982ad77..13bdaf1f75 100644 --- a/drivers/tsl4531x/tsl4531x.c +++ b/drivers/tsl4531x/tsl4531x.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2016 Inria * Copyright (C) 2018 Freie Universität Berlin * * This file is subject to the terms and conditions of the GNU Lesser @@ -11,15 +12,13 @@ * @{ * * @file - * @brief Device driver for the TSL4531 Luminosity sensor. + * @brief Device driver for the TSL4531x Luminosity sensor. * + * @author Daniel Petry * @author Juan I Carrano + * @author Alexandre Abadie * - * This driver was derived from the one for TSL2561. - * - * @todo The i2c address is hardcoded - * @todo The only operating mode is "Normal Operation" - * @todo PowerSave is not supported + * This driver was derived from the TSL2561 driver. * * @} */ @@ -28,113 +27,202 @@ #include "log.h" #include "tsl4531x.h" +#include "tsl4531x_internals.h" +#include "xtimer.h" #define ENABLE_DEBUG (0) #include "debug.h" -static const uint16_t TSL4531_Addr = 0x29; -static const uint16_t TSL4531_PARTNO = 0xA; /* 0b1010 */ +#define _DATALOW 0 +#define _DATAHIGH 1 -static const uint16_t Control_Reg = 0x0; -static const uint16_t Configuration_Reg = 0x01; -static const uint16_t ALSData1_Reg = 0x04; /* contains DATALOW */ -static const uint16_t ALSData2_Reg = 0x05; /* contains DATAHIGH */ -static const uint16_t ID_Reg = 0x0A; - -static const uint16_t TSL4531x_MODE_Normal = 0x03; /* 0b11 */ - -/* Assemble the Command register */ -#define TSL4531_COMMAND(addr) ((1<<7)|(addr)) - -/* Assemble the Configuration register */ -#define TSL4531_CONFIG(psaveskip, tcntrl) (((!!(psaveskip)) << 3) | (tcntrl)) - -/* Assemble the Control register */ -#define TSL4531_CONTROL(mode) (mode) - -#define TSL4531_GET_PARTNO(id_reg) ((id_reg) >> 4) - -/* Apply the multiplier corresponding to the active integration time */ -/* From the manual: - * MULTIPLIER = 1 for TCNTRL = 00 (Tint = 400 ms), - * MULTIPLIER = 2 for TCNTRL = 01 (Tint = 200 ms), and - * MULTIPLIER = 4 for TCNTRL = 10 (Tint = 100 ms), and - */ -#define MULTIPLY_DATA(data_raw, integration_time) ((data_raw) << (integration_time)) - -int tsl4531x_init(tsl4531x_t *dev, i2c_t i2c_dev, uint8_t integration_time) +int tsl4531x_init(tsl4531x_t *dev, const tsl4531x_params_t *params) { int r; uint8_t id; - dev->i2c_dev = i2c_dev; - dev->integration_time = integration_time; - - if ((r = i2c_acquire(dev->i2c_dev)) < 0) { - DEBUG("[Error] Cannot acquire device: %d\n", r); - goto acquire_error; + /* 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; } - /* Verify sensor ID */ - if ((r = i2c_read_reg(dev->i2c_dev, TSL4531_Addr, TSL4531_COMMAND(ID_Reg), + /* 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: %d\n", r); - goto init_error; - } - DEBUG("[Info] ID ? %d\n", id); - if (TSL4531_GET_PARTNO(id) != TSL4531_PARTNO) { - DEBUG("[Error] not a TSL4531 sensor\n"); - r = -ENOTSUP; - goto init_error; + DEBUG("[Error] Cannot read ID register. I2C error: %d\n", r); + i2c_release(params->i2c_dev); + return -ENXIO; } - /* configure device */ - if (((r = i2c_write_reg(dev->i2c_dev , TSL4531_Addr, TSL4531_COMMAND(Control_Reg), - TSL4531_CONTROL(TSL4531x_MODE_Normal), 0)) < 0) - || - ((r = i2c_write_reg(dev->i2c_dev , TSL4531_Addr, TSL4531_COMMAND(Configuration_Reg), - TSL4531_CONFIG(1, integration_time), 0)) < 0)) { - DEBUG("[Error] Cannot configure device %d\n", r); - goto init_error; + 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; } -init_error: - /* ignore errors on device release */ - i2c_release(dev->i2c_dev); + /* 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)) { -acquire_error: + DEBUG("[Error] Cannot configure device. I2C error: %d\n", r); + i2c_release(params->i2c_dev); + return -ENXIO; + } - return r; + /* 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; } -#define _DATALOW 0 -#define _DATAHIGH 1 - -int tsl4531x_read(const tsl4531x_t *dev, uint16_t *result_lux) +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: %d\n", r); - goto read_acquire_error; + DEBUG("[Error] Cannot acquire device. I2C error: %d\n", r); + return -ENODEV; } - if ((r = i2c_read_regs(dev->i2c_dev, TSL4531_Addr, TSL4531_COMMAND(ALSData1_Reg), - als_data, 2, 0)) < 0) { - (void)ALSData2_Reg; /* suppress warning */ - DEBUG("[Error] Cannot read device: %d\n", r); - goto read_error; + 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; } - *result_lux = MULTIPLY_DATA((((uint16_t)als_data[_DATAHIGH]) << 8) - + als_data[_DATALOW], dev->integration_time); - -read_error: - /* ignore errors on device release */ i2c_release(dev->i2c_dev); -read_acquire_error: - return r; + 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 index 010c146111..ee719ac230 100644 --- a/drivers/tsl4531x/tsl4531x_saul.c +++ b/drivers/tsl4531x/tsl4531x_saul.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2016 Inria * Copyright (C) 2018 Freie Universität Berlin * * This file is subject to the terms and conditions of the GNU Lesser @@ -11,27 +12,25 @@ * @{ * * @file - * @brief SAUL interface for TSL4531 Luminosity sensor. + * @brief SAUL interface for TSL4531x Luminosity sensor. * * @author Juan I Carrano + * @author Daniel Petry * * @} */ +#include #include "saul.h" #include "tsl4531x.h" -static int _read(const void *dev, phydat_t *res) +static int _read(const void *dev, phydat_t *data) { - int nvals; - uint16_t lux; - - nvals = (tsl4531x_read(dev, &lux) >= 0)? 1 : 0; - - res->val[0] = lux; - res->unit = UNIT_LUX; - res->scale = 0; - return nvals; + memset(data, 0, sizeof(phydat_t)); + data->val[0] = tsl4531x_simple_read((const tsl4531x_t *)dev); + data->unit = UNIT_LUX; + data->scale = 0; + return 1; } const saul_driver_t tsl4531x_saul_driver = {