diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig index a7e7420038f..649ac13dcc4 100644 --- a/drivers/sensors/Kconfig +++ b/drivers/sensors/Kconfig @@ -145,6 +145,21 @@ config HCSR04_NPOLLWAITERS endif # SENSORS_HCSR04 +config SENSORS_ISL29023 + bool "Renesas ISL29023 ALS sensor" + default n + select I2C + ---help--- + Enable driver support for the Renesas ISL29023 ambient light sensor. + +if SENSORS_ISL29023 + +config ISL29023_I2C_FREQUENCY + int "ISL29023 I2C frequency" + default 400000 + +endif # SENSORS_ISL29023 + config SENSORS_HTS221 bool "STMicro HTS221 humidity sensor" default n diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs index 126d9872967..3d0c8a63b0c 100644 --- a/drivers/sensors/Make.defs +++ b/drivers/sensors/Make.defs @@ -121,6 +121,9 @@ ifeq ($(CONFIG_SENSORS_BMP280),y) CSRCS += bmp280.c endif +ifeq ($(CONFIG_SENSORS_ISL29023),y) + CSRCS += isl29023.c +endif ifeq ($(CONFIG_SENSORS_HTS221),y) CSRCS += hts221.c diff --git a/drivers/sensors/isl29023.c b/drivers/sensors/isl29023.c new file mode 100644 index 00000000000..d8b78de7fda --- /dev/null +++ b/drivers/sensors/isl29023.c @@ -0,0 +1,504 @@ +/**************************************************************************** + * drivers/sensors/isl29023.c + * + * Copyright (C) 2019 DataVision s.r.o. All rights reserved. + * Authors: Matous Pokorny + * + * 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 NuttX 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 AND CONTRIBUTORS + * "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 OWNER OR CONTRIBUTORS 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#if defined(CONFIG_I2C) && defined(CONFIG_SENSORS_ISL29023) + +/**************************************************************************** + * Pre-Processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_ISL29023_I2C_FREQUENCY +# define CONFIG_ISL29023_I2C_FREQUENCY 400000 +#endif + +/* Registers definitions */ + +#define ISL29023_COMMAND_1 0x00 +#define ISL29023_COMMAND_2 0x01 +#define ISL29023_DATA_LSB 0x02 +#define ISL29023_DATA_MSB 0x03 +#define ISL29023_INT_LT_LSB 0x04 +#define ISL29023_INT_LT_MSB 0x05 +#define ISL29023_INT_HT_LSB 0x06 +#define ISL29023_INT_HT_MSB 0x07 +#define ISL29023_TEST 0x08 + +/* Registers definitions */ + +#define ISL29023_RESOLUTION_MASK 0x3 +#define ISL29023_RESOLUTION_SHIFT 0x2 + +#define ISL29023_ALS_RANGE_MASK 0x3 +#define ISL29023_ALS_RANGE_SHIFT 0x0 + +#define ISL29023_OP_MODE_MASK 0x7 +#define ISL29023_OP_MODE_SHIFT 0x5 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct isl29023_dev_s +{ + FAR struct i2c_master_s *i2c; + uint8_t addr; /* Address on the I2C bus */ + uint8_t op_mode; /* Defined by isl29023_operational_mode_e */ + uint32_t resolution; /* Sensor ADC res. 16..65536 */ + uint32_t range; /* Sensor range 1000..64000 */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* I2C Helpers */ + +static int isl29023_i2c_write(FAR struct isl29023_dev_s *dev, + FAR const uint8_t *buffer, ssize_t buflen); +static int isl29023_i2c_read(FAR struct isl29023_dev_s *dev, + FAR uint8_t *buffer, ssize_t buflen); +static int isl29023_read_reg(FAR struct isl29023_dev_s *dev, + const uint8_t regaddr, uint8_t *buffer, size_t buflen); +static int isl29023_read_lux(FAR struct isl29023_dev_s *dev, + FAR struct isl29023_data_s *data); +static int isl29023_set_op_mode(FAR struct isl29023_dev_s *dev, uint8_t mode); +static int isl29023_set_resolution(FAR struct isl29023_dev_s *dev, + uint8_t res_mode); +static int isl29023_set_range(FAR struct isl29023_dev_s *dev, + uint8_t range_mode); + +/* Driver methods */ + +static int isl29023_open(FAR struct file *filep); +static int isl29023_close(FAR struct file *filep); +static ssize_t isl29023_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static ssize_t isl29023_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static int isl29023_ioctl(FAR struct file *filep, int cmd, unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_isl29023fops = +{ + isl29023_open, /* open */ + isl29023_close, /* close */ + isl29023_read, /* read */ + isl29023_write, /* write */ + NULL, /* seek */ + isl29023_ioctl, /* ioctl */ + NULL /* poll */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ +/**************************************************************************** + * Name: isl29023_i2c_write + * + * Description: + * Write to the I2C device. + * + ****************************************************************************/ + +static int isl29023_i2c_write(FAR struct isl29023_dev_s *dev, + FAR const uint8_t *buffer, ssize_t buflen) +{ + struct i2c_msg_s msg; + int ret; + + /* Setup for the transfer */ + + msg.frequency = CONFIG_ISL29023_I2C_FREQUENCY, + msg.addr = dev->addr; + msg.flags = 0; + msg.buffer = (FAR uint8_t *)buffer; /* Override const */ + msg.length = buflen; + + /* Then perform the transfer. */ + + ret = I2C_TRANSFER(dev->i2c, &msg, 1); + return (ret >= 0) ? OK : ret; +} + +/**************************************************************************** + * Name: isl29023_i2c_read + * + * Description: + * Read from the I2C device. + * + ****************************************************************************/ + +static int isl29023_i2c_read(FAR struct isl29023_dev_s *dev, + FAR uint8_t *buffer, ssize_t buflen) +{ + struct i2c_msg_s msg; + int ret; + + /* Setup for the transfer */ + + msg.frequency = CONFIG_LM75_I2C_FREQUENCY, + msg.addr = dev->addr, + msg.flags = I2C_M_READ; + msg.buffer = buffer; + msg.length = buflen; + + /* Then perform the transfer. */ + + ret = I2C_TRANSFER(dev->i2c, &msg, 1); + return (ret >= 0) ? OK : ret; +} + +/**************************************************************************** + * Name: isl29023_read_reg + * + * Description: + * Read register from the I2C device. + * + ****************************************************************************/ + +static int isl29023_read_reg(FAR struct isl29023_dev_s *dev, + const uint8_t regaddr, uint8_t *buffer, size_t buflen) +{ + int ret; + + ret = isl29023_i2c_write(dev, ®addr, 1); + if (ret < 0) + { + snerr("ERROR: i2c write failed: %d\n", ret); + return ret; + } + + ret = isl29023_i2c_read(dev, buffer, buflen); + if (ret < 0) + { + snerr("ERROR: i2c read failed: %d\n", ret); + return ret; + } + + return ret; +} + +/**************************************************************************** + * Name: isl29023_open + * + * Description: + * This function is called whenever the ISL29023 device is opened. + * + ****************************************************************************/ + +static int isl29023_open(FAR struct file *filep) +{ + return OK; +} + +/**************************************************************************** + * Name: isl29023_close + * + * Description: + * This routine is called when the ISL29023 device is closed. + * + ****************************************************************************/ + +static int isl29023_close(FAR struct file *filep) +{ + return OK; +} + +/**************************************************************************** + * Name: isl29023_read + ****************************************************************************/ + +static ssize_t isl29023_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct isl29023_dev_s *priv = inode->i_private; + int ret; + struct isl29023_data_s data; + + ret = isl29023_read_lux(priv, &data); + if (ret < 0) + { + snerr("ERROR: failed to read the sensor: %d\n", ret); + return (ssize_t)ret; + } + else + { + if (buflen < sizeof(data)) + { + return -EIO; + } + + memcpy(buffer, &data, sizeof(data)); + } + + return buflen; +} + +/**************************************************************************** + * Name: isl29023_write + ****************************************************************************/ + +static ssize_t isl29023_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) +{ + return -ENOSYS; +} + +/**************************************************************************** + * Name: isl29023_read_lux + ****************************************************************************/ + +static int isl29023_read_lux(FAR struct isl29023_dev_s *dev, + FAR struct isl29023_data_s *data) +{ + int ret; + uint8_t buffer[2]; + + ret = isl29023_read_reg(dev, ISL29023_DATA_LSB, buffer, 2); + if (ret < 0) + { + return ret; + } + + data->raw = (buffer[1] << 8) | buffer[0]; + + add_sensor_randomness(data->raw); + + uint32_t tmp = (data->raw * dev->range) / dev->resolution; + data->lux = (uint16_t)tmp; + + sninfo("raw value %8x, lux: %5u\n", data->raw, data->lux); + + return OK; +} + +/**************************************************************************** + * Name: isl29023_set_op_mode + ****************************************************************************/ + +static int isl29023_set_op_mode(FAR struct isl29023_dev_s *dev, uint8_t mode) +{ + uint8_t buffer[2]; + int ret; + + ret = isl29023_read_reg(dev, ISL29023_COMMAND_1, &buffer[1], 1); + if (ret < 0) + { + snerr("ERROR: i2c read reg failed: %d\n", ret); + return ret; + } + + /* Clear the mode bits */ + + buffer[1] &= ~(ISL29023_OP_MODE_MASK << ISL29023_OP_MODE_SHIFT); + mode &= ISL29023_OP_MODE_MASK; + + /* Modify mode bits */ + + buffer[1] |= mode << ISL29023_OP_MODE_SHIFT; + buffer[0] = ISL29023_COMMAND_1; + + dev->op_mode = mode; + sninfo("mode: %x\n", dev->mode); + + return isl29023_i2c_write(dev, buffer, 2); +} + +/**************************************************************************** + * Name: isl29023_set_resolution + ****************************************************************************/ + +static int isl29023_set_resolution(FAR struct isl29023_dev_s *dev, + uint8_t res_mode) +{ + uint8_t buffer[2]; + int ret; + + ret = isl29023_read_reg(dev, ISL29023_COMMAND_2, &buffer[1], 1); + if (ret < 0) + { + snerr("ERROR: i2c read reg failed: %d\n", ret); + return ret; + } + + /* Clear the mode bits */ + + buffer[1] &= ~(ISL29023_RESOLUTION_MASK << ISL29023_RESOLUTION_SHIFT); + res_mode &= ISL29023_RESOLUTION_MASK; + + /* Modify mode bits */ + + buffer[1] |= res_mode << ISL29023_RESOLUTION_SHIFT; + buffer[0] = ISL29023_COMMAND_2; + + dev->resolution = 1u << (16u - res_mode * 4u); + sninfo("resolution: %d\n", dev->resolution); + + return isl29023_i2c_write(dev, buffer, 2); +} + +/**************************************************************************** + * Name: isl29023_set_resolution + ****************************************************************************/ + +static int isl29023_set_range(FAR struct isl29023_dev_s *dev, + uint8_t range_mode) +{ + uint8_t buffer[2]; + int ret; + + ret = isl29023_read_reg(dev, ISL29023_COMMAND_2, &buffer[1], 1); + if (ret < 0) + { + snerr("ERROR: i2c read reg failed: %d\n", ret); + return ret; + } + + /* Clear the mode bits */ + + buffer[1] &= ~(ISL29023_ALS_RANGE_MASK << ISL29023_ALS_RANGE_SHIFT); + + /* Modify mode bits */ + + range_mode &= ISL29023_ALS_RANGE_MASK; + buffer[1] |= range_mode << ISL29023_ALS_RANGE_SHIFT; + buffer[0] = ISL29023_COMMAND_2; + + dev->range = 1000u * (1u << range_mode) * (1u << range_mode); + sninfo("range: %u\n", dev->range); + + return isl29023_i2c_write(dev, buffer, 2); +} + +/**************************************************************************** + * Name: isl29023_ioctl + ****************************************************************************/ + +static int isl29023_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct isl29023_dev_s *priv = inode->i_private; + int ret = OK; + + switch (cmd) + { + /* Write to the COMMAND1 register. Arg: uint8_t value */ + + case SNIOC_SET_OPERATIONAL_MODE: + ret = isl29023_set_op_mode(priv, (uint8_t)arg); + sninfo("Set operation mode %d with result %d\n", (uint8_t)arg, ret); + break; + + /* Write to the COMMAND2 register. Arg: uint8_t value */ + + case SNIOC_SET_RESOLUTION: + ret = isl29023_set_resolution(priv, (uint8_t)arg); + sninfo("Set resolution mode %d with result %d\n", (uint8_t)arg, ret); + break; + + /* Write to the COMMAND2 register. Arg: uint8_t value */ + + case SNIOC_SET_RANGE: + ret = isl29023_set_range(priv, (uint8_t)arg); + sninfo("Set range mode %d with result %d\n", (uint8_t)arg, ret); + break; + + default: + sninfo("Unrecognized cmd: 0x%04x\n", cmd); + ret = -ENOTTY; + break; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int isl29023_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, + uint8_t addr) +{ + FAR struct isl29023_dev_s *priv; + int ret; + + priv = (FAR struct isl29023_dev_s *)kmm_malloc(sizeof(struct isl29023_dev_s)); + if (priv == NULL) + { + snerr("ERROR: Failed to allocate instance\n"); + return -ENOMEM; + } + + priv->i2c = i2c; + priv->addr = addr; + priv->resolution = 0x10000; + priv->range = 64000; + priv->op_mode = ISL29023_OP_MODE_POWER_DOWN; + + /* Register the character driver */ + + ret = register_driver(devpath, &g_isl29023fops, 0666, priv); + if (ret < 0) + { + snerr("ERROR: Failed to register driver: %d\n", ret); + kmm_free(priv); + } + + return ret; +} + +#endif /* CONFIG_I2C && CONFIG_SENSORS_ISL29023 */ diff --git a/include/nuttx/sensors/ioctl.h b/include/nuttx/sensors/ioctl.h index 4a67a38477f..72979efee6e 100644 --- a/include/nuttx/sensors/ioctl.h +++ b/include/nuttx/sensors/ioctl.h @@ -221,4 +221,10 @@ #define SNIOC_CALIBRATE _SNIOC(0x0102) /* Arg: b16_t value */ #define SNIOC_TEMPUPDATE _SNIOC(0x0103) /* Arg: b16_t value */ +/* IOCTL commands unique to the ISL29023 */ + +#define SNIOC_SET_OPERATIONAL_MODE _SNIOC(0x0060) /* Arg: uint8_t value */ +#define SNIOC_SET_RESOLUTION _SNIOC(0x0062) /* Arg: uint8_t value */ +#define SNIOC_SET_RANGE _SNIOC(0x0064) /* Arg: uint8_t value */ + #endif /* __INCLUDE_NUTTX_SENSORS_IOCTL_H */ diff --git a/include/nuttx/sensors/isl29023.h b/include/nuttx/sensors/isl29023.h new file mode 100644 index 00000000000..fe3ebe9e214 --- /dev/null +++ b/include/nuttx/sensors/isl29023.h @@ -0,0 +1,126 @@ +/**************************************************************************** + * include/nuttx/sensors/isl29023.h + * + * Copyright (C) 2019 DataVision s.r.o. All rights reserved. + * Authors: Matous Pokorny + * + * 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 NuttX 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 AND CONTRIBUTORS + * "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 OWNER OR CONTRIBUTORS 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_SENSORS_ISL29023 +#define __INCLUDE_NUTTX_SENSORS_ISL29023 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#if defined(CONFIG_I2C) && defined(CONFIG_SENSORS_ISL29023) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct i2c_master_s; + +enum isl29023_resolution_e +{ + ISL29023_RESOLUTION_16BITS = 0x0, + ISL29023_RESOLUTION_12BITS = 0x1, + ISL29023_RESOLUTION_8BITS = 0x2, + ISL29023_RESOLUTION_4BITS = 0x3, +}; + +enum isl29023_als_range_e +{ + ISL29023_ALS_RANGE_1000 = 0x0, + ISL29023_ALS_RANGE_4000 = 0x1, + ISL29023_ALS_RANGE_16000 = 0x2, + ISL29023_ALS_RANGE_64000 = 0x3, +}; + +/* ISL2923 goes to power dowm mode after mode *once */ + +enum isl29023_operational_mode_e +{ + ISL29023_OP_MODE_POWER_DOWN = 0x0, + ISL29023_OP_MODE_ALS_ONCE = 0x1, + ISL29023_OP_MODE_IR_ONCE = 0x2, + ISL29023_OP_MODE_ALS_CONTINUES = 0x5, + ISL29023_OP_MODE_IR_CONTINUES = 0x6, +}; + +/* Data transfer structure */ + +struct isl29023_data_s +{ + uint16_t lux; /* Converted lux value */ + uint16_t raw; /* Raw unconverted value */ +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: isl29023_register + * + * Description: + * Register the ISL29023 ALS device as 'devpath' + * + * Input Parameters: + * devpath - The full path to the driver to register. E.g., "/dev/als0" + * i2c - An instance of the I2C interface to use to communicate with ALS + * addr - The I2C address of the ALS. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int isl29023_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, + uint8_t addr); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_I2C && CONFIG_SENSORS_ISL29023 */ +#endif /* __INCLUDE_NUTTX_SENSORS_ISL29023 */