nuttx-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From gn...@apache.org
Subject [incubator-nuttx] 01/03: Added driver for the hdc1008 temperature/humidity sensor.
Date Tue, 05 May 2020 18:21:02 GMT
This is an automated email from the ASF dual-hosted git repository.

gnutt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit 8a0b2bc14e1f8562dea6987878073838a777aa65
Author: Pelle <iceaway@gmail.com>
AuthorDate: Mon May 4 21:16:07 2020 +0200

    Added driver for the hdc1008 temperature/humidity sensor.
---
 drivers/sensors/Kconfig         |   29 ++
 drivers/sensors/Make.defs       |    6 +
 drivers/sensors/hdc1008.c       | 1006 +++++++++++++++++++++++++++++++++++++++
 include/nuttx/sensors/hdc1008.h |  109 +++++
 4 files changed, 1150 insertions(+)

diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig
index f08951f..f58b810 100644
--- a/drivers/sensors/Kconfig
+++ b/drivers/sensors/Kconfig
@@ -852,4 +852,33 @@ config SENSORS_ADT7320
 	---help---
 		Enables support for the ADT7320 Driver
 
+config SENSORS_HDC1008
+	bool "TI HDC1008 temperature and humidity sensor"
+	default n
+	select I2C
+	---help---
+		Enable driver support for the TI HDC1008 temperature and humidity sensor.
+
+if SENSORS_HDC1008
+
+config HDC1008_I2C_ADDRESS
+  hex "HDC1008 I2C address"
+  default 0x40
+  range 0x40 0x43
+  ---help---
+    The I2C address of the HDC1008 sensor. It can be configured via straps to
+    a value between 0x40 and 0x43.
+
+config HDC1008_I2C_FREQUENCY
+	int "HDC1008 I2C frequency"
+	default 400000
+	range 1 400000
+
+config HDC1008_DEBUG
+	bool "Debug support for the HDC1008"
+	default n
+	---help---
+		Enables debug features for the HDC1008
+
+endif # SENSORS_HDC1008
 endif # SENSORS
diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs
index ae47934..dbba327 100644
--- a/drivers/sensors/Make.defs
+++ b/drivers/sensors/Make.defs
@@ -291,6 +291,12 @@ ifeq ($(CONFIG_SENSORS_ZEROCROSS),y)
   CSRCS += zerocross.c
 endif
 
+# TI HDC100X
+
+ifeq ($(CONFIG_SENSORS_HDC1008),y)
+  CSRCS += hdc1008.c
+endif
+
 # Include sensor driver build support
 
 DEPPATH += --dep-path sensors
