From commits-return-8982-archive-asf-public=cust-asf.ponee.io@nuttx.apache.org Tue May 5 18:21:04 2020 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [207.244.88.153]) by mx-eu-01.ponee.io (Postfix) with SMTP id AAC83180665 for ; Tue, 5 May 2020 20:21:03 +0200 (CEST) Received: (qmail 34713 invoked by uid 500); 5 May 2020 18:21:03 -0000 Mailing-List: contact commits-help@nuttx.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@nuttx.apache.org Delivered-To: mailing list commits@nuttx.apache.org Received: (qmail 34687 invoked by uid 99); 5 May 2020 18:21:03 -0000 Received: from ec2-52-202-80-70.compute-1.amazonaws.com (HELO gitbox.apache.org) (52.202.80.70) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 05 May 2020 18:21:03 +0000 Received: by gitbox.apache.org (ASF Mail Server at gitbox.apache.org, from userid 33) id D775C8BFAD; Tue, 5 May 2020 18:21:02 +0000 (UTC) Date: Tue, 05 May 2020 18:21:02 +0000 To: "commits@nuttx.apache.org" Subject: [incubator-nuttx] 01/03: Added driver for the hdc1008 temperature/humidity sensor. MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit From: gnutt@apache.org In-Reply-To: <158870286129.21796.1561391154522065914@gitbox.apache.org> References: <158870286129.21796.1561391154522065914@gitbox.apache.org> X-Git-Host: gitbox.apache.org X-Git-Repo: incubator-nuttx X-Git-Refname: refs/heads/master X-Git-Reftype: branch X-Git-Rev: 8a0b2bc14e1f8562dea6987878073838a777aa65 X-Git-NotificationType: diff X-Git-Multimail-Version: 1.5.dev Auto-Submitted: auto-generated Message-Id: <20200505182102.D775C8BFAD@gitbox.apache.org> 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 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 + * + * 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 + +#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, ®, 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, ®, 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, ®); + 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, ®); + 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, ®); + 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, ®); + 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, ®addr, 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, ®); + 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, ®); + 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, ®); + 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, ®); + 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 + +/**************************************************************************** + * 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 */