1025 lines
27 KiB
C
1025 lines
27 KiB
C
/****************************************************************************
|
|
* 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, humidity by 10.
|
|
*/
|
|
|
|
#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
|
|
*
|
|
* Description:
|
|
* Called when the driver is read from.
|
|
*
|
|
****************************************************************************/
|
|
|
|
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 (temperature, humidity or
|
|
* both).
|
|
*
|
|
****************************************************************************/
|
|
|
|
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
|
|
*
|
|
* Description:
|
|
* Called when the driver is written to (not supported).
|
|
*
|
|
****************************************************************************/
|
|
|
|
static ssize_t hdc1008_write(FAR struct file *filep, FAR const char *buffer,
|
|
size_t buflen)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hdc1008_ioctl
|
|
*
|
|
* Description:
|
|
* Called when an ioctl() operation is made for the driver.
|
|
*
|
|
****************************************************************************/
|
|
|
|
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
|
|
*
|
|
* Description:
|
|
* Called when the driver is unlinked.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#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 */
|