diff --git a/drivers/sensors/hdc1008.c b/drivers/sensors/hdc1008.c
new file mode 100644
index 0000000..fd6ccb3
--- /dev/null
+++ b/drivers/sensors/hdc1008.c
@@ -0,0 +1,1006 @@
+/****************************************************************************
+ * drivers/sensors/hdc1008.c
+ * Driver for the TI HDC1008 temperature and humidity sensor
+ *
+ *   Copyright (C) 2020 Pelle Windestam. All rights reserved.
+ *   Author: Pelle Windestam <pelle@windestam.se>
+ *
+ * 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 <nuttx/config.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <debug.h>
+#include <time.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/sensors/hdc1008.h>
+#include <nuttx/random.h>
+
+#if defined(CONFIG_I2C) && defined(CONFIG_SENSORS_HDC1008)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifdef CONFIG_HDC1008_DEBUG
+#  define hdc1008_dbg(x, ...)    _info(x, ##__VA_ARGS__)
+#else
+#  define hdc1008_dbg(x, ...)    sninfo(x, ##__VA_ARGS__)
+#endif
+
+#ifndef CONFIG_SHT21_I2C_FREQUENCY
+#  define CONFIG_SHT21_I2C_FREQUENCY 400000
+#endif
+
+/* Macros to convert raw temperature and humidity to real values. Temperature
+ * is scaled by 100.
+ */
+
+#define RAW_TO_TEMP(x) (((x) * 16500 / 65536) - 4000)
+#define RAW_TO_RH(x)   ((x) * 1000 / 65536)
+
+/* Resolution for measurements. 8-bit is only valid for humidity. */
+
+#define CONFIGURATION_RES_14BIT  0x00
+#define CONFIGURATION_RES_11BIT  0x01
+#define CONFIGURATION_RES_8BIT   0x02
+
+/* HDC1008 registers */
+
+#define HDC1008_REG_TEMPERATURE     0x00
+#define HDC1008_REG_HUMIDITY        0x01
+#define HDC1008_REG_CONFIGURATION   0x02
+#define HDC1008_REG_SERIALID_0      0xFB /* Bits 0-15: Bit 24-39 of serial */
+#define HDC1008_REG_SERIALID_1      0xFC /* Bits 0-15: Bit 8-23 of serial */
+#define HDC1008_REG_SERIALID_2      0xFD /* Bits 7-15: Bit 0-7 of serial */
+
+/* Configuration register bits */
+
+#define HDC1008_CONFIGURATION_HRES_SHIFT      (8)       /* Bits 8-9: Humidity resolution
*/
+#define HDC1008_CONFIGURATION_HRES_MASK       (0x03 << HDC1008_CONFIGURATION_HRES_SHIFT)
+#  define HDC1008_CONFIGURATION_HRES_14BIT    (CONFIGURATION_RES_14BIT << HDC1008_CONFIGURATION_HRES_SHIFT)
+#  define HDC1008_CONFIGURATION_HRES_11BIT    (CONFIGURATION_RES_11BIT << HDC1008_CONFIGURATION_HRES_SHIFT)
+#  define HDC1008_CONFIGURATION_HRES_8BIT     (CONFIGURATION_RES_8BIT  << HDC1008_CONFIGURATION_HRES_SHIFT)
+#define HDC1008_CONFIGURATION_TRES_SHIFT      (10)      /* Bit 10: Temperature resolution
*/
+#define HDC1008_CONFIGURATION_TRES_MASK       (0x01 << HDC1008_CONFIGURATION_TRES_SHIFT)
+#  define HDC1008_CONFIGURATION_TRES_14BIT    (CONFIGURATION_RES_14BIT << HDC1008_CONFIGURATION_TRES_SHIFT)
+#  define HDC1008_CONFIGURATION_TRES_11BIT    (CONFIGURATION_RES_11BIT << HDC1008_CONFIGURATION_TRES_SHIFT)
+#define HDC1008_CONFIGURATION_BTST            (1 << 11) /* Bit 11: Battery status */
+#define HDC1008_CONFIGURATION_MODE            (1 << 12) /* Bit 12: Mode of aquisition
*/
+#define HDC1008_CONFIGURATION_HEAT_SHIFT      (13)      /* Bit 13: Heater */
+#define HDC1008_CONFIGURATION_HEAT_MASK       (0x01 << HDC1008_CONFIGURATION_HEAT_SHIFT)

+#  define HDC1008_CONFIGURATION_HEAT_DISABLE  (0x00 << HDC1008_CONFIGURATION_HEAT_SHIFT)

+#  define HDC1008_CONFIGURATION_HEAT_ENABLE   (0x01 << HDC1008_CONFIGURATION_HEAT_SHIFT)

