You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by GitBox <gi...@apache.org> on 2018/07/23 14:03:59 UTC

[GitHub] mkiiskila commented on a change in pull request #1259: TSL2591 driver w/sim stubs

mkiiskila commented on a change in pull request #1259: TSL2591 driver w/sim stubs
URL: https://github.com/apache/mynewt-core/pull/1259#discussion_r204411214
 
 

 ##########
 File path: hw/drivers/sensors/tsl2591/src/tsl2591.c
 ##########
 @@ -0,0 +1,706 @@
+/*****************************************************************************/
+/*!
+    @file     tsl2591.c
+    @author   Kevin Townsend
+    @section LICENSE
+    Software License Agreement (BSD License)
+    Copyright (c) 2018, Kevin Townsend
+    All rights reserved.
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holders nor the
+    names of its contributors may be used to endorse or promote products
+    derived from this software without specific prior written permission.
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/*****************************************************************************/
+
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "os/mynewt.h"
+#include "hal/hal_i2c.h"
+#include "sensor/sensor.h"
+#include "sensor/light.h"
+#include "tsl2591/tsl2591.h"
+#include "tsl2591_priv.h"
+#include "modlog/modlog.h"
+#include "syscfg/syscfg.h"
+#include "stats/stats.h"
+#if MYNEWT_VAL(TSL2591_CONFIG)
+#include "config/config.h"
+#endif
+
+/* Define the stats section and records */
+STATS_SECT_START(tsl2591_stat_section)
+    STATS_SECT_ENTRY(polled)
+    STATS_SECT_ENTRY(gain_changed)
+    STATS_SECT_ENTRY(timing_changed)
+    STATS_SECT_ENTRY(ints_cleared)
+    STATS_SECT_ENTRY(errors)
+STATS_SECT_END
+
+/* Define stat names for querying */
+STATS_NAME_START(tsl2591_stat_section)
+    STATS_NAME(tsl2591_stat_section, polled)
+    STATS_NAME(tsl2591_stat_section, gain_changed)
+    STATS_NAME(tsl2591_stat_section, timing_changed)
+    STATS_NAME(tsl2591_stat_section, ints_cleared)
+    STATS_NAME(tsl2591_stat_section, errors)
+STATS_NAME_END(tsl2591_stat_section)
+
+/* Global variable used to hold stats data */
+STATS_SECT_DECL(tsl2591_stat_section) g_tsl2591stats;
+
+#define TSL2591_LOG(lvl_, ...) \
+    MODLOG_ ## lvl_(MYNEWT_VAL(TSL2591_LOG_MODULE), __VA_ARGS__)
+
+#if MYNEWT_VAL(TSL2591_ITIME_DELAY)
+static int g_tsl2591_itime_delay_ms;
+#endif
+
+/* Exports for the sensor API */
+static int tsl2591_sensor_read(struct sensor *, sensor_type_t,
+        sensor_data_func_t, void *, uint32_t);
+static int tsl2591_sensor_get_config(struct sensor *, sensor_type_t,
+        struct sensor_cfg *);
+
+static const struct sensor_driver g_tsl2591_sensor_driver = {
+    tsl2591_sensor_read,
+    tsl2591_sensor_get_config
+};
+
+int
+tsl2591_write8(struct sensor_itf *itf, uint8_t reg, uint32_t value)
+{
+    int rc;
+    uint8_t payload[2] = { reg, value & 0xFF };
+
+    struct hal_i2c_master_data data_struct = {
+        .address = itf->si_addr,
+        .len = 2,
+        .buffer = payload
+    };
+
+    rc = sensor_itf_lock(itf, MYNEWT_VAL(TSL2591_ITF_LOCK_TMO));
+    if (rc) {
+        return rc;
+    }
+
+    rc = hal_i2c_master_write(itf->si_num, &data_struct,
+                              OS_TICKS_PER_SEC / 10, 1);
+    if (rc) {
+        TSL2591_LOG(ERROR,
+                    "Failed to write 0x%02X:0x%02X with value 0x%02lX\n",
+                    data_struct.address, reg, value);
+        STATS_INC(g_tsl2591stats, errors);
+    }
+
+    sensor_itf_unlock(itf);
+
+    return rc;
+}
+
+int
+tsl2591_write16(struct sensor_itf *itf, uint8_t reg, uint16_t value)
+{
+    int rc;
+    uint8_t payload[3] = { reg, value & 0xFF, (value >> 8) & 0xFF };
+
+    struct hal_i2c_master_data data_struct = {
+        .address = itf->si_addr,
+        .len = 3,
+        .buffer = payload
+    };
+
+    rc = sensor_itf_lock(itf, MYNEWT_VAL(TSL2591_ITF_LOCK_TMO));
+    if (rc) {
+        return rc;
+    }
+
+    rc = hal_i2c_master_write(itf->si_num, &data_struct,
+                              OS_TICKS_PER_SEC / 10, 1);
+    if (rc) {
+        TSL2591_LOG(ERROR,
+                    "Failed to write @0x%02X with value 0x%02X 0x%02X\n",
+                    reg, payload[0], payload[1]);
+    }
+
+    sensor_itf_unlock(itf);
+
+    return rc;
+}
+
+int
+tsl2591_read8(struct sensor_itf *itf, uint8_t reg, uint8_t *value)
+{
+    int rc;
+    uint8_t payload;
+
+    struct hal_i2c_master_data data_struct = {
+        .address = itf->si_addr,
+        .len = 1,
+        .buffer = &payload
+    };
+
+    rc = sensor_itf_lock(itf, MYNEWT_VAL(TSL2591_ITF_LOCK_TMO));
+    if (rc) {
+        return rc;
+    }
+
+    /* Register write */
+    payload = reg;
+    rc = hal_i2c_master_write(itf->si_num, &data_struct,
+                              OS_TICKS_PER_SEC / 10, 1);
+    if (rc) {
+        TSL2591_LOG(ERROR, "Failed to address sensor\n");
+        goto err;
+    }
+
+    /* Read one byte back */
+    payload = 0;
+    rc = hal_i2c_master_read(itf->si_num, &data_struct,
+                             OS_TICKS_PER_SEC / 10, 1);
+    *value = payload;
+    if (rc) {
+        TSL2591_LOG(ERROR, "Failed to read @0x%02X\n", reg);
+    }
+
+err:
+    sensor_itf_unlock(itf);
+
+    return rc;
+}
+
+int
+tsl2591_read16(struct sensor_itf *itf, uint8_t reg, uint16_t *value)
+{
+    int rc;
+    uint8_t payload[2] = { reg, 0 };
+
+    struct hal_i2c_master_data data_struct = {
+        .address = itf->si_addr,
+        .len = 1,
+        .buffer = payload
+    };
+
+    rc = sensor_itf_lock(itf, MYNEWT_VAL(TSL2591_ITF_LOCK_TMO));
+    if (rc) {
+        return rc;
+    }
+
+    /* Register write */
+    rc = hal_i2c_master_write(itf->si_num, &data_struct,
+                              OS_TICKS_PER_SEC / 10, 1);
+    if (rc) {
+        TSL2591_LOG(ERROR, "Failed to address sensor\n");
+        goto err;
+    }
+
+    /* Read two bytes back */
+    memset(payload, 0, 2);
+    data_struct.len = 2;
+    rc = hal_i2c_master_read(itf->si_num, &data_struct,
+                             OS_TICKS_PER_SEC / 10, 1);
+    *value = (uint16_t)payload[0] | ((uint16_t)payload[1] << 8);
+    if (rc) {
+        TSL2591_LOG(ERROR, "Failed to read @0x%02X\n", reg);
+        goto err;
+    }
+
+err:
+    sensor_itf_unlock(itf);
+
+    return rc;
+}
+
+int
+tsl2591_enable(struct sensor_itf *itf, uint8_t state)
+{
+    /* Enable the device by setting the PON and AEN bits */
+    return tsl2591_write8(itf, TSL2591_COMMAND_BIT | TSL2591_REGISTER_ENABLE,
+                          state ? TSL2591_ENABLE_POWERON | TSL2591_ENABLE_AEN :
+                          TSL2591_ENABLE_POWEROFF);
+}
+
+int
+tsl2591_get_enable(struct sensor_itf *itf, uint8_t *enabled)
+{
+    int rc;
+    uint8_t reg;
+
+    /* Check the two enable bits (PON and AEN) */
+    rc =  tsl2591_read8(itf, TSL2591_COMMAND_BIT | TSL2591_REGISTER_ENABLE,
+                        &reg);
+    if (rc) {
+        goto err;
+    }
+
+    *enabled = reg & (TSL2591_ENABLE_POWERON | TSL2591_ENABLE_AEN) ? 1 : 0;
+
+    return 0;
+err:
+    return rc;
+}
+
+int
+tsl2591_set_integration_time(struct sensor_itf *itf,
+                             uint8_t int_time)
+{
+    int rc;
+    uint8_t gain;
+
+    if (int_time > TSL2591_LIGHT_ITIME_600MS) {
+      int_time = TSL2591_LIGHT_ITIME_600MS;
+    }
+
+    rc = tsl2591_get_gain(itf, &gain);
+    if (rc) {
+        goto err;
+    }
+
+    rc = tsl2591_write8(itf, TSL2591_COMMAND_BIT | TSL2591_REGISTER_CONTROL,
+                        int_time | gain);
+    if (rc) {
+        goto err;
+    }
+
+    #if MYNEWT_VAL(TSL2591_ITIME_DELAY)
+    /* Set the intergration time delay value in ms (+8% margin of error) */
+    g_tsl2591_itime_delay_ms = (int_time + 1) * 108;
+    #endif
+
+    /* Increment the timing changed counter */
+    STATS_INC(g_tsl2591stats, timing_changed);
+
+    return 0;
+err:
+    return rc;
+}
+
+int
+tsl2591_get_integration_time(struct sensor_itf *itf, uint8_t *itime)
+{
+    int rc;
+    uint8_t reg;
+
+    rc = tsl2591_read8(itf, TSL2591_COMMAND_BIT | TSL2591_REGISTER_CONTROL,
+                       &reg);
+    if (rc) {
+        goto err;
+    }
+
+    *itime = reg & 0x07;
+
+    return 0;
+err:
+    return rc;
+}
+
+int
+tsl2591_set_gain(struct sensor_itf *itf, uint8_t gain)
+{
+    int rc;
+    uint8_t int_time;
+
+    if ((gain != TSL2591_LIGHT_GAIN_LOW)
+        && (gain != TSL2591_LIGHT_GAIN_MED)
+        && (gain != TSL2591_LIGHT_GAIN_HIGH)
+        && (gain != TSL2591_LIGHT_GAIN_MAX)) {
+            TSL2591_LOG(ERROR, "Invalid gain value\n");
+            rc = SYS_EINVAL;
+            goto err;
+    }
+
+    rc = tsl2591_get_integration_time(itf, &int_time);
+    if (rc) {
+        goto err;
+    }
+
+    rc = tsl2591_write8(itf, TSL2591_COMMAND_BIT | TSL2591_REGISTER_CONTROL,
+                        int_time | gain);
+    if (rc) {
+        goto err;
+    }
+
+    /* Increment the gain change counter */
+    STATS_INC(g_tsl2591stats, gain_changed);
+
+    return 0;
+err:
+    return rc;
+}
+
+int
+tsl2591_get_gain(struct sensor_itf *itf, uint8_t *gain)
+{
+    int rc;
+    uint8_t reg;
+
+    rc = tsl2591_read8(itf, TSL2591_COMMAND_BIT | TSL2591_REGISTER_CONTROL,
+                       &reg);
+    if (rc) {
+        goto err;
+    }
+
+    *gain = reg & 0x30;
+
+    return 0;
+err:
+    return rc;
+}
+
+int
+tsl2591_get_data_r(struct sensor_itf *itf, uint16_t *broadband, uint16_t *ir)
+{
+    int rc;
+
+    #if MYNEWT_VAL(TSL2591_ITIME_DELAY)
+    /* Insert a delay of integration time to ensure valid sample */
+    os_time_delay((OS_TICKS_PER_SEC * g_tsl2591_itime_delay_ms) / 1000);
+    #endif
+
+    /* CHAN0 must be read before CHAN1 */
+    /* See: https://forums.adafruit.com/viewtopic.php?f=19&t=124176 */
+    *broadband = *ir = 0;
+    rc = tsl2591_read16(itf, TSL2591_COMMAND_BIT |
+                        TSL2591_REGISTER_CHAN0_LOW, broadband);
+    if (rc) {
+        goto err;
+    }
+    rc = tsl2591_read16(itf, TSL2591_COMMAND_BIT |
+                        TSL2591_REGISTER_CHAN1_LOW, ir);
+    if (rc) {
+        goto err;
+    }
+
+    /* Increment the polling counter */
+    STATS_INC(g_tsl2591stats, polled);
+
+    return 0;
+err:
+    return rc;
+}
+
+int
+tsl2591_get_data(struct sensor_itf *itf, uint16_t *broadband, uint16_t *ir)
+{
+    int rc;
+    uint8_t itime;
+    uint16_t igain;
+    uint16_t maxval;
+
+    #if !(MYNEWT_VAL(TSL2591_AUTO_GAIN))
+      return tsl2591_get_data_r(itf, broadband, ir);
+    #endif
+
+    /* Use auto-gain algorithm for better range at the expensive of */
+    /* unpredictable conversion times */
+
+    /* Get the integration time to determine max raw value */
+    rc = tsl2591_get_integration_time(itf, &itime);
+    if (rc) {
+        goto err;
+    }
+    /* Max value for 100ms = 37888, otherwise 65535 */
+    maxval = itime ? 65535 : 37888;
+
+    /* Set gain to 1x for the baseline conversion */
+    rc = tsl2591_set_gain(itf, TSL2591_LIGHT_GAIN_LOW);
+    if (rc) {
+        goto err;
+    }
+
+    /* Get the baseline conversion values */
+    /* Note: double-read required to empty cached values with prev gain */
+    rc = tsl2591_get_data_r(itf, broadband, ir);
+    rc = tsl2591_get_data_r(itf, broadband, ir);
+    if (rc) {
+        goto err;
+    }
+
+    /* Determine the ideal gain setting */
+    igain = maxval / (*broadband > *ir ? *broadband : *ir);
+
+    /* Find the closest gain <= igain */
+    if (igain < 25) {
+        /* Gain 1x, return the current sample */
+        return 0;
+    }
+    else if (igain < 428) {
+        /* Set gain to ~25x */
+        rc = tsl2591_set_gain(itf, TSL2591_LIGHT_GAIN_MED);
+        if (rc) {
+            goto err;
+        }
+    }
+    else if (igain < 9876) {
+        /* Set gain to ~428x */
+        rc = tsl2591_set_gain(itf, TSL2591_LIGHT_GAIN_HIGH);
+        if (rc) {
+            goto err;
+        }
+    }
+    else {
+        /* Set gain to ~9876x */
+        rc = tsl2591_set_gain(itf, TSL2591_LIGHT_GAIN_MAX);
+        if (rc) {
+            goto err;
+        }
+    }
+
+    /* Get a new data sample */
+    /* Note: double-read required to empty cached values with prev gain */
 
 Review comment:
   Good to have this comment explaining the double read.

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services