2016-05-24 17:09:22 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2016 Intel Corporation
|
|
|
|
*
|
2017-01-19 09:01:01 +08:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2016-05-24 17:09:22 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <i2c.h>
|
|
|
|
#include <init.h>
|
|
|
|
#include <misc/__assert.h>
|
|
|
|
#include <misc/byteorder.h>
|
|
|
|
#include <sensor.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2016-10-15 20:19:56 +08:00
|
|
|
#include "hts221.h"
|
2016-05-24 17:09:22 +08:00
|
|
|
|
|
|
|
static int hts221_channel_get(struct device *dev,
|
|
|
|
enum sensor_channel chan,
|
|
|
|
struct sensor_value *val)
|
|
|
|
{
|
|
|
|
struct hts221_data *drv_data = dev->driver_data;
|
2017-04-21 23:03:20 +08:00
|
|
|
s32_t conv_val;
|
2016-05-24 17:09:22 +08:00
|
|
|
|
2018-02-01 00:35:44 +08:00
|
|
|
__ASSERT_NO_MSG(chan == SENSOR_CHAN_TEMP ||
|
|
|
|
chan == SENSOR_CHAN_HUMIDITY);
|
2016-05-24 17:09:22 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* see "Interpreting humidity and temperature readings" document
|
|
|
|
* for more details
|
|
|
|
*/
|
|
|
|
if (chan == SENSOR_CHAN_TEMP) {
|
2017-04-21 23:03:20 +08:00
|
|
|
conv_val = (s32_t)(drv_data->t1_degc_x8 -
|
2016-05-24 17:09:22 +08:00
|
|
|
drv_data->t0_degc_x8) *
|
|
|
|
(drv_data->t_sample - drv_data->t0_out) /
|
|
|
|
(drv_data->t1_out - drv_data->t0_out) +
|
|
|
|
drv_data->t0_degc_x8;
|
|
|
|
|
|
|
|
/* convert temperature x8 to degrees Celsius */
|
|
|
|
val->val1 = conv_val / 8;
|
|
|
|
val->val2 = (conv_val % 8) * (1000000 / 8);
|
|
|
|
} else { /* SENSOR_CHAN_HUMIDITY */
|
2017-04-21 23:03:20 +08:00
|
|
|
conv_val = (s32_t)(drv_data->h1_rh_x2 - drv_data->h0_rh_x2) *
|
2016-05-24 17:09:22 +08:00
|
|
|
(drv_data->rh_sample - drv_data->h0_t0_out) /
|
|
|
|
(drv_data->h1_t0_out - drv_data->h0_t0_out) +
|
|
|
|
drv_data->h0_rh_x2;
|
|
|
|
|
sensors: Redefine SENSOR_CHAN_HUMIDITY in percents, not milli-percents.
Based on the discussion in #5693, the reason why humidity was defined
in milli-percent was likely following Linux which defines it as such
in its sensor subsystem:
http://elixir.free-electrons.com/linux/latest/source/Documentation/ABI/testing/sysfs-bus-iio#L263
However, Linux defines temperature in milli-degrees either, but
Zephyr uses degrees (similarly for most other quantities). Typical
sensor resolution/precision for humidity is also on the order of 1%.
One of the existing drivers, th02.c, already returned values in
percents, and few apps showed it without conversion and/or units,
leading to confusing output to user like "54500".
So, switching units to percents, and update all the drivers and
sample apps.
For few drivers, there was also optimized conversion arithmetics
to avoid u64_t operations. (There're probably more places to
optimize it, and temperature conversion could use such optimization
too, but that's left for another patch.)
Fixes: #5693
Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
2018-01-25 21:06:17 +08:00
|
|
|
/* convert humidity x2 to percent */
|
|
|
|
val->val1 = conv_val / 2;
|
|
|
|
val->val2 = (conv_val % 2) * 500000;
|
2016-05-24 17:09:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hts221_sample_fetch(struct device *dev, enum sensor_channel chan)
|
|
|
|
{
|
|
|
|
struct hts221_data *drv_data = dev->driver_data;
|
2017-04-21 23:03:20 +08:00
|
|
|
u8_t buf[4];
|
2016-05-24 17:09:22 +08:00
|
|
|
|
2016-07-28 07:56:08 +08:00
|
|
|
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);
|
2016-05-24 17:09:22 +08:00
|
|
|
|
|
|
|
if (i2c_burst_read(drv_data->i2c, HTS221_I2C_ADDR,
|
|
|
|
HTS221_REG_DATA_START | HTS221_AUTOINCREMENT_ADDR,
|
|
|
|
buf, 4) < 0) {
|
|
|
|
SYS_LOG_ERR("Failed to fetch data sample.");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
drv_data->rh_sample = sys_le16_to_cpu(buf[0] | (buf[1] << 8));
|
|
|
|
drv_data->t_sample = sys_le16_to_cpu(buf[2] | (buf[3] << 8));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hts221_read_conversion_data(struct hts221_data *drv_data)
|
|
|
|
{
|
2017-04-21 23:03:20 +08:00
|
|
|
u8_t buf[16];
|
2016-05-24 17:09:22 +08:00
|
|
|
|
|
|
|
if (i2c_burst_read(drv_data->i2c, HTS221_I2C_ADDR,
|
|
|
|
HTS221_REG_CONVERSION_START |
|
|
|
|
HTS221_AUTOINCREMENT_ADDR, buf, 16) < 0) {
|
|
|
|
SYS_LOG_ERR("Failed to read conversion data.");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
drv_data->h0_rh_x2 = buf[0];
|
|
|
|
drv_data->h1_rh_x2 = buf[1];
|
|
|
|
drv_data->t0_degc_x8 = sys_le16_to_cpu(buf[2] | ((buf[5] & 0x3) << 8));
|
|
|
|
drv_data->t1_degc_x8 = sys_le16_to_cpu(buf[3] | ((buf[5] & 0xC) << 6));
|
|
|
|
drv_data->h0_t0_out = sys_le16_to_cpu(buf[6] | (buf[7] << 8));
|
|
|
|
drv_data->h1_t0_out = sys_le16_to_cpu(buf[10] | (buf[11] << 8));
|
|
|
|
drv_data->t0_out = sys_le16_to_cpu(buf[12] | (buf[13] << 8));
|
|
|
|
drv_data->t1_out = sys_le16_to_cpu(buf[14] | (buf[15] << 8));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-10-24 15:32:32 +08:00
|
|
|
static const struct sensor_driver_api hts221_driver_api = {
|
2016-05-24 17:09:22 +08:00
|
|
|
#if CONFIG_HTS221_TRIGGER
|
|
|
|
.trigger_set = hts221_trigger_set,
|
|
|
|
#endif
|
|
|
|
.sample_fetch = hts221_sample_fetch,
|
|
|
|
.channel_get = hts221_channel_get,
|
|
|
|
};
|
|
|
|
|
|
|
|
int hts221_init(struct device *dev)
|
|
|
|
{
|
|
|
|
struct hts221_data *drv_data = dev->driver_data;
|
2017-04-21 23:03:20 +08:00
|
|
|
u8_t id, idx;
|
2016-05-24 17:09:22 +08:00
|
|
|
|
|
|
|
drv_data->i2c = device_get_binding(CONFIG_HTS221_I2C_MASTER_DEV_NAME);
|
|
|
|
if (drv_data->i2c == NULL) {
|
|
|
|
SYS_LOG_ERR("Could not get pointer to %s device.",
|
|
|
|
CONFIG_HTS221_I2C_MASTER_DEV_NAME);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check chip ID */
|
|
|
|
if (i2c_reg_read_byte(drv_data->i2c, HTS221_I2C_ADDR,
|
|
|
|
HTS221_REG_WHO_AM_I, &id) < 0) {
|
|
|
|
SYS_LOG_ERR("Failed to read chip ID.");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (id != HTS221_CHIP_ID) {
|
|
|
|
SYS_LOG_ERR("Invalid chip ID.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if CONFIG_HTS221_ODR is valid */
|
|
|
|
for (idx = 0; idx < ARRAY_SIZE(hts221_odr_strings); idx++) {
|
|
|
|
if (!strcmp(hts221_odr_strings[idx], CONFIG_HTS221_ODR)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (idx == ARRAY_SIZE(hts221_odr_strings)) {
|
|
|
|
SYS_LOG_ERR("Invalid ODR value.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i2c_reg_write_byte(drv_data->i2c, HTS221_I2C_ADDR, HTS221_REG_CTRL1,
|
|
|
|
(idx + 1) << HTS221_ODR_SHIFT | HTS221_BDU_BIT |
|
|
|
|
HTS221_PD_BIT) < 0) {
|
|
|
|
SYS_LOG_ERR("Failed to configure chip.");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hts221_read_conversion_data(drv_data) < 0) {
|
|
|
|
SYS_LOG_ERR("Failed to read conversion data.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_HTS221_TRIGGER
|
|
|
|
if (hts221_init_interrupt(dev) < 0) {
|
|
|
|
SYS_LOG_ERR("Failed to initialize interrupt.");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
dev->driver_api = &hts221_driver_api;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct hts221_data hts221_driver;
|
|
|
|
|
|
|
|
DEVICE_INIT(hts221, CONFIG_HTS221_NAME, hts221_init, &hts221_driver,
|
2016-11-09 03:06:55 +08:00
|
|
|
NULL, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY);
|