+                                                        /* Bit 14: Reserved */
+#define HDC1008_CONFIGURATION_RST             (1 << 15) /* Bit 15: Software reset bit
*/
+
+/****************************************************************************
+ * Private
+ ****************************************************************************/
+
+struct hdc1008_dev_s
+{
+  FAR struct i2c_master_s *i2c; /* I2C interface */
+  uint8_t addr;                 /* I2C address */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  bool unlinked;                /* True, driver has been unlinked */
+#endif
+  uint8_t mode;                 /* Aquisition mode */
+  uint16_t configuration;       /* Configuration shadow register */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  int16_t crefs;                /* Number of open references */
+#endif
+  sem_t devsem;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Character driver methods */
+
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+static int     hdc1008_open(FAR struct file *filep);
+static int     hdc1008_close(FAR struct file *filep);
+#endif
+static ssize_t hdc1008_read(FAR struct file *filep, FAR char *buffer,
+                          size_t buflen);
+static ssize_t hdc1008_write(FAR struct file *filep, FAR const char *buffer,
+                           size_t buflen);
+static int     hdc1008_ioctl(FAR struct file *filep, int cmd,
+                           unsigned long arg);
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+static int     hdc1008_unlink(FAR struct inode *inode);
+#endif
+static int hdc1008_getreg(FAR struct hdc1008_dev_s *priv, uint8_t regaddr,
+                          FAR uint16_t *regval);
+static int hdc1008_putreg(FAR struct hdc1008_dev_s *priv, uint8_t regaddr,
+                          FAR uint16_t regval);
+static int hdc1008_reset(FAR struct hdc1008_dev_s *priv);
+static int hdc1008_measure_trh(FAR struct hdc1008_dev_s *priv, int *t,
+                               int *h);
+static int hdc1008_measure_current_mode(struct hdc1008_dev_s *priv,
+                                        struct hdc1008_conv_data_s *data);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_hdc1008fops =
+{
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  hdc1008_open,     /* open */
+  hdc1008_close,    /* close */
+#else
+  NULL,             /* open */
+  NULL,             /* close */
+#endif
+  hdc1008_read,     /* read */
+  hdc1008_write,    /* write */
+  NULL,             /* seek */
+  hdc1008_ioctl,    /* ioctl */
+  NULL              /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , hdc1008_unlink  /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: hdc1008_measure_trh
+ *
+ * Description:
+ *   Read both temperature and humidity from the sensor
+ *
+ ****************************************************************************/
+
+static int hdc1008_measure_trh(FAR struct hdc1008_dev_s *priv, int *t,
+                               int *h)
+{
+  struct i2c_config_s config;
+  uint8_t buf[4];
+  uint8_t reg = HDC1008_REG_TEMPERATURE;
+  int tmp;
+  int ret;
+
+  config.frequency = CONFIG_HDC1008_I2C_FREQUENCY;
+  config.address = priv->addr;
+  config.addrlen = 7;
+
+  ret = i2c_write(priv->i2c, &config, &reg, 1);
+
+  /* Wait for measurement to complete. Max should be about 20 ms if measuring
+   * both temperature and humidity.
+   */
+
+  nxsig_usleep(20000);
+
+  ret = i2c_read(priv->i2c, &config, buf, 4);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Convert raw data from sensor to temperature/humidity */
+
+  tmp = ((int)buf[0] << 8) | (int)buf[1];
+  tmp = RAW_TO_TEMP(tmp);
+  *t = tmp;
+
+  tmp = ((int)buf[2] << 8) | (int)buf[3];
+  tmp = RAW_TO_RH(tmp);
+  *h = tmp;
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: hdc1008_measure_t_or_rh
+ *
+ * Description:
+ *   Read both temperature and humidity from the sensor
+ *
+ ****************************************************************************/
+
+static int hdc1008_measure_t_or_rh(FAR struct hdc1008_dev_s *priv,
+                                   bool temperature, int *val)
+{
+  struct i2c_config_s config;
+  uint8_t buf[4];
+  uint8_t reg;
+  int tmp;
+  int ret;
+
+  config.frequency = CONFIG_HDC1008_I2C_FREQUENCY;
+  config.address = priv->addr;
+  config.addrlen = 7;
+
+  reg = temperature ? HDC1008_REG_TEMPERATURE : HDC1008_REG_HUMIDITY;
+
+  ret = i2c_write(priv->i2c, &config, &reg, 1);
+
+  /* Wait for measurement to complete. 10 ms wait should give enough
+   * margin for either temperature/humidity at maximum resolution.
+   */
+
+  nxsig_usleep(10000);
+
+  ret = i2c_read(priv->i2c, &config, buf, 2);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Convert raw data from sensor to temperature/humidity */
+
+  tmp = ((int)buf[0] << 8) | (int)buf[1];
+  tmp = temperature ? RAW_TO_TEMP(tmp) : RAW_TO_RH(tmp);
+  *val = tmp;
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: hdc1008_set_operational_mode
+ *
+ * Description:
+ *   Configure the HDC1008 for measuring humitidy, temperature or both.
+ *
+ ****************************************************************************/
+
+static int hdc1008_set_operational_mode(struct hdc1008_dev_s *priv,
+                                        uint8_t mode)
+{
+  int ret;
+  uint16_t reg;
+
+  ret = hdc1008_getreg(priv, HDC1008_REG_CONFIGURATION, &reg);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  switch (mode)
+    {
+      case HDC1008_MEAS_TEMPERATURE:
+        {
+          reg &= ~HDC1008_CONFIGURATION_MODE;
+          break;
+        }
+
+      case HDC1008_MEAS_HUMIDITY:
+        {
+          reg &= ~HDC1008_CONFIGURATION_MODE;
+          break;
+        }
+
+      case HDC1008_MEAS_T_AND_RH:
+        {
+          reg |= HDC1008_CONFIGURATION_MODE;
+        }
+
+      default:
+        return -EINVAL;
+    }
+
+  priv->mode = mode;
+
+  return hdc1008_putreg(priv, HDC1008_REG_CONFIGURATION, reg);
+}
+
+/****************************************************************************
+ * Name: hdc1008_set_temperature_resolution
+ *
+ * Description:
+ *   Configure the HDC1008 temperature resolution
+ *
+ ****************************************************************************/
+
+static int hdc1008_set_temperature_resolution(struct hdc1008_dev_s *priv,
+                                              uint8_t resolution)
+{
+  int ret;
+  uint16_t reg;
+
+  ret = hdc1008_getreg(priv, HDC1008_REG_CONFIGURATION, &reg);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  reg &= ~HDC1008_CONFIGURATION_TRES_MASK;
+  switch (resolution)
+    {
+      case 11:
+        {
+          reg |= HDC1008_CONFIGURATION_TRES_11BIT;
+          break;
+        }
+
+      case 14:
+        {
+          reg |= HDC1008_CONFIGURATION_TRES_14BIT;
+          break;
+        }
+
+      default:
+        return -EINVAL;
+    }
+
+  return hdc1008_putreg(priv, HDC1008_REG_CONFIGURATION, reg);
+}
+
+/****************************************************************************
+ * Name: hdc1008_set_humidity_resolution
+ *
+ * Description:
+ *   Configure the HDC1008 humidity resolution
+ *
+ ****************************************************************************/
+
+static int hdc1008_set_humidity_resolution(struct hdc1008_dev_s *priv,
+                                           uint8_t resolution)
+{
+  int ret;
+  uint16_t reg;
+
+  ret = hdc1008_getreg(priv, HDC1008_REG_CONFIGURATION, &reg);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  reg &= ~HDC1008_CONFIGURATION_HRES_MASK;
+  switch (resolution)
+    {
+      case 8:
+        {
+          reg |= HDC1008_CONFIGURATION_HRES_8BIT;
+          break;
+        }
+
+      case 11:
+        {
+          reg |= HDC1008_CONFIGURATION_HRES_11BIT;
+          break;
+        }
+
+      case 14:
+        {
+          reg |= HDC1008_CONFIGURATION_HRES_14BIT;
+          break;
+        }
+
+      default:
+        return -EINVAL;
+    }
+
+  return hdc1008_putreg(priv, HDC1008_REG_CONFIGURATION, reg);
+}
+
+/****************************************************************************
+ * Name: hdc1008_set_heater_mode
+ *
+ * Description:
+ *   Configure the HDC1008 heater mode
+ *
+ ****************************************************************************/
+
+static int hdc1008_set_heater_mode(struct hdc1008_dev_s *priv,
+                                   uint8_t mode)
+{
+  int ret;
+  uint16_t reg;
+
+  ret = hdc1008_getreg(priv, HDC1008_REG_CONFIGURATION, &reg);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  reg &= ~HDC1008_CONFIGURATION_HEAT_MASK;
+  if (mode == 0)
+    {
+      reg |= HDC1008_CONFIGURATION_HEAT_DISABLE;
+    }
+  else if (mode == 1)
+    {
+      reg |= HDC1008_CONFIGURATION_HEAT_ENABLE;
+    }
+  else
+    {
+      return -EINVAL;
+    }
+
+  return hdc1008_putreg(priv, HDC1008_REG_CONFIGURATION, reg);
+}
+
+/****************************************************************************
+ * Name: hdc1008_getreg
+ *
+ * Description:
+ *   Get value of 16-bit register
+ *
+ ****************************************************************************/
+
+static int hdc1008_getreg(FAR struct hdc1008_dev_s *priv, uint8_t regaddr,
+                          FAR uint16_t *regval)
+{
+  int ret;
+  struct i2c_config_s config;
+  uint8_t buf[2];
+
+  config.frequency = CONFIG_HDC1008_I2C_FREQUENCY;
+  config.address = priv->addr;
+  config.addrlen = 7;
+
+  ret = i2c_write(priv->i2c, &config, &regaddr, 1);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  ret = i2c_read(priv->i2c, &config, buf, 2);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  *regval = (buf[0] << 8) | buf[1];
+  return ret;
+}
+
+/****************************************************************************
+ * Name: hdc1008_putreg
+ *
+ * Description:
+ *   Set value of 16-bit register
+ *
+ ****************************************************************************/
+
+static int hdc1008_putreg(FAR struct hdc1008_dev_s *priv, uint8_t regaddr,
+                          FAR uint16_t regval)
+{
+  struct i2c_config_s config;
+  uint8_t buf[3];
+
+  config.frequency = CONFIG_HDC1008_I2C_FREQUENCY;
+  config.address = priv->addr;
+  config.addrlen = 7;
+
+  hdc1008_dbg("addr=%02x; reg=%02x; val=%04x\n", priv->addr, regaddr,
+              regval);
+
+  buf[0] = regaddr;
+  buf[1] = (uint8_t)((regval >> 8) & 0xff);
+  buf[2] = (uint8_t)(regval & 0xff);
+  return i2c_write(priv->i2c, &config, buf, 3);
+}
+
+/****************************************************************************
+ * Name: hdc1008_open
+ *
+ * Description:
+ *   This function is called whenever the hdc1008 device is opened.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+static int hdc1008_open(FAR struct file *filep)
+{
+  FAR struct inode *inode         = filep->f_inode;
+  FAR struct hdc1008_dev_s *priv  =
+    (FAR struct hdc1008_dev_s *)inode->i_private;
+  int ret;
+
+  ret = nxsem_wait_uninterruptible(&priv->devsem);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Increases the reference count */
+
+  ++priv->crefs;
+  DEBUGASSERT(priv->crefs > 0);
+
+  nxsem_post(&priv->devsem);
+
+  return 0;
+}
+#endif
+
+/****************************************************************************
+ * Name: hdc1008_close
+ *
+ * Description:
+ *   This routine is called when the hdc1008 device is closed.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+static int hdc1008_close(FAR struct file *filep)
+{
+  FAR struct inode *inode         = filep->f_inode;
+  FAR struct hdc1008_dev_s *priv  =
+    (FAR struct hdc1008_dev_s *)inode->i_private;
+  int ret;
+
+  ret = nxsem_wait_uninterruptible(&priv->devsem);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Decrement the reference count */
+
+  DEBUGASSERT(priv->crefs > 0);
+  --priv->crefs;
+
+  /* If the reference count is zero, and the driver has been unlinked, free
+   * the memory.
+   */
+
+  if ((priv->crefs <= 0) && priv->unlinked)
+    {
+      nxsem_destroy(&priv->devsem);
+      kmm_free(priv);
+      return OK;
+    }
+
+  nxsem_post(&priv->devsem);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: hdc1008_read
+ ****************************************************************************/
+
+static ssize_t hdc1008_read(FAR struct file *filep, FAR char *buffer,
+                          size_t buflen)
+{
+  FAR struct inode *inode         = filep->f_inode;
+  FAR struct hdc1008_dev_s *priv  =
+    (FAR struct hdc1008_dev_s *)inode->i_private;
+  int ret;
+  int len = 0;
+  struct hdc1008_conv_data_s data;
+
+  DEBUGASSERT(filep != NULL);
+
+  /* Sanity check of input buffer argument */
+
+  if (buffer == NULL)
+    {
+      return -EINVAL;
+    }
+
+  /* Get exclusive access */
+
+  ret = nxsem_wait_uninterruptible(&priv->devsem);
+  if (ret < 0)
+    {
+      return (ssize_t)ret;
+    }
+
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  if (priv->unlinked)
+    {
+      /* The driver is unliked, access is not allowed */
+
+      nxsem_post(&priv->devsem);
+      return -ENODEV;
+    }
+#endif
+
+  ret = hdc1008_measure_current_mode(priv, &data);
+  if (ret < 0)
+    {
+      nxsem_post(&priv->devsem);
+      return ret;
+    }
+
+  if (priv->mode == HDC1008_MEAS_TEMPERATURE)
+    {
+      len = snprintf(buffer, buflen, "%d.%d", data.temperature / 100,
+                     data.temperature % 100);
+    }
+  else if (priv->mode == HDC1008_MEAS_HUMIDITY)
+    {
+      len = snprintf(buffer, buflen, "%d.%d", data.humidity / 10,
+                     data.humidity % 10);
+    }
+  else if (priv->mode == HDC1008_MEAS_T_AND_RH)
+    {
+      len = snprintf(buffer, buflen, "%d.%d %d.%d",
+                     data.temperature / 100, data.temperature % 100,
+                     data.humidity / 10, data.humidity % 10);
+    }
+  else
+    {
+      nxsem_post(&priv->devsem);
+      return -EINVAL;
+    }
+
+  nxsem_post(&priv->devsem);
+
+  return len;
+}
+
+/****************************************************************************
+ * Name: hdc1008_measure_current_mode
+ * Description:
+ *   Measure from sensor according to current mode
+ *
+ ****************************************************************************/
+
+static int hdc1008_measure_current_mode(struct hdc1008_dev_s *priv,
+                                        struct hdc1008_conv_data_s *data)
+{
+  int ret;
+  int temperature;
+  int humidity;
+
+  switch (priv->mode)
+    {
+      case HDC1008_MEAS_TEMPERATURE:
+        ret = hdc1008_measure_t_or_rh(priv, true, &temperature);
+        if (ret < 0)
+          {
+            return ret;
+          }
+
+        data->temperature = temperature;
+        break;
+
+      case HDC1008_MEAS_HUMIDITY:
+        ret = hdc1008_measure_t_or_rh(priv, false, &humidity);
+        if (ret < 0)
+          {
+            return ret;
+          }
+
+        data->humidity = humidity;
+        break;
+
+      case HDC1008_MEAS_T_AND_RH:
+        ret = hdc1008_measure_trh(priv, &temperature, &humidity);
+        if (ret < 0)
+          {
+            return ret;
+          }
+
+        data->temperature = temperature;
+        data->humidity = humidity;
+        break;
+
+      default:
+        ret = -EINVAL;
+        break;
+    }
+
+    return ret;
+}
+
+/****************************************************************************
+ * Name: hdc1008_write
+ ****************************************************************************/
+
+static ssize_t hdc1008_write(FAR struct file *filep, FAR const char *buffer,
+                           size_t buflen)
+{
+  return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: hdc1008_ioctl
+ ****************************************************************************/
+
+static int hdc1008_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+  FAR struct inode *inode         = filep->f_inode;
+  FAR struct hdc1008_dev_s *priv  =
+    (FAR struct hdc1008_dev_s *)inode->i_private;
+  int ret;
+
+  /* Get exclusive access */
+
+  ret = nxsem_wait_uninterruptible(&priv->devsem);
+  if (ret < 0)
+    {
+      return (ssize_t)ret;
+    }
+
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  if (priv->unlinked)
+    {
+      /* The driver is unliked, access is not allowed */
+
+      nxsem_post(&priv->devsem);
+      return -ENODEV;
+    }
+#endif
+
+  switch (cmd)
+    {
+      case SNIOC_RESET:
+        {
+          ret = hdc1008_reset(priv);
+          hdc1008_dbg("reset ret: %d\n", ret);
+          break;
+        }
+
+      case SNIOC_SET_OPERATIONAL_MODE:
+        {
+          ret = hdc1008_set_operational_mode(priv, (uint8_t)arg);
+          hdc1008_dbg("opmode ret: %d\n", ret);
+          break;
+        }
+
+      case SNIOC_SET_RESOLUTION_T:
+        {
+          ret = hdc1008_set_temperature_resolution(priv, (uint8_t)arg);
+          hdc1008_dbg("tres ret: %d\n", ret);
+          break;
+        }
+
+      case SNIOC_SET_RESOLUTION_RH:
+        {
+          ret = hdc1008_set_humidity_resolution(priv, (uint8_t)arg);
+          hdc1008_dbg("hres ret: %d\n", ret);
+          break;
+        }
+
+      case SNIOC_SET_HEATER_MODE:
+        {
+          ret = hdc1008_set_heater_mode(priv, (uint8_t)arg);
+          hdc1008_dbg("heater ret: %d\n", ret);
+          break;
+        }
+
+      case SNIOC_GET_CONFIGURATION:
+        {
+          uint16_t reg;
+          ret = hdc1008_getreg(priv, HDC1008_REG_CONFIGURATION, &reg);
+          if (ret >= 0)
+            {
+              *(uint16_t *)arg = reg;
+            }
+
+          hdc1008_dbg("read config ret: %d\n", ret);
+          break;
+        }
+
+      case SNIOC_MEASURE:
+        {
+          struct hdc1008_conv_data_s data;
+          ret = hdc1008_measure_current_mode(priv, &data);
+          if (ret >= 0)
+            memcpy((struct hdc1008_conv_data_s *)arg, &data, sizeof(data));
+          break;
+        }
+
+      default:
+        {
+          hdc1008_dbg("unrecognized cmd: %d\n", cmd);
+          _info("unknown cmd: %d\n", cmd);
+          ret = -ENOTTY;
+          break;
+        }
+    }
+
+  nxsem_post(&priv->devsem);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: hdc1008_unlink
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+static int hdc1008_unlink(FAR struct inode *inode)
+{
+  FAR struct hdc1008_dev_s *priv;
+  int ret;
+
+  DEBUGASSERT((inode != NULL) && (inode->i_private != NULL));
+  priv = (FAR struct hdc1008_dev_s *)inode->i_private;
+
+  ret = nxsem_wait_uninterruptible(&priv->devsem);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Are there any open references to the driver? */
+
+  if (priv->crefs <= 0)
+    {
+      nxsem_destroy(&priv->devsem);
+      kmm_free(priv);
+      return OK;
+    }
+
+  /* We still have open references, mark driver as unliked and wait for
+   * everyone to close their instance.
+   */
+
+  priv->unlinked = true;
+  nxsem_post(&priv->devsem);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: hdc1008_reset
+ *
+ * Description:
+ *   Perform a software reset of the sensor
+ *
+ ****************************************************************************/
+
+static int hdc1008_reset(FAR struct hdc1008_dev_s *priv)
+{
+  int ret;
+  int count = 10;
+  uint16_t reg;
+
+  ret = hdc1008_getreg(priv, HDC1008_REG_CONFIGURATION, &reg);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  ret = hdc1008_putreg(priv, HDC1008_REG_CONFIGURATION,
+                       reg | HDC1008_CONFIGURATION_RST);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Now we wait until the sensor has reset */
+
+  do
+    {
+      ret = hdc1008_getreg(priv, HDC1008_REG_CONFIGURATION, &reg);
+      nxsig_usleep(1000);
+      --count;
+    }
+  while ((reg & HDC1008_CONFIGURATION_RST) && (ret == OK) && count);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: hdc1008_register
+ *
+ * Description:
+ *   Register the HDC1008 character device as 'devpath'
+ *
+ * Input Parameters:
+ *   devpath - The full path to the driver to register. E.g., "/dev/temp0"
+ *   i2c     - An instance of the I2C interface to use to communicate with
+ *             the HDC1008
+ *   addr    - The I2C address of the HDC1008. The I2C address is
+ *             configurable by two address pins, in the range of 0x40-0x43
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int hdc1008_register(FAR const char *devpath, FAR struct i2c_master_s *i2c,
+                     uint8_t addr)
+{
+  FAR struct hdc1008_dev_s *priv;
+  uint16_t reg;
+  int ret;
+
+  DEBUGASSERT(i2c != NULL);
+
+  /* Initialize the driver structure */
+
+  priv =
+    (FAR struct hdc1008_dev_s *)kmm_zalloc(sizeof(struct hdc1008_dev_s));
+
+  if (priv == NULL)
+    {
+      snerr("ERROR: Failed to allocate memory for driver data\n");
+      return -ENOMEM;
+    }
+
+  priv->i2c = i2c;
+  priv->addr = addr;
+
+  nxsem_init(&priv->devsem, 0, 1);
+
+  /* Register the driver */
+
+  ret = register_driver(devpath, &g_hdc1008fops, 0666, priv);
+  if (ret < 0)
+    {
+      snerr("ERROR: Failed to register driver: %d\n", ret);
+      nxsem_destroy(&priv->devsem);
+      kmm_free(priv);
+    }
+
+  sninfo("driver registered\n");
+
+  /* 15 ms max startup time according to datasheet, let's wait just to be
+   * sure that it is ready.
+   */
+
+  nxsig_usleep(15000);
+
+  /* Set default configuration */
+
+  ret = hdc1008_getreg(priv, HDC1008_REG_CONFIGURATION, &reg);
+  if (ret < 0)
+    {
+      snerr("ERROR: failed to read default configuration\n");
+      nxsem_destroy(&priv->devsem);
+      kmm_free(priv);
+      return ret;
+    }
+
+  /* Enforce sensor default configuration at startup */
+
+  reg = HDC1008_CONFIGURATION_MODE;
+  ret = hdc1008_putreg(priv, HDC1008_REG_CONFIGURATION, reg);
+  if (ret < 0)
+    {
+      snerr("ERROR: failed to set default configuration: %d\n", ret);
+      nxsem_destroy(&priv->devsem);
+      kmm_free(priv);
+      return ret;
+    }
+
+  priv->mode = HDC1008_MEAS_T_AND_RH;
+
+  return ret;
+}
+#endif /* CONFIG_I2C && CONFIG_SENSORS_HDC1008 */
diff --git a/include/nuttx/sensors/hdc1008.h b/include/nuttx/sensors/hdc1008.h
new file mode 100644
index 0000000..b082d2a
--- /dev/null
+++ b/include/nuttx/sensors/hdc1008.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+ * include/nuttx/sensors/hdc1008.h
+ *
+ *   Copyright (C) 2020 Pelle Windestam. 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 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_NUTT_SENSORS_HDC1008_H
+#define __INCLUDE_NUTT_SENSORS_HDC1008_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/sensors/ioctl.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Default address if both address pins are grounded */
+
+#define CONFIG_HDC1008_ADDR 0x40
+
+/* Modes of operation. Used in the calls to ioctl(). */
+
+#define HDC1008_MEAS_TEMPERATURE  0x00
+#define HDC1008_MEAS_HUMIDITY     0x01
+#define HDC1008_MEAS_T_AND_RH     0x02
+
+/* IOCTL commands */
+
+#define SNIOC_RESET                 _SNIOC(0x0001) /* Soft Reset */
+#define SNIOC_SET_OPERATIONAL_MODE  _SNIOC(0x0002) /* Mode: 0, 1, 2 */
+#define SNIOC_SET_RESOLUTION_T      _SNIOC(0x0003) /* Resolution: 11, 14 */
+#define SNIOC_SET_RESOLUTION_RH     _SNIOC(0x0004) /* Resolution: 8, 11, 14 */
+#define SNIOC_SET_HEATER_MODE       _SNIOC(0x0005) /* Heater on or off */
+#define SNIOC_GET_CONFIGURATION     _SNIOC(0x0006) /* Read config register */
+#define SNIOC_MEASURE               _SNIOC(0x0007) /* Perform measurement */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct i2c_master_s;  /* Forward reference */
+
+/* Structure with measurement data. Temperature is scaled by 100 and humidity
+ * by 10.
+ */
+
+struct hdc1008_conv_data_s
+{
+  int temperature;
+  int humidity;
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: hdc1008_register
+ *
+ * Description:
+ *   Register the HDC1008 character device as 'devpath'
+ *
+ * Input Parameters:
+ *   devpath - The full path to the driver to register. E.g., "/dev/temp0"
+ *   i2c     - An instance of the I2C interface to use to communicate with
+ *             the HDC1008
+ *   addr    - The I2C address of the HDC1008. The I2C address is
+ *             configurable by two address pins, in the range of 0x40-0x43
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int hdc1008_register(FAR const char *devpath, FAR struct i2c_master_s *i2c,
+                     uint8_t addr);
+
+#endif /* __INCLUDE_NUTT_SENSORS_HDC1008_H */


Mime
View raw message