From 63dcc18c55b172c4312b88f3483f5bbb995f302a Mon Sep 17 00:00:00 2001
From: Juan Carrano <j.carrano@fu-berlin.de>
Date: Mon, 10 Sep 2018 14:13:52 +0200
Subject: [PATCH] drivers/tsl4531x: Add driver for TSL45315 illuiminance
 sensor.

Missing run modes and power saving configurations, but otherwise
functional.
---
 drivers/Makefile.dep             |   4 +
 drivers/include/tsl4531x.h       |  60 +++++++++++++
 drivers/tsl4531x/Makefile        |   3 +
 drivers/tsl4531x/tsl4531x.c      | 140 +++++++++++++++++++++++++++++++
 drivers/tsl4531x/tsl4531x_saul.c |  41 +++++++++
 5 files changed, 248 insertions(+)
 create mode 100644 drivers/include/tsl4531x.h
 create mode 100644 drivers/tsl4531x/Makefile
 create mode 100644 drivers/tsl4531x/tsl4531x.c
 create mode 100644 drivers/tsl4531x/tsl4531x_saul.c

diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep
index 6a3de6c93d..12ff91b964 100644
--- a/drivers/Makefile.dep
+++ b/drivers/Makefile.dep
@@ -475,3 +475,7 @@ ifneq (,$(filter xbee,$(USEMODULE)))
   USEMODULE += xtimer
   USEMODULE += netif
 endif
+
+ifneq (,$(filter tsl4531x,$(USEMODULE)))
+  FEATURES_REQUIRED += periph_i2c
+endif
diff --git a/drivers/include/tsl4531x.h b/drivers/include/tsl4531x.h
new file mode 100644
index 0000000000..2bcd4b6a42
--- /dev/null
+++ b/drivers/include/tsl4531x.h
@@ -0,0 +1,60 @@
+/*
+ * 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
+ * @{
+ *
+ * @file
+ * @brief       Device driver for the AMS TSL4531 sensor
+ *
+ * @author      Juan I Carrano <j.carrano@fu-berlin.de>
+ */
+
+#ifndef TSL4531x_H
+#define TSL4531x_H
+
+#include <stdint.h>
+
+#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 */
+
+/**
+ * @brief   Represents a single TSL4531x device.
+ */
+typedef struct tsl4531x {
+    i2c_t i2c_dev;
+    uint8_t integration_time;
+} tsl4531x_t;
+
+/**
+ * Initialize TSL4513 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.
+ *
+ * @return  Zero on success, negative on error.
+ */
+int tsl4531x_init(tsl4531x_t *dev, i2c_t i2c_dev, uint8_t integration_time);
+
+/**
+ * Get the last measurements of illuminance, in lux.
+ *
+ * @return  0 on sucess, negative on error. If there is an error, the value of
+ *           `*result_lux` is undefined.
+ */
+int tsl4531x_read(const tsl4531x_t *dev, uint16_t *result_lux);
+
+#endif /* TSL4531x_H */
diff --git a/drivers/tsl4531x/Makefile b/drivers/tsl4531x/Makefile
new file mode 100644
index 0000000000..2d6d86e78e
--- /dev/null
+++ b/drivers/tsl4531x/Makefile
@@ -0,0 +1,3 @@
+MODULE = tsl4531x
+
+include $(RIOTBASE)/Makefile.base
diff --git a/drivers/tsl4531x/tsl4531x.c b/drivers/tsl4531x/tsl4531x.c
new file mode 100644
index 0000000000..703982ad77
--- /dev/null
+++ b/drivers/tsl4531x/tsl4531x.c
@@ -0,0 +1,140 @@
+/*
+ * 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 TSL4531 Luminosity sensor.
+ *
+ * @author      Juan I Carrano <j.carrano@fu-berlin.de>
+ *
+ * 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
+ *
+ * @}
+ */
+
+#include <errno.h>
+
+#include "log.h"
+#include "tsl4531x.h"
+
+#define ENABLE_DEBUG        (0)
+#include "debug.h"
+
+static const uint16_t TSL4531_Addr = 0x29;
+static const uint16_t TSL4531_PARTNO = 0xA; /* 0b1010 */
+
+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 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;
+    }
+
+    /* Verify sensor ID */
+    if ((r = i2c_read_reg(dev->i2c_dev, TSL4531_Addr, TSL4531_COMMAND(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;
+    }
+
+    /* 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;
+    }
+
+init_error:
+    /* ignore errors on device release */
+    i2c_release(dev->i2c_dev);
+
+acquire_error:
+
+    return r;
+}
+
+#define _DATALOW 0
+#define _DATAHIGH 1
+
+int tsl4531x_read(const tsl4531x_t *dev, uint16_t *result_lux)
+{
+    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;
+    }
+
+    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;
+    }
+
+    *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;
+}
+
diff --git a/drivers/tsl4531x/tsl4531x_saul.c b/drivers/tsl4531x/tsl4531x_saul.c
new file mode 100644
index 0000000000..f040beb25a
--- /dev/null
+++ b/drivers/tsl4531x/tsl4531x_saul.c
@@ -0,0 +1,41 @@
+/*
+ * 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 <j.carrano@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include "saul.h"
+#include "tsl4531x.h"
+
+static int _read(const void *dev, phydat_t *res)
+{
+    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;
+}
+
+const saul_driver_t tsl2561_illuminance_saul_driver = {
+    .read = _read,
+    .write = saul_notsup,
+    .type = SAUL_SENSE_LIGHT
+};
-- 
GitLab