incubator-nuttx/drivers/sensors/hts221.c

1258 lines
30 KiB
C

/****************************************************************************
* drivers/sensors/hts221.c
*
* Copyright (C) 2014 Haltian Ltd. 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <debug.h>
#include <stdio.h>
#include <fcntl.h>
#include <poll.h>
#include <errno.h>
#include <nuttx/arch.h>
#include <nuttx/i2c/i2c_master.h>
#include <nuttx/irq.h>
#include <nuttx/kmalloc.h>
#include <nuttx/random.h>
#include <nuttx/sensors/hts221.h>
/****************************************************************************
* Pre-Processor Definitions
****************************************************************************/
#ifdef CONFIG_HTS221_DEBUG
# define hts221_dbg(x, ...) _info(x, ##__VA_ARGS__)
#else
# define hts221_dbg(x, ...) sninfo(x, ##__VA_ARGS__)
#endif
#ifndef CONFIG_HTS221_I2C_FREQUENCY
# define CONFIG_HTS221_I2C_FREQUENCY 400000
#endif
#define HTS221_WHO_AM_I 0x0f
#define HTS221_AV_CONF 0x10
#define HTS221_CTRL_REG1 0x20
#define HTS221_CTRL_REG2 0x21
#define HTS221_CTRL_REG3 0x22
#define HTS221_STATUS_REG 0x27
#define HTS221_HUM_OUT_L 0x28
#define HTS221_HUM_OUT_H 0x29
#define HTS221_TEMP_OUT_L 0x2a
#define HTS221_TEMP_OUT_H 0x2b
/* Calibration registers */
#define HTS221_CALIB_H0_RH_X2 0x30
#define HTS221_CALIB_H1_RH_X2 0x31
#define HTS221_CALIB_T0_DEGC_X8 0x32
#define HTS221_CALIB_T1_DEGC_X8 0x33
#define HTS221_CALIB_T1_T0_MSB 0x35
#define HTS221_CALIB_H0T0_OUT_L 0x36
#define HTS221_CALIB_H0T0_OUT_H 0x37
#define HTS221_CALIB_H1T0_OUT_L 0x3a
#define HTS221_CALIB_H1T0_OUT_H 0x3b
#define HTS221_CALIB_T0_OUT_L 0x3c
#define HTS221_CALIB_T0_OUT_H 0x3d
#define HTS221_CALIB_T1_OUT_L 0x3e
#define HTS221_CALIB_T1_OUT_H 0x3f
/* HTS221_CTRL_REG1 */
#define HTS221_CTRL_REG1_PD (1 << 7)
#define HTS221_CTRL_REG1_BDU (1 << 2)
/* HTS221_CTRL_REG2 */
#define HTS221_CTRL_REG2_BOOT (1 << 7)
#define HTS221_CTRL_REG2_ONE_SHOT (1 << 0)
/* HTS221_CTRL_REG3 */
#define HTS221_CTRL_REG3_DRDY_L_H (1 << 7)
#define HTS221_CTRL_REG3_PP_OD (1 << 6)
#define HTS221_CTRL_REG3_DRDY_EN (1 << 2)
/* HTS221_STATUS_REG */
#define HTS221_STATUS_REG_H_DA (1 << 1)
#define HTS221_STATUS_REG_T_DA (1 << 0)
#define HTS221_I2C_RETRIES 10
/****************************************************************************
* Private Function Prototypes
*****************************************************************************/
static int hts221_open(FAR struct file *filep);
static int hts221_close(FAR struct file *filep);
static ssize_t hts221_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
static ssize_t hts221_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen);
static int hts221_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
#ifndef CONFIG_DISABLE_POLL
static int hts221_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup);
#endif
/****************************************************************************
* Private Types
****************************************************************************/
struct hts221_dev_s
{
struct i2c_master_s *i2c;
uint8_t addr;
hts221_config_t *config;
sem_t devsem;
volatile bool int_pending;
#ifndef CONFIG_DISABLE_POLL
struct pollfd *fds[CONFIG_HTS221_NPOLLWAITERS];
#endif
struct
{
int16_t t0_out;
int16_t t1_out;
int16_t h0_t0_out;
int16_t h1_t0_out;
unsigned int t0_x8:10;
unsigned int t1_x8:10;
uint8_t h0_x2;
uint8_t h1_x2;
} calib;
};
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations g_humidityops =
{
hts221_open, /* open */
hts221_close, /* close */
hts221_read, /* read */
hts221_write, /* write */
NULL, /* seek */
hts221_ioctl /* ioctl */
#ifndef CONFIG_DISABLE_POLL
, hts221_poll /* poll */
#endif
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
, NULL /* unlink */
#endif
};
/****************************************************************************
* Private Functions
****************************************************************************/
static int hts221_do_transfer(FAR struct hts221_dev_s *priv,
FAR struct i2c_msg_s *msgv,
size_t nmsg)
{
int ret = -EIO;
int retries;
for (retries = 0; retries < HTS221_I2C_RETRIES; retries++)
{
ret = I2C_TRANSFER(priv->i2c, msgv, nmsg);
if (ret >= 0)
{
return 0;
}
else
{
/* Some error. Try to reset I2C bus and keep trying. */
#ifdef CONFIG_I2C_RESET
if (retries == HTS221_I2C_RETRIES - 1)
{
break;
}
ret = I2C_RESET(priv->i2c);
if (ret < 0)
{
hts221_dbg("I2C_RESET failed: %d\n", ret);
return ret;
}
#endif
}
}
hts221_dbg("xfer failed: %d\n", ret);
return ret;
}
static int32_t hts221_write_reg8(FAR struct hts221_dev_s *priv,
const uint8_t *command)
{
struct i2c_msg_s msgv[2] =
{
{
.frequency = CONFIG_HTS221_I2C_FREQUENCY,
.addr = priv->addr,
.flags = 0,
.buffer = (FAR void *)&command[0],
.length = 1
},
{
.frequency = CONFIG_HTS221_I2C_FREQUENCY,
.addr = priv->addr,
.flags = I2C_M_NORESTART,
.buffer = (FAR void *)&command[1],
.length = 1
}
};
return hts221_do_transfer(priv, msgv, 2);
}
static int hts221_read_reg(FAR struct hts221_dev_s *priv,
FAR const uint8_t *command, FAR uint8_t *value)
{
struct i2c_msg_s msgv[2] =
{
{
.frequency = CONFIG_HTS221_I2C_FREQUENCY,
.addr = priv->addr,
.flags = 0,
.buffer = (FAR void *)command,
.length = 1
},
{
.frequency = CONFIG_HTS221_I2C_FREQUENCY,
.addr = priv->addr,
.flags = I2C_M_READ,
.buffer = value,
.length = 1
}
};
return hts221_do_transfer(priv, msgv, 2);
}
static int hts221_get_id(FAR struct hts221_dev_s *priv, uint8_t *value)
{
int ret = OK;
uint8_t cmd = HTS221_WHO_AM_I;
ret = hts221_read_reg(priv, &cmd, value);
hts221_dbg("Who am I request: 0x%02X\n", *value);
return ret;
}
static int hts221_cfgr_resolution(FAR struct hts221_dev_s *priv,
FAR hts221_settings_t *settings)
{
int ret;
uint8_t value;
const uint8_t addr = HTS221_AV_CONF;
uint8_t regval = 0;
uint8_t cmd[2] = { 0 };
const uint8_t mask = 0x3f;
ret = hts221_read_reg(priv, &addr, &regval);
hts221_dbg("Default resolution: 0x%02X\n", regval);
if (ret < 0)
{
return ERROR;
}
value = (uint8_t)settings->humid_resol |
((uint8_t)settings->temp_resol << 3);
regval &= ~mask;
cmd[0] = addr;
cmd[1] = regval | value;
hts221_dbg("New resolution: 0x%02X\n", cmd[1]);
ret = hts221_write_reg8(priv, cmd);
hts221_dbg("Resolution changed: temp=%d humid=%d ret=%d\n",
settings->temp_resol, settings->humid_resol, ret);
return ret;
}
static int hts221_config_ctrl_reg3(FAR struct hts221_dev_s *priv,
FAR hts221_settings_t *settings)
{
int ret = OK;
uint8_t regval = 0;
uint8_t addr = HTS221_CTRL_REG3;
const uint8_t mask = 0xc4;
uint8_t data_to_write[2] = { 0 };
ret = hts221_read_reg(priv, &addr, &regval);
hts221_dbg("CTRL_REG%d: 0x%02X\n", 3, regval);
if (ret < 0)
{
return ERROR;
}
regval &= ~mask;
regval |= (uint8_t)(settings->is_high_edge ? 0 : HTS221_CTRL_REG3_DRDY_L_H);
regval |= (uint8_t)(settings->is_open_drain ? HTS221_CTRL_REG3_PP_OD : 0);
regval |= (uint8_t)(settings->is_data_rdy ? HTS221_CTRL_REG3_DRDY_EN : 0);
data_to_write[0] = addr;
data_to_write[1] = regval;
ret = hts221_write_reg8(priv, data_to_write);
if (ret >= 0)
{
ret = hts221_read_reg(priv, &addr, &regval);
if (ret >= 0)
{
hts221_dbg("wrote 0x%02X => CTRL_REG%d: 0x%02X\n",
data_to_write[1], 3, regval);
}
}
return ret;
}
static int hts221_config_ctrl_reg2(FAR struct hts221_dev_s *priv,
FAR hts221_settings_t *settings)
{
int ret = OK;
uint8_t regval = 0;
uint8_t addr = HTS221_CTRL_REG2;
const uint8_t mask = 0x80;
uint8_t data_to_write[2] = { 0 };
int retries = 5;
if (!settings->is_boot)
{
return OK;
}
ret = hts221_read_reg(priv, &addr, &regval);
hts221_dbg("CTRL_REG%d: 0x%02X\n", 2, regval);
if (ret < 0)
{
return ERROR;
}
regval &= ~mask;
regval |= HTS221_CTRL_REG2_BOOT;
data_to_write[0] = addr;
data_to_write[1] = regval;
ret = hts221_write_reg8(priv, data_to_write);
if (ret >= 0)
{
/* Wait until boot bit is cleared. */
do
{
ret = hts221_read_reg(priv, &addr, &regval);
if (ret >= 0)
{
hts221_dbg("wrote 0x%02X => CTRL_REG%d: 0x%02X\n",
data_to_write[1], 2, regval);
}
else
{
break;
}
if ((regval & HTS221_CTRL_REG2_BOOT) == 0)
{
/* After boot bit is cleared, wait additional 5 msec as
* recommended in HTS221 application note.
*/
up_mdelay(5);
break;
}
usleep(10 * 1000);
retries--;
}
while (retries);
if (ret >= 0 && (regval & HTS221_CTRL_REG2_BOOT) != 0)
{
ret = -ETIMEDOUT;
}
}
return ret;
}
static int hts221_config_ctrl_reg1(FAR struct hts221_dev_s *priv,
FAR hts221_settings_t *settings)
{
int ret = OK;
uint8_t regval = 0;
uint8_t addr = HTS221_CTRL_REG1;
const uint8_t mask = 0x87;
uint8_t data_to_write[2] = { 0 };
ret = hts221_read_reg(priv, &addr, &regval);
hts221_dbg("CTRL_REG%d: 0x%02X\n", 1, regval);
if (ret < 0)
{
return ERROR;
}
regval &= ~mask;
regval |= (uint8_t) (settings->odr & 0xFF);
regval |= (uint8_t) (settings->is_bdu ? HTS221_CTRL_REG1_BDU : 0);
data_to_write[0] = addr;
data_to_write[1] = regval;
ret = hts221_write_reg8(priv, data_to_write);
if (ret >= 0)
{
ret = hts221_read_reg(priv, &addr, &regval);
if (ret >= 0)
{
hts221_dbg("wrote 0x%02X => CTRL_REG%d: 0x%02X\n",
data_to_write[1], 1, regval);
}
}
return ret;
}
static int hts221_power_on_off(FAR struct hts221_dev_s *priv, bool on)
{
int ret = OK;
uint8_t regval = 0;
uint8_t addr = HTS221_CTRL_REG1;
uint8_t data_to_write[2];
ret = hts221_read_reg(priv, &addr, &regval);
hts221_dbg("CTRL_REG%d: 0x%02X\n", 1, regval);
if (ret < 0)
{
return ret;
}
if (on)
{
regval |= HTS221_CTRL_REG1_PD;
}
else
{
regval &= ~HTS221_CTRL_REG1_PD;
}
data_to_write[0] = addr;
data_to_write[1] = regval;
ret = hts221_write_reg8(priv, data_to_write);
if (ret >= 0)
{
ret = hts221_read_reg(priv, &addr, &regval);
if (ret >= 0)
{
hts221_dbg("wrote 0x%02X => CTRL_REG%d: 0x%02X\n",
data_to_write[1], 1, regval);
}
}
return ret;
}
static int hts221_config(FAR struct hts221_dev_s *priv,
FAR hts221_settings_t *cfgr)
{
int ret = OK;
ret = hts221_config_ctrl_reg2(priv, cfgr); /* Performs sensor reset. */
if (ret < 0)
{
return ERROR;
}
ret = hts221_cfgr_resolution(priv, cfgr);
if (ret < 0)
{
return ERROR;
}
ret = hts221_config_ctrl_reg3(priv, cfgr);
if (ret < 0)
{
return ERROR;
}
ret = hts221_config_ctrl_reg1(priv, cfgr);
if (ret < 0)
{
return ERROR;
}
return ret;
}
static int hts221_start_conversion(FAR struct hts221_dev_s *priv)
{
int ret;
uint8_t addr = HTS221_CTRL_REG2;
uint8_t data_to_write[2];
ret = hts221_power_on_off(priv, true);
if (ret < 0)
{
return ERROR;
}
data_to_write[0] = addr;
data_to_write[1] = (uint8_t) HTS221_CTRL_REG2_ONE_SHOT;
ret = hts221_write_reg8(priv, data_to_write);
if (ret < 0)
{
hts221_dbg("Cannot start conversion\n");
ret = ERROR;
}
return ret;
}
static int hts221_check_status(FAR struct hts221_dev_s *priv,
FAR hts221_status_t *status)
{
int ret = OK;
uint8_t addr = HTS221_STATUS_REG;
const uint8_t humid_mask = 0x02;
const uint8_t temp_mask = 0x01;
uint8_t regval = 0;
ret = hts221_read_reg(priv, &addr, &regval);
if (ret < 0)
{
return ERROR;
}
status->is_humid_ready = ((regval & humid_mask) ? true : false);
status->is_temp_ready = ((regval & temp_mask) ? true : false);
return ret;
}
static int hts221_read_raw_data(FAR struct hts221_dev_s *priv,
FAR hts221_raw_data_t *data)
{
int ret = OK;
uint8_t addr_humid_low = HTS221_HUM_OUT_L;
uint8_t addr_humid_high = HTS221_HUM_OUT_H;
uint8_t addr_temp_low = HTS221_TEMP_OUT_L;
uint8_t addr_temp_high = HTS221_TEMP_OUT_H;
irqstate_t flags;
ret = hts221_read_reg(priv, &addr_humid_low, &data->humid_low_bits);
if (ret < 0)
{
return ERROR;
}
ret = hts221_read_reg(priv, &addr_humid_high, &data->humid_high_bits);
if (ret < 0)
{
return ERROR;
}
ret = hts221_read_reg(priv, &addr_temp_low, &data->temp_low_bits);
if (ret < 0)
{
return ERROR;
}
ret = hts221_read_reg(priv, &addr_temp_high, &data->temp_high_bits);
if (ret < 0)
{
return ERROR;
}
/* Add low-order bytes to entropy pool. */
add_sensor_randomness(((uint32_t)data->humid_low_bits << 8) | data->temp_low_bits);
flags = enter_critical_section();
priv->int_pending = false;
leave_critical_section(flags);
hts221_dbg("Humid: 0x%02X, 0x%02X Temper: 0x%02X 0x%02X\n",
data->humid_high_bits, data->humid_low_bits,
data->temp_high_bits, data->temp_low_bits);
return ret;
}
static int hts221_load_calibration_data(FAR struct hts221_dev_s *priv)
{
int ret;
uint8_t addr;
uint8_t t0_degc_x8 = 0;
uint8_t t1_degc_x8 = 0;
uint8_t t1_t0_msb = 0;
uint8_t t0_out_lsb = 0;
uint8_t t0_out_msb = 0;
uint8_t t1_out_lsb = 0;
uint8_t t1_out_msb = 0;
uint8_t h0_rh_x2 = 0;
uint8_t h1_rh_x2 = 0;
uint8_t h0t0_out_lsb = 0;
uint8_t h0t0_out_msb = 0;
uint8_t h1t0_out_lsb = 0;
uint8_t h1t0_out_msb = 0;
addr = HTS221_CALIB_T0_DEGC_X8;
ret = hts221_read_reg(priv, &addr, &t0_degc_x8);
if (ret < 0)
{
return ret;
}
addr = HTS221_CALIB_T1_DEGC_X8;
ret = hts221_read_reg(priv, &addr, &t1_degc_x8);
if (ret < 0)
{
return ret;
}
addr = HTS221_CALIB_T1_T0_MSB;
ret = hts221_read_reg(priv, &addr, &t1_t0_msb);
if (ret < 0)
{
return ret;
}
addr = HTS221_CALIB_T0_OUT_L;
ret = hts221_read_reg(priv, &addr, &t0_out_lsb);
if (ret < 0)
{
return ret;
}
addr = HTS221_CALIB_T0_OUT_H;
ret = hts221_read_reg(priv, &addr, &t0_out_msb);
if (ret < 0)
{
return ret;
}
addr = HTS221_CALIB_T1_OUT_L;
ret = hts221_read_reg(priv, &addr, &t1_out_lsb);
if (ret < 0)
{
return ret;
}
addr = HTS221_CALIB_T1_OUT_H;
ret = hts221_read_reg(priv, &addr, &t1_out_msb);
if (ret < 0)
{
return ret;
}
addr = HTS221_CALIB_H0_RH_X2;
ret = hts221_read_reg(priv, &addr, &h0_rh_x2);
if (ret < 0)
{
return ret;
}
addr = HTS221_CALIB_H1_RH_X2;
ret = hts221_read_reg(priv, &addr, &h1_rh_x2);
if (ret < 0)
{
return ret;
}
addr = HTS221_CALIB_H0T0_OUT_L;
ret = hts221_read_reg(priv, &addr, &h0t0_out_lsb);
if (ret < 0)
{
return ret;
}
addr = HTS221_CALIB_H0T0_OUT_H;
ret = hts221_read_reg(priv, &addr, &h0t0_out_msb);
if (ret < 0)
{
return ret;
}
addr = HTS221_CALIB_H1T0_OUT_L;
ret = hts221_read_reg(priv, &addr, &h1t0_out_lsb);
if (ret < 0)
{
return ret;
}
addr = HTS221_CALIB_H1T0_OUT_H;
ret = hts221_read_reg(priv, &addr, &h1t0_out_msb);
if (ret < 0)
{
return ret;
}
priv->calib.t0_x8 = t0_degc_x8 | ((t1_t0_msb & 0x3) << 8);
priv->calib.t1_x8 = t1_degc_x8 | ((t1_t0_msb & (0x3 << 2)) << (8 - 2));
priv->calib.t0_out = (uint16_t) (t0_out_lsb | (t0_out_msb << 8));
priv->calib.t1_out = (uint16_t) (t1_out_lsb | (t1_out_msb << 8));
priv->calib.h0_x2 = h0_rh_x2;
priv->calib.h1_x2 = h1_rh_x2;
priv->calib.h0_t0_out = (uint16_t) (h0t0_out_lsb | (h0t0_out_msb << 8));
priv->calib.h1_t0_out = (uint16_t) (h1t0_out_lsb | (h1t0_out_msb << 8));
hts221_dbg("calib.t0_x8: %d\n", priv->calib.t0_x8);
hts221_dbg("calib.t1_x8: %d\n", priv->calib.t1_x8);
hts221_dbg("calib.t0_out: %d\n", priv->calib.t0_out);
hts221_dbg("calib.t1_out: %d\n", priv->calib.t1_out);
hts221_dbg("calib.h0_x2: %d\n", priv->calib.h0_x2);
hts221_dbg("calib.h1_x2: %d\n", priv->calib.h1_x2);
hts221_dbg("calib.h0_t0_out: %d\n", priv->calib.h0_t0_out);
hts221_dbg("calib.h1_t0_out: %d\n", priv->calib.h1_t0_out);
/* As calibration coefficients are unique to each sensor device,
* they are a good candidate to be added to entropy pool.
*/
up_rngaddentropy(RND_SRC_HW, (uint32_t *)&priv->calib,
sizeof(priv->calib) / sizeof(uint32_t));
return OK;
}
static int hts221_calculate_temperature(FAR struct hts221_dev_s *priv,
FAR int *temperature,
FAR hts221_raw_data_t *raw_data)
{
int16_t t_out = (raw_data->temp_high_bits << 8) | raw_data->temp_low_bits;
int x0 = priv->calib.t0_out;
int x1 = priv->calib.t1_out;
int y0 = priv->calib.t0_x8;
int y1 = priv->calib.t1_x8;
int x = t_out;
int64_t y;
int x1_x0_diff;
x1_x0_diff = x1 - x0;
y = (y0 * x1_x0_diff + (y1 - y0) * (x - x0));
y *= HTS221_TEMPERATURE_PRECISION;
y /= x1_x0_diff * 8;
*temperature = (int)y;
hts221_dbg("Interpolation data temper: %d\n", *temperature);
return OK;
}
static int hts221_calculate_humidity(FAR struct hts221_dev_s *priv,
FAR unsigned int *humidity,
FAR hts221_raw_data_t *raw_data)
{
int16_t h_out = (raw_data->humid_high_bits << 8) | raw_data->humid_low_bits;
int x0 = priv->calib.h0_t0_out;
int x1 = priv->calib.h1_t0_out;
int y0 = priv->calib.h0_x2;
int y1 = priv->calib.h1_x2;
int x = h_out;
int64_t y;
int x1_x0_diff;
x1_x0_diff = x1 - x0;
y = (y0 * x1_x0_diff + (y1 - y0) * (x - x0));
y *= HTS221_HUMIDITY_PRECISION;
y /= x1_x0_diff * 2;
*humidity = (int)y;
hts221_dbg("Interpolation data humidity: %d\n", *humidity);
return OK;
}
static int hts221_read_convert_data(FAR struct hts221_dev_s *priv,
FAR hts221_conv_data_t *data)
{
int ret = OK;
hts221_raw_data_t raw_data;
ret = hts221_read_raw_data(priv, &raw_data);
if (ret < 0)
{
return ERROR;
}
ret = hts221_calculate_temperature(priv, &data->temperature, &raw_data);
if (ret < 0)
{
return ERROR;
}
hts221_dbg("Temperature calculated\n");
ret = hts221_calculate_humidity(priv, &data->humidity, &raw_data);
if (ret < 0)
{
return ERROR;
}
hts221_dbg("Humidity calculated\n");
return ret;
}
#ifdef CONFIG_HTS221_DEBUG
static int hts221_dump_registers(FAR struct hts221_dev_s *priv)
{
int ret = OK;
uint8_t av_addr = HTS221_AV_CONF;
uint8_t ctrl_reg1_addr = HTS221_CTRL_REG1;
uint8_t ctrl_reg2_addr = HTS221_CTRL_REG2;
uint8_t ctrl_reg3_addr = HTS221_CTRL_REG3;
uint8_t regval = 0;
ret = hts221_read_reg(priv, &av_addr, &regval);
if (ret < 0)
{
return ERROR;
}
hts221_dbg("AV_CONF_REG: 0x%02X\n", regval);
ret = hts221_read_reg(priv, &ctrl_reg1_addr, &regval);
if (ret < 0)
{
return ERROR;
}
hts221_dbg("CTRL_REG_1: 0x%02X\n", regval);
ret = hts221_read_reg(priv, &ctrl_reg2_addr, &regval);
if (ret < 0)
{
return ERROR;
}
hts221_dbg("CTRL_REG_2: 0x%02X\n", regval);
ret = hts221_read_reg(priv, &ctrl_reg3_addr, &regval);
if (ret < 0)
{
return ERROR;
}
hts221_dbg("CTRL_REG_3: 0x%02X\n", regval);
return ret;
}
#endif
static int hts221_open(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct hts221_dev_s *priv = inode->i_private;
int ret;
/* Get exclusive access */
do
{
ret = nxsem_wait(&priv->devsem);
/* The only case that an error should occur here is if the wait was
* awakened by a signal.
*/
DEBUGASSERT(ret == OK || ret == -EINTR);
}
while (ret == -EINTR);
priv->config->set_power(priv->config, true);
priv->config->irq_enable(priv->config, true);
nxsem_post(&priv->devsem);
hts221_dbg("Sensor is powered on\n");
return OK;
}
static int hts221_close(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct hts221_dev_s *priv = inode->i_private;
int ret;
/* Get exclusive access */
do
{
ret = nxsem_wait(&priv->devsem);
/* The only case that an error should occur here is if the wait was
* awakened by a signal.
*/
DEBUGASSERT(ret == OK || ret == -EINTR);
}
while (ret == -EINTR);
priv->config->irq_enable(priv->config, false);
ret = hts221_power_on_off(priv, false);
priv->config->set_power(priv->config, false);
nxsem_post(&priv->devsem);
hts221_dbg("CLOSED\n");
return ret;
}
static ssize_t hts221_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct hts221_dev_s *priv = inode->i_private;
hts221_conv_data_t data;
ssize_t length = 0;
int ret;
/* Get exclusive access */
do
{
ret = nxsem_wait(&priv->devsem);
/* The only case that an error should occur here is if the wait was
* awakened by a signal.
*/
DEBUGASSERT(ret == OK || ret == -EINTR);
}
while (ret == -EINTR);
ret = hts221_read_convert_data(priv, &data);
if (ret < 0)
{
hts221_dbg("cannot read data: %d\n", ret);
}
else
{
/* This interface is mainly intended for easy debugging in nsh. */
length = snprintf(buffer, buflen, "%d %u\n",
data.temperature, data.humidity);
if (length > buflen)
{
length = buflen;
}
}
nxsem_post(&priv->devsem);
return length;
}
static ssize_t hts221_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen)
{
ssize_t length = 0;
return length;
}
static int hts221_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct inode *inode = filep->f_inode;
FAR struct hts221_dev_s *priv = inode->i_private;
int ret;
/* Get exclusive access */
do
{
ret = nxsem_wait(&priv->devsem);
/* The only case that an error should occur here is if the wait was
* awakened by a signal.
*/
DEBUGASSERT(ret == OK || ret == -EINTR);
}
while (ret == -EINTR);
switch (cmd)
{
case SNIOC_GET_DEV_ID:
ret = hts221_get_id(priv, (FAR uint8_t *) arg);
break;
case SNIOC_CFGR:
ret = hts221_config(priv, (FAR hts221_settings_t *) arg);
break;
case SNIOC_START_CONVERSION:
ret = hts221_start_conversion(priv);
break;
case SNIOC_CHECK_STATUS_REG:
ret = hts221_check_status(priv, (FAR hts221_status_t *) arg);
break;
case SNIOC_READ_RAW_DATA:
ret = hts221_read_raw_data(priv, (FAR hts221_raw_data_t *) arg);
break;
#ifdef CONFIG_HTS221_DEBUG
case SNIOC_DUMP_REGS:
ret = hts221_dump_registers(priv);
break;
#endif
case SNIOC_READ_CONVERT_DATA:
ret = hts221_read_convert_data(priv, (FAR hts221_conv_data_t *) arg);
break;
default:
ret = -ENOTTY;
break;
}
nxsem_post(&priv->devsem);
return ret;
}
#ifndef CONFIG_DISABLE_POLL
static bool hts221_sample(FAR struct hts221_dev_s *priv)
{
int ret;
hts221_status_t status =
{
.is_humid_ready = false,
.is_temp_ready = false
};
ret = hts221_check_status(priv, &status);
if (ret < 0)
{
return false;
}
return status.is_humid_ready || status.is_temp_ready;
}
static void hts221_notify(FAR struct hts221_dev_s *priv)
{
DEBUGASSERT(priv != NULL);
int i;
/* If there are threads waiting on poll() for data to become available,
* then wake them up now. NOTE: we wake up all waiting threads because we
* do not know that they are going to do. If they all try to read the data,
* then some make end up blocking after all.
*/
for (i = 0; i < CONFIG_HTS221_NPOLLWAITERS; i++)
{
FAR struct pollfd *fds = priv->fds[i];
if (fds)
{
fds->revents |= POLLIN;
hts221_dbg("Report events: %02x\n", fds->revents);
nxsem_post(fds->sem);
}
}
}
static int hts221_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup)
{
FAR struct inode *inode;
FAR struct hts221_dev_s *priv;
irqstate_t flags;
int ret;
int i;
DEBUGASSERT(filep && fds);
inode = filep->f_inode;
DEBUGASSERT(inode && inode->i_private);
priv = (FAR struct hts221_dev_s *)inode->i_private;
/* Get exclusive access */
do
{
ret = nxsem_wait(&priv->devsem);
/* The only case that an error should occur here is if the wait was
* awakened by a signal.
*/
DEBUGASSERT(ret == OK || ret == -EINTR);
}
while (ret == -EINTR);
if (setup)
{
/* Ignore waits that do not include POLLIN */
if ((fds->events & POLLIN) == 0)
{
ret = -EDEADLK;
goto out;
}
/* This is a request to set up the poll. Find an available slot for
* the poll structure reference.
*/
for (i = 0; i < CONFIG_HTS221_NPOLLWAITERS; i++)
{
/* Find an available slot */
if (!priv->fds[i])
{
/* Bind the poll structure and this slot */
priv->fds[i] = fds;
fds->priv = &priv->fds[i];
break;
}
}
if (i >= CONFIG_HTS221_NPOLLWAITERS)
{
fds->priv = NULL;
ret = -EBUSY;
goto out;
}
flags = enter_critical_section();
if (priv->int_pending || hts221_sample(priv))
{
hts221_notify(priv);
}
leave_critical_section(flags);
}
else if (fds->priv)
{
/* This is a request to tear down the poll. */
struct pollfd **slot = (struct pollfd **)fds->priv;
DEBUGASSERT(slot != NULL);
/* Remove all memory of the poll setup */
*slot = NULL;
fds->priv = NULL;
}
out:
nxsem_post(&priv->devsem);
return ret;
}
#endif /* !CONFIG_DISABLE_POLL */
static int hts221_int_handler(int irq, FAR void *context, FAR void *arg)
{
FAR struct hts221_dev_s *priv = (FAR struct hts221_dev_s *)arg;
DEBUGASSERT(priv != NULL);
priv->int_pending = true;
hts221_dbg("Hts221 interrupt\n");
#ifndef CONFIG_DISABLE_POLL
hts221_notify(priv);
#endif
return OK;
}
int hts221_register(FAR const char *devpath, FAR struct i2c_master_s *i2c,
uint8_t addr, FAR hts221_config_t *config)
{
int ret = 0;
FAR struct hts221_dev_s *priv;
priv = (struct hts221_dev_s *)kmm_zalloc(sizeof(struct hts221_dev_s));
if (!priv)
{
hts221_dbg("Memory cannot be allocated for hts221 sensor");
return -ENOMEM;
}
priv->addr = addr;
priv->i2c = i2c;
priv->config = config;
nxsem_init(&priv->devsem, 0, 1);
priv->config->set_power(priv->config, true);
ret = hts221_load_calibration_data(priv);
if (ret < 0)
{
kmm_free(priv);
hts221_dbg("Cannot calibrate hts221 sensor\n");
return -EAGAIN;
}
ret = register_driver(devpath, &g_humidityops, 0666, priv);
hts221_dbg("Registered with %d\n", ret);
if (ret < 0)
{
kmm_free(priv);
hts221_dbg("Error occurred during the driver registering\n");
return ret;
}
if (priv->config->irq_clear)
{
priv->config->irq_clear(priv->config);
}
priv->config->irq_attach(priv->config, hts221_int_handler, priv);
priv->config->irq_enable(priv->config, false);
priv->config->set_power(priv->config, false);
return OK;
}