diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index a6734daa54..298702986d 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -122,6 +122,10 @@ ifneq (,$(filter sht1%,$(USEMODULE))) USEMODULE += sht1x endif +ifneq (,$(filter si1133,$(USEMODULE))) + USEMODULE += si1133 +endif + ifneq (,$(filter si114%,$(USEMODULE))) USEMODULE += si114x endif diff --git a/drivers/include/si1133.h b/drivers/include/si1133.h new file mode 100644 index 0000000000..b8c311dbcd --- /dev/null +++ b/drivers/include/si1133.h @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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_si1133 Si1133 UV Index/Ambient Light Sensor with I2C + * @ingroup drivers_sensors + * @ingroup drivers_saul + * @brief Device driver for the Si1133 sensor + * + * The Si1133 is a UV Index Sensor and Ambient Light Sensor with I2C digital + * interface and programmable-event interrupt output. + * + * The I2C protocol implemented in this driver is most similar in registers and + * commands to the Si115x family, like the SI1153, however the Si1133 supports + * UV index while the Si115x doesn't. + * + * This driver provides @ref drivers_saul capabilities as well. + * @{ + * + * @file + * @brief Device driver interface for the Si1133 sensor + * + * @author iosabi + */ + +#ifndef SI1133_H +#define SI1133_H + +#include "periph/i2c.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Driver error return codes + */ +typedef enum { + SI1133_OK = 0, /**< No error. */ + SI1133_ERR_PARAMS = -1, /**< Invalid parameters. */ + SI1133_ERR_I2C = -2, /**< I2C communication error. */ + SI1133_ERR_LOGIC = -3, /**< Device communication logic error. */ + SI1133_ERR_NODEV = -4, /**< No SI1133 device detected. */ + SI1133_ERR_OVERFLOW = -5, /**< ADC overflow when sampling. */ +} si1133_ret_code_t; + +/** + * @brief Sensor (photodiode combination) in the Si1133 package. + */ +typedef enum { + SI1133_SENS_SMALL_IR = 1u << 0, + SI1133_SENS_MEDIUM_IR = 1u << 1, + SI1133_SENS_LARGE_IR = 1u << 2, + SI1133_SENS_WHITE = 1u << 3, + SI1133_SENS_LARGE_WHITE = 1u << 4, + SI1133_SENS_UV = 1u << 5, + SI1133_SENS_DEEP_UV = 1u << 6, +} si1133_sensor_t; + +/** + * @brief Channel configuration the Si1133 sensor. + * + * The sensor sampling in the Si1133 is done via Analog to Digital "channels" + * that read from a given sensor (photodiode combination) and output a numeric + * value. The A/D process has some configuration parameters that affect the + * acquisition time, the power consumption and the quality of the result. + * + * The A/D time is controlled by the internal 21 MHz clock. The sampling + * duration time is: + * + * (1 << decimation) * (1 << hw_gain) * 512 / 21000000 s + * + * where @p decimation is a number between 0 and 3 and @p hw_gain" is between 0 + * and 11. The shortest A/D sampling time is therefore 24.4 us while the longest + * is 400 ms. However, each sample is performed twice internally to cancel ADC + * offset and there are processing and sampling start times specified in the + * datasheet that increase the total sampling time. Increasing the sampling time + * doesn't make the output value be larger. + * + * The @p hw_gain and @p decimation parameters are configured from a single + * @p sample_time_log parameter in this struct, between 0 and 14, preferring the + * "normal" decimation when possible. + * + * An additional software sampling and averaging is possible by selecting a + * "sw_gain" value between 0 and 7. This will cause each A/D measurement to be + * repeated for a total of (1 << sw_gain) and accumulated in software in the + * 24-bit output. The output value will be affected by the sw_gain since it is + * a sum of samples and not an average. + */ +typedef struct { + uint8_t sample_time_log; /**< Log2 of sampling time, 0 to 14. */ + uint8_t sw_gain; /**< Software gain, 0 to 7. */ + si1133_sensor_t sensor; /**< Sensor to sample. */ +} si1133_channel_t; + +/** + * @brief Device initialization parameters + */ +typedef struct { + i2c_t i2c_dev; /**< I2C bus the sensor is connected to */ + /** + * @brief sensor address. + * Note: it is possible to change the a sensor's I2C address after it has + * been initialized to have multiple sensor on the same bus, but this is not + * supported by this driver. However, two different addresses can be + * selected by hardware. + */ + uint8_t address; +} si1133_params_t; + +/** + * @brief Device descriptor for the Si1133 sensor + */ +typedef struct { + /* Initialization parameters */ + i2c_t i2c_dev; /**< I2C bus the sensor is connected to */ + uint8_t address; /**< sensor address */ + /* Internal members */ + uint8_t cmd_counter; /**< Si1133 command counter */ + uint8_t num_channels; /**< Number of configured channels. */ + si1133_channel_t channel[6]; /**< Channel configuration. */ +} si1133_t; + +/** + * @brief Initialize the given Si1133 device + * + * @param[out] dev Initialized device descriptor of Si1133 device + * @param[in] params Initialization parameters + * + * @return A si1133_ret_code_t error or status code. + */ +si1133_ret_code_t si1133_init(si1133_t *dev, const si1133_params_t *params); + +/** + * @brief Configure the capture channels. + * + * The Si1133 has up to 6 "channels" that can be configured to capture from the + * different sensors (photodiode combinations). See @ref si1133_channel_t for + * a description of the channel configuration. + * + * @param[in] dev Device descriptor of Si1133 device to read from + * @param[in] channels Array of @p num_channels channel configuration. + * @param[in] num_channels Number of configured channel passed in @p channels. + * + * @return A si1133_ret_code_t error or status code. + */ +si1133_ret_code_t si1133_configure_channels(si1133_t *dev, + const si1133_channel_t *channels, + uint32_t num_channels); + +/** + * @brief Convenience function to configure all capture channels. + * + * This function is a convenience function to configure one channel per selected + * sensor in the @p sensor_mask, up to the maximum number of channels, setting + * all channels to force-mode only with the same parameters. This is equivalent + * to a call to @ref si1133_configure_channels with as many channels as bits + * set in the @p sensor_mask. + * + * The channels are configured in increasing order of the @ref si1133_sensor_t + * values. + * + * @param[in] dev Device descriptor of Si1133 device to read from + * @param[in] sensor_mask Combination of up to 6 si1133_sensor_t values. + * @param[in] sample_time_log Log2 of sampling time, 0 to 14. See @ref + * si1133_channel_t for details. + * @param[in] sw_gain Software gain, 0 to 7. See @ref si1133_channel_t + * for details. + */ +si1133_ret_code_t si1133_easy_configure(si1133_t *dev, + si1133_sensor_t sensor_mask, + uint8_t sample_time_log, + uint8_t sw_gain); + +/** + * @brief Perform a one-time blocking sample of the configured channels. + * + * Forces a one-time blocking sample of the sensors configured in the channels + * and returns the read values as signed 24-bit integers, sign extended to + * 32-bits. The number of channels sampled and configured sensor is set by the + * last call to @ref si1133_configure_channels, however after sampling all of + * them only up to the first @p num_channels values will be returned by this + * function. + * + * In case of ADC overflow, for example because there's too much light for the + * configured sensors the overflown sensor will read @p 0x7fffff and the + * function will return @ref SI1133_ERR_OVERFLOW. In case of overflow, try + * configuring a smaller sensor, for example @p SI1133_SENS_MEDIUM_IR instead of + * @p SI1133_SENS_LARGE_IR, or reduce the @p sw_gain for the given sensor. + * + * @param[in] dev Device descriptor of Si1133 device to read from + * @param[out] values Pointer to the output value buffer. + * @param[in] num_channels Maximum number of channel values to return. + * + * @return A si1133_ret_code_t error or status code. + */ +si1133_ret_code_t si1133_capture_sensors(si1133_t *dev, int32_t *values, + uint32_t num_channels); + +#ifdef __cplusplus +} +#endif + +#endif /* SI1133_H */ +/** @} */ diff --git a/drivers/saul/init_devs/auto_init_si1133.c b/drivers/saul/init_devs/auto_init_si1133.c new file mode 100644 index 0000000000..b26a48766c --- /dev/null +++ b/drivers/saul/init_devs/auto_init_si1133.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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 SI1133 driver. + * + * @author iosabi + * + * @} + */ + +#ifdef MODULE_SI1133 + +#include "assert.h" +#include "log.h" +#include "saul_reg.h" +#include "si1133.h" +#include "si1133_params.h" + +/** + * @brief Define the number of configured sensors + */ +#define SI1133_NUMOF ARRAY_SIZE(si1133_params) + +/** + * @brief Allocation of memory for device descriptors + */ +static si1133_t si1133_devs[SI1133_NUMOF]; + +/** + * @brief Memory for the SAUL registry entries + */ +static saul_reg_t saul_entries[SI1133_NUMOF * 4]; + +/** + * @brief Define the number of saul info + */ +#define SI1133_INFO_NUMOF ARRAY_SIZE(si1133_saul_reg_info) + +/** + * @name Reference the driver structs + * @{ + */ +extern const saul_driver_t si1133_uv_saul_driver; +extern const saul_driver_t si1133_ir_saul_driver; +extern const saul_driver_t si1133_visible_saul_driver; +/** @} */ + +void auto_init_si1133(void) +{ + assert(SI1133_INFO_NUMOF == SI1133_NUMOF); + unsigned entry = 0; + for (unsigned i = 0; i < SI1133_NUMOF; i++) { + LOG_DEBUG("[auto_init_saul] initializing SI1133 #%u\n", i); + + si1133_ret_code_t ret = si1133_init(&si1133_devs[i], &si1133_params[i]); + if (ret != SI1133_OK) { + LOG_ERROR("[auto_init_saul] error initializing SI1133 #%u: %d\n", + i, (int)ret); + continue; + } + + /* UV index */ + saul_entries[entry].dev = &si1133_devs[i]; + saul_entries[entry].name = si1133_saul_reg_info[i].name; + saul_entries[entry].driver = &si1133_uv_saul_driver; + saul_reg_add(&saul_entries[entry++]); + + /* Infra red */ + saul_entries[entry].dev = &si1133_devs[i]; + saul_entries[entry].name = si1133_saul_reg_info[i].name; + saul_entries[entry].driver = &si1133_ir_saul_driver; + saul_reg_add(&saul_entries[entry++]); + + /* Visible */ + saul_entries[entry].dev = &si1133_devs[i]; + saul_entries[entry].name = si1133_saul_reg_info[i].name; + saul_entries[entry].driver = &si1133_visible_saul_driver; + saul_reg_add(&saul_entries[entry]); + } +} + +#else +typedef int dont_be_pedantic; +#endif /* MODULE_SI1133 */ diff --git a/drivers/saul/init_devs/init.c b/drivers/saul/init_devs/init.c index dda33a68e8..da43e77da9 100644 --- a/drivers/saul/init_devs/init.c +++ b/drivers/saul/init_devs/init.c @@ -255,6 +255,10 @@ void saul_init_devs(void) extern void auto_init_sds011(void); auto_init_sds011(); } + if (IS_USED(MODULE_SI1133)) { + extern void auto_init_si1133(void); + auto_init_si1133(); + } if (IS_USED(MODULE_SI114X)) { extern void auto_init_si114x(void); auto_init_si114x(); diff --git a/drivers/si1133/Makefile b/drivers/si1133/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/si1133/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/si1133/Makefile.dep b/drivers/si1133/Makefile.dep new file mode 100644 index 0000000000..095389d9a5 --- /dev/null +++ b/drivers/si1133/Makefile.dep @@ -0,0 +1,2 @@ +USEMODULE += xtimer +FEATURES_REQUIRED += periph_i2c diff --git a/drivers/si1133/Makefile.include b/drivers/si1133/Makefile.include new file mode 100644 index 0000000000..3380d828dc --- /dev/null +++ b/drivers/si1133/Makefile.include @@ -0,0 +1,2 @@ +USEMODULE_INCLUDES_si1133 := $(LAST_MAKEFILEDIR)/include +USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_si1133) diff --git a/drivers/si1133/include/si1133_internals.h b/drivers/si1133/include/si1133_internals.h new file mode 100644 index 0000000000..8a0bb24f30 --- /dev/null +++ b/drivers/si1133/include/si1133_internals.h @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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_si1133 + * @brief Internal addresses, registers, constants for the Si1133 sensors + * family. + * @{ + * + * @file + * @brief Internal addresses, registers, constants for the Si1133 sensor. + * + * @author iosabi + */ + +#ifndef SI1133_INTERNALS_H +#define SI1133_INTERNALS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Si1133 I2C address + */ +#define SI1133_I2C_ADDRESS (0x52) /* or 0x55 */ + +/** + * @name Si1133 registers + * @{ + */ +#define SI1133_REG_PART_ID (0x00) +#define SI1133_REG_HW_ID (0x01) +#define SI1133_REG_REV_ID (0x02) +#define SI1133_REG_INFO0 (0x03) +#define SI1133_REG_INFO1 (0x04) +#define SI1133_REG_HOSTIN0 (0x0a) +#define SI1133_REG_COMMAND (0x0b) +#define SI1133_REG_IRQENABLE (0x0f) /* Also RESET in the datasheet.*/ +#define SI1133_REG_RESPONSE1 (0x10) +#define SI1133_REG_RESPONSE0 (0x11) +#define SI1133_REG_IRQ_STATUS (0x12) +#define SI1133_REG_HOSTOUTx (0x13) /* Ranges from 0x13 to 0x2c */ +/** @} */ + +/** + * @name Si1133 commands + * @{ + */ +#define SI1133_CMD_RESET_CMD_CTR (0x00) +#define SI1133_CMD_RESET_SW (0x01) +#define SI1133_CMD_FORCE (0x11) +#define SI1133_CMD_PAUSE (0x12) +#define SI1133_CMD_START (0x13) +#define SI1133_CMD_PARAM_QUERY (0x40) /* Add to SI1133_PARAM_* */ +#define SI1133_CMD_PARAM_SET (0x80) /* Add to SI1133_PARAM_* */ +/** @} */ + +/** + * @brief Si1133 channel parameters. + * + * These parameters define how a "channel" is sampled, ADC settings, resolution + * timing, etc. These four register values define a single channel. This is a + * convenience struct to handle them together in the same order as they appear + * in the parameter list below. + */ +typedef struct __attribute__((packed)) _si1133_channel_params { + uint8_t adcconfig; /**< ADCCONFIGx register. */ + uint8_t adcsens; /**< ADCSENSx register. */ + uint8_t adcpost; /**< ADCPOSTx register. */ + uint8_t measconfig; /**< MEASCONFIGx register. */ +} si1133_channel_params_t; + +/** + * @name Si1133 parameters + * + * @note These parameters are not accessible directly from the I2C registers. + * Instead, to access these parameters SI1133_CMD_PARAM_QUERY and + * SI1133_CMD_PARAM_SET commands should be used. + * @{ + */ +#define SI1133_PARAM_I2C_ADDR (0x00) +#define SI1133_PARAM_CHAN_LIST (0x01) +#define SI1133_PARAM_ADCCONFIG0 (0x02) +#define SI1133_PARAM_ADCSENS0 (0x03) +#define SI1133_PARAM_ADCPOST0 (0x04) +#define SI1133_PARAM_MEASCONFIG0 (0x05) +#define SI1133_PARAM_ADCCONFIG1 (0x06) +#define SI1133_PARAM_ADCSENS1 (0x07) +#define SI1133_PARAM_ADCPOST1 (0x08) +#define SI1133_PARAM_MEASCONFIG1 (0x09) +#define SI1133_PARAM_ADCCONFIG2 (0x0a) +#define SI1133_PARAM_ADCSENS2 (0x0b) +#define SI1133_PARAM_ADCPOST2 (0x0c) +#define SI1133_PARAM_MEASCONFIG2 (0x0d) +#define SI1133_PARAM_ADCCONFIG3 (0x0e) +#define SI1133_PARAM_ADCSENS3 (0x0f) +#define SI1133_PARAM_ADCPOST3 (0x10) +#define SI1133_PARAM_MEASCONFIG3 (0x11) +#define SI1133_PARAM_ADCCONFIG4 (0x12) +#define SI1133_PARAM_ADCSENS4 (0x13) +#define SI1133_PARAM_ADCPOST4 (0x14) +#define SI1133_PARAM_MEASCONFIG4 (0x15) +#define SI1133_PARAM_ADCCONFIG5 (0x16) +#define SI1133_PARAM_ADCSENS5 (0x17) +#define SI1133_PARAM_ADCPOST5 (0x18) +#define SI1133_PARAM_MEASCONFIG5 (0x19) +#define SI1133_PARAM_MEASRATE_H (0x1a) +#define SI1133_PARAM_MEASRATE_L (0x1b) +#define SI1133_PARAM_MEASCOUNT0 (0x1c) +#define SI1133_PARAM_MEASCOUNT1 (0x1d) +#define SI1133_PARAM_MEASCOUNT2 (0x1e) +#define SI1133_PARAM_THRESHOLD0_H (0x25) +#define SI1133_PARAM_THRESHOLD0_L (0x26) +#define SI1133_PARAM_THRESHOLD1_H (0x27) +#define SI1133_PARAM_THRESHOLD1_L (0x28) +#define SI1133_PARAM_THRESHOLD2_H (0x29) +#define SI1133_PARAM_THRESHOLD2_L (0x2a) +#define SI1133_PARAM_BURST (0x2b) +/** @} */ + +/** + * @name Si1133 RESPONSE0 register constants + * @{ + */ +#define SI1133_RESP0_COUNTER_MASK (0x0f) +#define SI1133_RESP0_CMD_ERR_MASK (0x10) +#define SI1133_RESP0_SLEEP_MASK (0x20) +#define SI1133_RESP0_SUSPEND_MASK (0x40) +#define SI1133_RESP0_RUNNING_MASK (0x80) + +/* Possible error values if SI1133_RESP0_CMD_ERR_MASK is set. */ +#define SI1133_RESP0_ERR_INVALID_COMMAND (0x01) +#define SI1133_RESP0_ERR_INVALID_PARAM_ADDR (0x80) +#define SI1133_RESP0_ERR_ADC_OVERFLOW (0x88) +#define SI1133_RESP0_ERR_BUFFER_OVERFLOW (0x89) +/** @} */ + +/** + * @name Si1133 Channel configuration constants + * @{ + */ +#define SI1133_ADCCONFIG_DECIM_RATE_MASK (0x60) +#define SI1133_ADCCONFIG_DECIM_RATE_SHIFT (5u) +#define SI1133_ADCCONFIG_ADCMUX_MASK (0x1f) +#define SI1133_ADCCONFIG_ADCMUX_SHIFT (0u) + +#define SI1133_ADCSENS_HSIG_MASK (0x80) +#define SI1133_ADCSENS_SW_GAIN_MASK (0x70) +#define SI1133_ADCSENS_SW_GAIN_SHIFT (4u) +#define SI1133_ADCSENS_HW_GAIN_MASK (0x0f) +#define SI1133_ADCSENS_HW_GAIN_SHIFT (0u) + +#define SI1133_ADCPOST_24BIT_OUT_MASK (0x40) +#define SI1133_ADCPOST_POSTSHIFT_MASK (0x38) +#define SI1133_ADCPOST_POSTSHIFT_SHIFT (3u) +#define SI1133_ADCPOST_THRESH_SEL_MASK (0x03) +#define SI1133_ADCPOST_THRESH_SEL_SHIFT (0u) + +#define SI1133_MEASCONFIG_COUNTER_IDX_MASK (0xc0) +#define SI1133_MEASCONFIG_COUNTER_IDX_SHIFT (6u) +/** @} */ + +/** + * @name Si1133 photodiode selection values for ADCMUX field. + * @{ + */ +#define SI1133_ADCMUX_SMALL_IR (0u) +#define SI1133_ADCMUX_MEDIUM_IR (1u) +#define SI1133_ADCMUX_LARGE_IR (2u) +#define SI1133_ADCMUX_WHITE (11u) +#define SI1133_ADCMUX_LARGE_WHITE (13u) +#define SI1133_ADCMUX_UV (24u) +#define SI1133_ADCMUX_DEEP_UV (25u) +/** @} */ + +/** + * @name Si1133 constants + * @{ + */ +#define SI1133_ID (0x33) +#define SI1133_STARTUP_TIME_MS (25u) +#define SI1133_NUM_CHANNELS (6u) +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* SI1133_INTERNALS_H */ +/** @} */ diff --git a/drivers/si1133/include/si1133_params.h b/drivers/si1133/include/si1133_params.h new file mode 100644 index 0000000000..6018052908 --- /dev/null +++ b/drivers/si1133/include/si1133_params.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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_si1133 + * + * @{ + * @file + * @brief Default configuration for SI1133 + * + * @author iosabi + */ + +#ifndef SI1133_PARAMS_H +#define SI1133_PARAMS_H + +#include "board.h" +#include "si1133.h" +#include "saul_reg.h" +#include "si1133_internals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Set default configuration parameters for the Si1133 + * @{ + */ +#ifndef SI1133_PARAM_I2C_DEV +#define SI1133_PARAM_I2C_DEV I2C_DEV(0) +#endif + +#ifndef SI1133_PARAM_ADDR +#define SI1133_PARAM_ADDR SI1133_I2C_ADDRESS +#endif + +#ifndef SI1133_PARAMS +#define SI1133_PARAMS { .i2c_dev = SI1133_PARAM_I2C_DEV, \ + .address = SI1133_PARAM_ADDR } +#endif + +#ifndef SI1133_SAUL_INFO +#define SI1133_SAUL_INFO { .name = "si1133" } +#endif +/**@}*/ + +/** + * @brief Configure Si1133 + */ +static const si1133_params_t si1133_params[] = +{ + SI1133_PARAMS +}; + +/** + * @brief Allocate and configure entries to the SAUL registry + */ +saul_reg_t si1133_saul_reg_info[] = +{ + SI1133_SAUL_INFO +}; + +#ifdef __cplusplus +} +#endif + +#endif /* SI1133_PARAMS_H */ +/** @} */ diff --git a/drivers/si1133/si1133.c b/drivers/si1133/si1133.c new file mode 100644 index 0000000000..5195943f96 --- /dev/null +++ b/drivers/si1133/si1133.c @@ -0,0 +1,467 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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_si1133 + * @{ + * + * @file + * @brief Device driver implementation for the SI1133 UV/IR/Ambient light + * sensor with I2C interface. + * + * @author iosabi + * + * @} + */ + +#include +#include +#include +#include + +#include "xtimer.h" + +#include "periph/i2c.h" + +#include "si1133.h" +#include "si1133_internals.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +/** + * @brief The command execution timeout in blocking mode. + * + * When forcing a measurement (SI1133_CMD_FORCE) the command is not done until + * all the measurements from all the channels are taken. This may take some time + * depending on the configuration. After the expected sampling time is over we + * wait up to this command timeout value for the command to be ready. + */ +#define SI1133_COMMAND_TIMEOUT_USEC 10000 + +/** + * @brief Return the expected sampling time in us for a FORCE command. + */ +static uint32_t _si1133_force_time_us(si1133_t *dev) +{ + /* Sample time measured in 512 clocks of the 21 MHz, or about 24.4 us. */ + uint32_t sample_time = 0; + + for (uint32_t i = 0; i < dev->num_channels; i++) { + /* A forced sample consists of (1 << sw_gain) measurements where each + * one has a total 155 us (about 7 * 24.4 us) of processing time, plus + * two ADC samples. Each ADC sample requires a t_adstart time (48.8 us + * or 2 * 24.4 us) plus the time configured by the sample_time_log. */ + sample_time += (2 * (2 + (1u << dev->channel[i].sample_time_log)) + 7) + << dev->channel[i].sw_gain; + } + /* The resulting sample_time value in microseconds is sample_time * 512 / 21 + * the sample_time is already a 24-bit number so we split the logic to + * fit in 32-bit arithmetic using the fact that 512 == 24 * 21 + 8. */ + return sample_time * 24 + sample_time * 8 / 21; +} + +/** + * @brief Run a command with no arguments. + * + * For commands CMD_PARAM_SET or CMD_PARAM_QUERY use @ref _si1133_set_param + * and @ref _si1133_get_param respectively instead. + */ +static int _si1133_run_command(si1133_t *dev, uint8_t command) +{ + DEBUG("[si1133] run_command: 0x%.2x, cmd_counter=%d\n", + (unsigned)command, dev->cmd_counter); + int ret; + ret = i2c_write_reg(dev->i2c_dev, dev->address, + SI1133_REG_COMMAND, command, 0 /* flags */); + if (ret) { + DEBUG("[si1133] write command I2C error: %s\n", strerror(-ret)); + return SI1133_ERR_I2C; + } + + if (command == SI1133_CMD_FORCE) { + /* Wait for the expected force acquisition time. */ + xtimer_usleep(_si1133_force_time_us(dev)); + } + + if (command == SI1133_CMD_RESET_SW) { + /* Reset command sets the command counter of 0x0f. */ + dev->cmd_counter = 0x0f; + /* Reset command puts the device in "Initialization Mode" which requires + * us to wait until the device is ready. */ + xtimer_msleep(SI1133_STARTUP_TIME_MS); + } + else if (command == SI1133_CMD_RESET_CMD_CTR) { + /* The reset cmd_counter command, well, resets it to 0. */ + dev->cmd_counter = 0; + } + else { + /* Increment the expected 4-bit command counter value. */ + dev->cmd_counter = (dev->cmd_counter + 1) & SI1133_RESP0_COUNTER_MASK; + } + + uint8_t new_cmd_ctr; + xtimer_ticks32_t start_time; + bool retry = false; + while (1) { + ret = i2c_read_reg(dev->i2c_dev, dev->address, SI1133_REG_RESPONSE0, + &new_cmd_ctr, 0 /* flags */); + if (ret) { + DEBUG("[si1133] read RESPONSE0 I2C error: %s\n", strerror(-ret)); + return SI1133_ERR_I2C; + } + if (new_cmd_ctr & SI1133_RESP0_CMD_ERR_MASK) { + DEBUG("[si1133] Command 0x%.2x returned error %d\n", + (unsigned)command, new_cmd_ctr & SI1133_RESP0_COUNTER_MASK); + /* Error code 2 is "ADC or accumulation overflow", while error code + * 3 is output buffer overflow which can only occur in BURST mode. + * However, in FORCE mode after the first overflow we can get an + * error code 3 if we change the settings in between the overflow + * and a new FORCE command, probably due to a silicon bug when + * handling sw_gain overflows since it is not possible to write more + * than 18 bytes of output in FORCE mode while the output buffer is + * 26 bytes long. */ + if ((new_cmd_ctr & SI1133_RESP0_COUNTER_MASK) >= 2) { + return SI1133_ERR_OVERFLOW; + } + return SI1133_ERR_LOGIC; + } + /* The reset command is done when the RUNNING flag is clear, the other + * commands are done when the command value is set to the expected + * one. */ + if ((command == SI1133_CMD_RESET_SW) + ? !(new_cmd_ctr & SI1133_RESP0_RUNNING_MASK) + : (dev->cmd_counter == + (new_cmd_ctr & SI1133_RESP0_COUNTER_MASK))) { + break; + } + /* The command didn't yet finish in this case so it should be in running + * state and we need to retry the loop with a timeout. This avoids + * calling xtimer for commands that are immediate. */ + if (retry) { + if (xtimer_usec_from_ticks(xtimer_diff(xtimer_now(), start_time)) + > SI1133_COMMAND_TIMEOUT_USEC) { + DEBUG("[si1133] Command 0x%.2x timeout.\n", (unsigned)command); + return SI1133_ERR_LOGIC; + } + } + else { + retry = true; + start_time = xtimer_now(); + } + } + if (retry) { + DEBUG("[si1133] Command overtime: %" PRIu32 " us.\n", + xtimer_usec_from_ticks(xtimer_diff(xtimer_now(), start_time))); + } + return SI1133_OK; +} + +static int _si1133_set_param(si1133_t *dev, uint8_t param, uint8_t value) +{ + int ret; + + ret = i2c_write_reg(dev->i2c_dev, dev->address, + SI1133_REG_HOSTIN0, value, 0 /* flags */); + if (ret) { + DEBUG("[si1133] write HOSTIN0 I2C error: %s\n", strerror(-ret)); + return SI1133_ERR_I2C; + } + ret = _si1133_run_command(dev, param | SI1133_CMD_PARAM_SET); + if (ret) { + return ret; + } + + uint8_t resp1; + ret = i2c_read_reg(dev->i2c_dev, dev->address, + SI1133_REG_RESPONSE1, &resp1, 0); + if (ret) { + DEBUG("[si1133] read RESPONSE1 I2C error: %s\n", strerror(-ret)); + return SI1133_ERR_I2C; + } + if (resp1 != value) { + DEBUG("[si1133] Expected to read back value 0x%.2" PRIu8 + " when setting param 0x%.2" PRIu8 " but got 0x%.2" PRIu8 "\n", + value, param, resp1); + return SI1133_ERR_LOGIC; + } + return SI1133_OK; +} + +/** + * @brief Reset the device. + */ +static int _si1133_reset(si1133_t *dev) +{ + DEBUG("[si1133] reset()\n"); + dev->num_channels = 0; + for (uint32_t i = 0; i < SI1133_NUM_CHANNELS; i++) { + /* Initialize the config with invalid values to force the first config + * call to update it. */ + dev->channel[i].sw_gain = 0xff; + } + int ret = _si1133_run_command(dev, SI1133_CMD_RESET_SW); + if (ret) { + return ret; + } + + return _si1133_run_command(dev, SI1133_CMD_RESET_CMD_CTR); +} + +/** + * @brief Configure a single channel with the passed parameters. + */ +static si1133_ret_code_t _si1133_configure_channel( + si1133_t *dev, uint32_t channel_id, const si1133_channel_t *channel) +{ + if (channel->sample_time_log > 14 || channel->sw_gain > 7) { + return SI1133_ERR_PARAMS; + } + + if (!memcmp(channel, &dev->channel[channel_id], sizeof(si1133_channel_t))) { + /* Avoid the I2C roundtrip if the channel configuration didn't + * change. */ + return SI1133_OK; + } + + /* Generate the channel configuration. */ + si1133_channel_params_t config; +#define SI1133_SENS_CASE(x) \ + case SI1133_SENS_ ## x: \ + config.adcconfig = \ + SI1133_ADCMUX_ ## x << SI1133_ADCCONFIG_ADCMUX_SHIFT; \ + break; + + switch (channel->sensor) { + SI1133_SENS_CASE(SMALL_IR) + SI1133_SENS_CASE(MEDIUM_IR) + SI1133_SENS_CASE(LARGE_IR) + SI1133_SENS_CASE(WHITE) + SI1133_SENS_CASE(LARGE_WHITE) + SI1133_SENS_CASE(UV) + SI1133_SENS_CASE(DEEP_UV) + default: + return SI1133_ERR_PARAMS; + } + /* Use normal decimation (1) except in the extremes where we must use + * the other decimation values. */ + const uint8_t hw_gain = channel->sample_time_log == 0 + ? 0 + : (channel->sample_time_log <= 11 + ? channel->sample_time_log - 1 + : 11); + /* Select decimation. A value of "0" in this field is the "normal" + * decimation, which corresponds to 1 in our "decimation" equation. + * The values in this hardware field are offset by 3. */ + const uint8_t decimation = + (channel->sample_time_log - hw_gain + 3) & 3; + config.adcconfig |= decimation << SI1133_ADCCONFIG_DECIM_RATE_SHIFT; + /* HSIG = 0, SW_GAIN and HW_GAIN as configured. */ + config.adcsens = (hw_gain << SI1133_ADCSENS_HW_GAIN_SHIFT) | + (channel->sw_gain << SI1133_ADCSENS_SW_GAIN_SHIFT); + /* 24-bit output, no output shift, no threshold. */ + config.adcpost = SI1133_ADCPOST_24BIT_OUT_MASK; + /* No counter, this will only be used in Force mode. */ + config.measconfig = 0; + + DEBUG("[si1133] config: %.2x %.2x %.2x %.2x\n", + ((uint8_t *)&config)[0], ((uint8_t *)&config)[1], + ((uint8_t *)&config)[2], ((uint8_t *)&config)[3]); + + for (uint8_t i = 0; i < sizeof(config); i++) { + uint8_t param = SI1133_PARAM_ADCCONFIG0 + sizeof(config) * channel_id + + i; + si1133_ret_code_t ret = + _si1133_set_param(dev, param, ((uint8_t *)&config)[i]); + if (ret) { + return ret; + } + + } + memcpy(&(dev->channel[channel_id]), channel, sizeof(si1133_channel_t)); + return SI1133_OK; +} + +static int _si1133_read_values(si1133_t *dev, int32_t *values, + uint32_t num_channels) +{ + /* We can read all registers in a single I2C burst. */ + uint8_t data[3 * num_channels]; + + /* We only request 24-bit values from the device so they are all 3 byte + * long. */ + int ret = i2c_read_regs(dev->i2c_dev, dev->address, SI1133_REG_HOSTOUTx, + data, 3 * num_channels, 0 /* flags */); + + if (ret) { + return SI1133_ERR_I2C; + } + uint8_t *offset = data; + for (uint8_t i = 0; i < num_channels; i++) { + /* Sign-extend the first 8-bit value before shifting. */ + int32_t value = ((int32_t)(int8_t)*(offset++)) << 16; + value |= *(offset++) << 8u; + value |= *(offset++); + values[i] = value; + } + return SI1133_OK; +} + +si1133_ret_code_t si1133_init(si1133_t *dev, const si1133_params_t *params) +{ + dev->i2c_dev = params->i2c_dev; + dev->address = params->address; + + /* After leaving "Off Mode" the SI1133 enters an "Initialization Mode" for + * a period of time in which it can't be reached over I2C. After this time + * the device will be in Standby Mode. */ + xtimer_msleep(SI1133_STARTUP_TIME_MS); + + i2c_acquire(params->i2c_dev); + + /* check sensor ID */ + uint8_t partid = 0; + int ret = i2c_read_reg(params->i2c_dev, params->address, SI1133_REG_PART_ID, + &partid, 0); + if (ret != 0) { + DEBUG("[si1133] i2c communication error: %s.\n", strerror(-ret)); + i2c_release(params->i2c_dev); + return SI1133_ERR_I2C; + } + if (partid != SI1133_ID) { + DEBUG("[si1133] Invalid part id: 0x%.2u\n", (unsigned)partid); + i2c_release(params->i2c_dev); + return SI1133_ERR_NODEV; + } + +#if ENABLE_DEBUG + uint8_t rev_id, hw_id; + i2c_read_reg(params->i2c_dev, params->address, SI1133_REG_REV_ID, &rev_id, + 0 /* flags */); + i2c_read_reg(params->i2c_dev, params->address, SI1133_REG_HW_ID, &hw_id, + 0 /* flags */); + DEBUG("[si1133] impl code: %u, silicon HW rev: %u, rev: %u.%u\n", + hw_id & 0x1f, hw_id >> 5, rev_id >> 4, rev_id & 0x0f); +#endif /* ENABLE_DEBUG */ + + /* We don't know the state in which the device is at this point so we need + * to perform a reset, unfortunately this requires another start-up wait. */ + ret = _si1133_reset(dev); + if (ret) { + i2c_release(dev->i2c_dev); + return ret; + } + + i2c_release(dev->i2c_dev); + return SI1133_OK; +} + +si1133_ret_code_t si1133_configure_channels(si1133_t *dev, + const si1133_channel_t *channels, + uint32_t num_channels) +{ + DEBUG("[si1133] configure_channels(num=%" PRIu32 ")\n", num_channels); + if (num_channels > SI1133_NUM_CHANNELS) { + return SI1133_ERR_PARAMS; + } + + i2c_acquire(dev->i2c_dev); + si1133_ret_code_t ret; + for (uint32_t i = 0; i < num_channels; i++) { + ret = _si1133_configure_channel(dev, i, channels + i); + if (ret) { + dev->num_channels = 0; + i2c_release(dev->i2c_dev); + return ret; + } + } + /* CHAN_LIST is a bit mask of channels used. */ + ret = _si1133_set_param(dev, SI1133_PARAM_CHAN_LIST, + (1u << num_channels) - 1); + if (ret) { + num_channels = 0; + } + dev->num_channels = num_channels; + i2c_release(dev->i2c_dev); + DEBUG("[si1133] Sample Time %" PRIu32 " us\n", _si1133_force_time_us(dev)); + return ret; +} + +si1133_ret_code_t si1133_easy_configure(si1133_t *dev, + si1133_sensor_t sensor_mask, + uint8_t sample_time_log, + uint8_t sw_gain) +{ + DEBUG("[si1133] easy_configure(0x%.2x)\n", (unsigned)sensor_mask); + i2c_acquire(dev->i2c_dev); + + si1133_ret_code_t ret; + uint8_t num_channels = 0; + for (uint8_t mask = sensor_mask; mask; + num_channels++, mask = mask & (mask - 1)) { + if (num_channels >= SI1133_NUM_CHANNELS) { + dev->num_channels = 0; + i2c_release(dev->i2c_dev); + return SI1133_ERR_PARAMS; + } + + si1133_channel_t channel; + channel.sensor = mask ^ (mask & (mask - 1)); + channel.sample_time_log = sample_time_log; + channel.sw_gain = sw_gain; + ret = _si1133_configure_channel(dev, num_channels, &channel); + if (ret) { + dev->num_channels = 0; + i2c_release(dev->i2c_dev); + return ret; + } + } + + /* CHAN_LIST is a bit mask of channels used. */ + ret = _si1133_set_param(dev, SI1133_PARAM_CHAN_LIST, + (1u << num_channels) - 1); + if (ret) { + num_channels = 0; + } + dev->num_channels = num_channels; + i2c_release(dev->i2c_dev); + DEBUG("[si1133] Sample Time %" PRIu32 " us\n", _si1133_force_time_us(dev)); + return ret; +} + +si1133_ret_code_t si1133_capture_sensors(si1133_t *dev, int32_t *values, + uint32_t num_channels) +{ + if (!dev->num_channels) { + /* Must be configured before calling capture_sensors. */ + return SI1133_ERR_PARAMS; + } + + i2c_acquire(dev->i2c_dev); + + si1133_ret_code_t force_ret; + force_ret = _si1133_run_command(dev, SI1133_CMD_FORCE); + if (force_ret != SI1133_OK && force_ret != SI1133_ERR_OVERFLOW) { + i2c_release(dev->i2c_dev); + DEBUG("[si1133] force read command error: %d\n", force_ret); + return force_ret; + } + si1133_ret_code_t ret = SI1133_OK; + if (force_ret == SI1133_ERR_OVERFLOW) { + /* We need to reset the overflow condition with a RESET_CMD_CTR */ + ret = _si1133_run_command(dev, SI1133_CMD_RESET_CMD_CTR); + } + if (ret == SI1133_OK) { + ret = _si1133_read_values(dev, values, num_channels); + } + i2c_release(dev->i2c_dev); + /* If there was an error reading the I2C values then return that error, + * otherwise we want to return the CMD_FORCE return value because there + * could be an overflow non-fatal error to report. */ + return ret ? ret : force_ret; +} diff --git a/drivers/si1133/si1133_saul.c b/drivers/si1133/si1133_saul.c new file mode 100644 index 0000000000..2ef885893c --- /dev/null +++ b/drivers/si1133/si1133_saul.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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_si1133 + * @{ + * + * @file + * @brief SAUL adaption for SI1133 devices + * + * @author iosabi + * + * @} + */ + +#include "saul.h" +#include "si1133.h" + +static int read_uv(const void *dev, phydat_t *res) +{ + if (si1133_easy_configure((si1133_t *)dev, + SI1133_SENS_UV | SI1133_SENS_DEEP_UV, + /*sample_time_log=*/ 1, + /*sw_gain=*/ 0) != 0) { + return -ECANCELED; + } + + int32_t values[2]; + if (si1133_capture_sensors((si1133_t *)dev, values, + ARRAY_SIZE(values)) != 0) { + return -ECANCELED; + } + for (uint8_t i = 0; i < ARRAY_SIZE(values); i++) { + res->val[i] = values[i]; + } + res->unit = UNIT_NONE; /* UV index */ + res->scale = 0; + return ARRAY_SIZE(values); +} + +static int read_ir(const void *dev, phydat_t *res) +{ + if (si1133_easy_configure((si1133_t *)dev, + SI1133_SENS_SMALL_IR | SI1133_SENS_MEDIUM_IR | + SI1133_SENS_LARGE_IR, + /*sample_time_log=*/ 1, + /*sw_gain=*/ 0) != 0) { + return -ECANCELED; + } + + int32_t values[3]; + if (si1133_capture_sensors((si1133_t *)dev, values, + ARRAY_SIZE(values)) != 0) { + return -ECANCELED; + } + for (uint8_t i = 0; i < ARRAY_SIZE(values); i++) { + res->val[i] = values[i]; + } + res->unit = UNIT_LUX; + res->scale = 0; + return ARRAY_SIZE(values); +} + +static int read_white(const void *dev, phydat_t *res) +{ + if (si1133_easy_configure((si1133_t *)dev, + SI1133_SENS_WHITE | SI1133_SENS_LARGE_WHITE, + /*sample_time_log=*/ 1, + /*sw_gain=*/ 0) != 0) { + return -ECANCELED; + } + + int32_t values[2]; + if (si1133_capture_sensors((si1133_t *)dev, values, + ARRAY_SIZE(values)) != 0) { + return -ECANCELED; + } + for (uint8_t i = 0; i < ARRAY_SIZE(values); i++) { + res->val[i] = values[i]; + } + res->unit = UNIT_LUX; + res->scale = 0; + return ARRAY_SIZE(values); +} + +const saul_driver_t si1133_uv_saul_driver = { + .read = read_uv, + .write = saul_notsup, + .type = SAUL_SENSE_UV +}; + +const saul_driver_t si1133_ir_saul_driver = { + .read = read_ir, + .write = saul_notsup, + .type = SAUL_SENSE_LIGHT +}; + +const saul_driver_t si1133_visible_saul_driver = { + .read = read_white, + .write = saul_notsup, + .type = SAUL_SENSE_LIGHT +}; diff --git a/tests/driver_si1133/Makefile b/tests/driver_si1133/Makefile new file mode 100644 index 0000000000..e8ea27281b --- /dev/null +++ b/tests/driver_si1133/Makefile @@ -0,0 +1,7 @@ +include ../Makefile.tests_common + +# required modules +USEMODULE += si1133 +USEMODULE += xtimer + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_si1133/Makefile.ci b/tests/driver_si1133/Makefile.ci new file mode 100644 index 0000000000..02b0eb5c36 --- /dev/null +++ b/tests/driver_si1133/Makefile.ci @@ -0,0 +1,8 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-nano \ + arduino-uno \ + atmega328p \ + atmega32u4 \ + # diff --git a/tests/driver_si1133/README.md b/tests/driver_si1133/README.md new file mode 100644 index 0000000000..3c1184e932 --- /dev/null +++ b/tests/driver_si1133/README.md @@ -0,0 +1,15 @@ +# Si1133 driver test + +Test application for the Silicon Labs Si1133 I2C device. + +## Usage + +This test will initialize the Si1133 device defined in the `si1133_params.h` +header, which can be override by the board or in CLFAGS by setting the following +macros: + + * `SI1133_PARAM_I2C_DEV` the I2C device to use, by default `I2C_DEV(0)`. + * `SI1133_PARAM_ADDR` the I2C address of the Si1133, either 0x52 or 0x55. + +The automated test checks that the Si1133 responds and all sensor values can be +read in blocking mode. diff --git a/tests/driver_si1133/main.c b/tests/driver_si1133/main.c new file mode 100644 index 0000000000..931a783a8d --- /dev/null +++ b/tests/driver_si1133/main.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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 Si1133 UV, IR and visible light sensor. + * + * @author iosabi + * + * @} + */ + +#include +#include + +#include "si1133.h" +#include "si1133_params.h" +#include "xtimer.h" +#include "board.h" + +/* Helper macro to define _si1133_strerr */ +#define CASE_SI1133_ERROR_STRING(X) \ + case X: \ + return #X; + +static const char *_si1133_strerr(si1133_ret_code_t err) +{ + switch (err) { + CASE_SI1133_ERROR_STRING(SI1133_OK); + CASE_SI1133_ERROR_STRING(SI1133_ERR_PARAMS); + CASE_SI1133_ERROR_STRING(SI1133_ERR_I2C); + CASE_SI1133_ERROR_STRING(SI1133_ERR_LOGIC); + CASE_SI1133_ERROR_STRING(SI1133_ERR_NODEV); + CASE_SI1133_ERROR_STRING(SI1133_ERR_OVERFLOW); + } + return NULL; +} + +#define EXPECT_RET_CODE(expected, actual) \ + do { \ + si1133_ret_code_t actual_value = (actual); \ + si1133_ret_code_t expected_value = (expected); \ + if (actual_value != expected_value) { \ + printf( \ + "ERROR: " #actual " = %s\nExpected value " #expected " (%s)\n",\ + _si1133_strerr(actual_value), _si1133_strerr(expected_value)); \ + failures++; \ + } \ + } while (0) + +static si1133_t dev; + +int main(void) +{ + uint32_t failures = 0; + + puts("Testing Si1133 in blocking mode:"); + static const si1133_params_t blocking_params = { + .i2c_dev = SI1133_PARAM_I2C_DEV, + .address = SI1133_PARAM_ADDR + }; + EXPECT_RET_CODE(SI1133_OK, si1133_init(&dev, &blocking_params)); + + static const si1133_sensor_t sensor_list[] = { + SI1133_SENS_SMALL_IR, + SI1133_SENS_MEDIUM_IR, + SI1133_SENS_LARGE_IR, + SI1133_SENS_WHITE, + SI1133_SENS_LARGE_WHITE, + SI1133_SENS_UV, + SI1133_SENS_DEEP_UV, + }; + /* Test reading a sample one by one. */ + for (uint32_t i = 0; i < ARRAY_SIZE(sensor_list); i++) { + EXPECT_RET_CODE(SI1133_OK, + si1133_easy_configure(&dev, sensor_list[i], 0, 0)); + int32_t value; + EXPECT_RET_CODE(SI1133_OK, + si1133_capture_sensors(&dev, &value, 1)); + if (value >= 0x7fffff) { + printf("ERROR: Sensor sample overflow, got %" PRId32 "\n", value); + failures++; + } + printf(" - sensor 0x%.2x: %" PRId32 "\n", (int)sensor_list[i], value); + } + + /* Test increasing the sw_gain until we get an overflow. */ + for (uint32_t sw_gain = 0; sw_gain <= 7; sw_gain++) { + uint8_t sensor_mask = + SI1133_SENS_LARGE_IR | + SI1133_SENS_LARGE_WHITE | + SI1133_SENS_UV; + EXPECT_RET_CODE(SI1133_OK, + si1133_easy_configure(&dev, sensor_mask, 1, sw_gain)); + int32_t values[3]; + si1133_ret_code_t ret = + si1133_capture_sensors(&dev, values, ARRAY_SIZE(values)); + printf("INFO: sw_gain=%" PRIu32 " LARGE_IR=%10" PRId32 + " LARGE_WHITE=%10" PRId32 " UV=%10" PRId32 "\n", + sw_gain, values[0], values[1], values[2]); + if (ret == SI1133_OK) { + continue; + } + /* If we didn't get an OK we should have an overflow condition. */ + EXPECT_RET_CODE(SI1133_ERR_OVERFLOW, ret); + /* One of the values must be in overflow state. */ + bool overflowed = false; + for (uint32_t i = 0; i < ARRAY_SIZE(values); i++) { + overflowed = overflowed || values[i] == 0x7fffff; + } + if (!overflowed) { + printf( + "ERROR: Sensor overflow but no value in overflowed state.\n"); + for (uint32_t i = 0; i < ARRAY_SIZE(values); i++) { + printf(" values[%" PRIu32 "] = %" PRId32 "\n", i, values[i]); + } + failures++; + } + else { + printf("NOTE: Overflow test OK.\n"); + } + } + /* Reading any sensor after overflowing should not fail. */ + EXPECT_RET_CODE(SI1133_OK, + si1133_easy_configure(&dev, SI1133_SENS_SMALL_IR, 1, 0)); + int32_t value; + EXPECT_RET_CODE(SI1133_OK, + si1133_capture_sensors(&dev, &value, 1)); + + /* Test reading most sensors at once. The maximum is 6 sensors. */ + uint32_t all = 0; + for (uint32_t i = 0; i < ARRAY_SIZE(sensor_list); i++) { + all |= sensor_list[i]; + } + EXPECT_RET_CODE(SI1133_ERR_PARAMS, si1133_easy_configure(&dev, all, 1, 0)); + + /* All except one is lower than the limit of 6. */ + all &= ~SI1133_SENS_WHITE; + EXPECT_RET_CODE(SI1133_OK, si1133_easy_configure(&dev, all, 1, 0)); + int32_t values[6]; + EXPECT_RET_CODE(SI1133_OK, + si1133_capture_sensors(&dev, values, ARRAY_SIZE(values))); + + if (failures != 0) { + printf("Result: FAILED %" PRIu32 "\n", failures); + } + else { + puts("Result: OK\n"); + } + return 0; +} diff --git a/tests/driver_si1133/tests/01-run.py b/tests/driver_si1133/tests/01-run.py new file mode 100755 index 0000000000..4ef52c72e7 --- /dev/null +++ b/tests/driver_si1133/tests/01-run.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2020 iosabi +# +# 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. + +import sys +from testrunner import run + + +def testfunc(child): + child.expect_exact('Testing Si1133 in blocking mode:') + i = child.expect([r'.*Result: OK\s', r'.*Result: FAILED (\d+)\s']) + if i == 1: + print('FAILED') + return + print('SUCCESS') + + +if __name__ == "__main__": + sys.exit(run(testfunc))