| /**************************************************************************** |
| * drivers/sensors/isl29023.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| #include <sys/types.h> |
| #include <errno.h> |
| #include <debug.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <inttypes.h> |
| |
| #include <nuttx/fs/fs.h> |
| #include <nuttx/i2c/i2c_master.h> |
| #include <nuttx/kmalloc.h> |
| #include <nuttx/random.h> |
| |
| #include <nuttx/sensors/isl29023.h> |
| |
| #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 in counts (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 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 = |
| { |
| NULL, /* open */ |
| NULL, /* close */ |
| isl29023_read, /* read */ |
| isl29023_write, /* write */ |
| NULL, /* seek */ |
| isl29023_ioctl, /* ioctl */ |
| }; |
| |
| /**************************************************************************** |
| * 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_ISL29023_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_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->op_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: %" PRIu32 "\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: %" PRIu32 "\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 */ |