Browse Source

drivers/tsl2561: initial implementation + saul support

pr/rotary
Alexandre Abadie 6 years ago committed by Alexandre Abadie
parent
commit
25ce12f3ef
  1. 3
      drivers/Makefile.include
  2. 113
      drivers/include/tsl2561.h
  3. 3
      drivers/tsl2561/Makefile
  4. 108
      drivers/tsl2561/include/tsl2561_internals.h
  5. 79
      drivers/tsl2561/include/tsl2561_params.h
  6. 291
      drivers/tsl2561/tsl2561.c
  7. 40
      drivers/tsl2561/tsl2561_saul.c
  8. 4
      sys/auto_init/auto_init.c
  9. 75
      sys/auto_init/saul/auto_init_tsl2561.c

3
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

113
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 <alexandre.abadie@inria.fr>
*/
#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_ */
/** @} */

3
drivers/tsl2561/Makefile

@ -0,0 +1,3 @@
MODULE = tsl2561
include $(RIOTBASE)/Makefile.base

108
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 <alexandre.abadie@inria.fr>
*/
#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_ */
/** @} */

79
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 <alexandre.abadie@inria.fr>
*/
#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
/** @} */

291
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 <alexandre.abadie@inria.fr>
*
* @}
*/
#include <math.h>
#include <string.h>
#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;
}
}

40
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 <hauke.petersen@fu-berlin.de>
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*
* @}
*/
#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
};

4
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();

75
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 <alexandre.abadie@inria.fr>
*
* @}
*/
#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 */
Loading…
Cancel
Save