drivers: sensor: shtcx: add driver supporting shtc1 and shtc3
Add driver for sensirion consumer humidity sensor line. Supports shtc1 and shtc3, but only shtc3 is tested. Signed-off-by: Thomas Stranger <thomas.stranger@outlook.com>
This commit is contained in:
parent
fa2d1ea422
commit
22c8d9973b
7 changed files with 400 additions and 0 deletions
|
@ -69,6 +69,7 @@ add_subdirectory_ifdef(CONFIG_QDEC_NRFX qdec_nrfx)
|
||||||
add_subdirectory_ifdef(CONFIG_QDEC_SAM qdec_sam)
|
add_subdirectory_ifdef(CONFIG_QDEC_SAM qdec_sam)
|
||||||
add_subdirectory_ifdef(CONFIG_TEMP_NRF5 nrf5)
|
add_subdirectory_ifdef(CONFIG_TEMP_NRF5 nrf5)
|
||||||
add_subdirectory_ifdef(CONFIG_SBS_GAUGE sbs_gauge)
|
add_subdirectory_ifdef(CONFIG_SBS_GAUGE sbs_gauge)
|
||||||
|
add_subdirectory_ifdef(CONFIG_SHTCX shtcx)
|
||||||
add_subdirectory_ifdef(CONFIG_SHT3XD sht3xd)
|
add_subdirectory_ifdef(CONFIG_SHT3XD sht3xd)
|
||||||
add_subdirectory_ifdef(CONFIG_SI7006 si7006)
|
add_subdirectory_ifdef(CONFIG_SI7006 si7006)
|
||||||
add_subdirectory_ifdef(CONFIG_SI7055 si7055)
|
add_subdirectory_ifdef(CONFIG_SI7055 si7055)
|
||||||
|
|
|
@ -178,6 +178,8 @@ source "drivers/sensor/qdec_sam/Kconfig"
|
||||||
|
|
||||||
source "drivers/sensor/sbs_gauge/Kconfig"
|
source "drivers/sensor/sbs_gauge/Kconfig"
|
||||||
|
|
||||||
|
source "drivers/sensor/shtcx/Kconfig"
|
||||||
|
|
||||||
source "drivers/sensor/sht3xd/Kconfig"
|
source "drivers/sensor/sht3xd/Kconfig"
|
||||||
|
|
||||||
source "drivers/sensor/si7006/Kconfig"
|
source "drivers/sensor/si7006/Kconfig"
|
||||||
|
|
5
drivers/sensor/shtcx/CMakeLists.txt
Normal file
5
drivers/sensor/shtcx/CMakeLists.txt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
zephyr_library()
|
||||||
|
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_SHTCX shtcx.c)
|
10
drivers/sensor/shtcx/Kconfig
Normal file
10
drivers/sensor/shtcx/Kconfig
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# SHTCX temperature and humidity sensor configuration options
|
||||||
|
|
||||||
|
# Copyright (c) 2021 Thomas Stranger
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
config SHTCX
|
||||||
|
bool "SHTCX: SHTC1 and SHTC3 Temperature and Humidity Sensor"
|
||||||
|
depends on I2C
|
||||||
|
help
|
||||||
|
Enable driver for SHTC1 and SHTC3 temperature and humidity sensors.
|
263
drivers/sensor/shtcx/shtcx.c
Normal file
263
drivers/sensor/shtcx/shtcx.c
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Thomas Stranger
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT sensirion_shtcx
|
||||||
|
|
||||||
|
#include <device.h>
|
||||||
|
#include <drivers/i2c.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <drivers/sensor.h>
|
||||||
|
#include <sys/__assert.h>
|
||||||
|
#include <sys/byteorder.h>
|
||||||
|
#include <sys/crc.h>
|
||||||
|
#include <logging/log.h>
|
||||||
|
|
||||||
|
#include "shtcx.h"
|
||||||
|
|
||||||
|
LOG_MODULE_REGISTER(SHTCX, CONFIG_SENSOR_LOG_LEVEL);
|
||||||
|
|
||||||
|
/* all cmds read temp first: cmd[MEASURE_MODE][Clock_stretching_enabled] */
|
||||||
|
static const uint16_t measure_cmd[2][2] = {
|
||||||
|
{ 0x7866, 0x7CA2 },
|
||||||
|
{ 0x609C, 0x6458 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* measure_wait_us[shtcx_chip][MEASURE_MODE] */
|
||||||
|
static const uint16_t measure_wait_us[2][2] = {
|
||||||
|
/* shtc3: 12.1ms, 0.8ms */
|
||||||
|
{ 1210, 800 }, /* shtc3 */
|
||||||
|
/* shtc1: 14.4ms, 0.94ms */
|
||||||
|
{ 14400, 940 }, /* shtc1 */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CRC algorithm parameters were taken from the
|
||||||
|
* "Checksum Calculation" section of the datasheet.
|
||||||
|
*/
|
||||||
|
static uint8_t shtcx_compute_crc(uint16_t value)
|
||||||
|
{
|
||||||
|
uint8_t buf[2];
|
||||||
|
|
||||||
|
sys_put_be16(value, buf);
|
||||||
|
return crc8(buf, 2, 0x31, 0xFF, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* val = -45 + 175 * sample / (2^16) */
|
||||||
|
static void shtcx_temperature_from_raw(uint16_t raw, struct sensor_value *val)
|
||||||
|
{
|
||||||
|
int32_t tmp;
|
||||||
|
|
||||||
|
tmp = (int32_t)raw * 175U - (45 << 16);
|
||||||
|
val->val1 = tmp / 0x10000;
|
||||||
|
/* x * 1.000.000 / 65.536 == x * 15625 / 2^10 */
|
||||||
|
val->val2 = ((tmp % 0x10000) * 15625U) / 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* val = 100 * sample / (2^16) */
|
||||||
|
static void shtcx_humidity_from_raw(uint16_t raw, struct sensor_value *val)
|
||||||
|
{
|
||||||
|
uint32_t tmp;
|
||||||
|
|
||||||
|
tmp = (uint32_t)raw * 100U;
|
||||||
|
val->val1 = tmp / 0x10000;
|
||||||
|
/* x * 1.000.000 / 65.536 == x * 15625 / 1024 */
|
||||||
|
val->val2 = (tmp % 0x10000) * 15625U / 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int shtcx_write_command(const struct device *dev, uint16_t cmd)
|
||||||
|
{
|
||||||
|
uint8_t tx_buf[2];
|
||||||
|
|
||||||
|
sys_put_be16(cmd, tx_buf);
|
||||||
|
return i2c_write(shtcx_i2c_bus(dev), tx_buf, sizeof(tx_buf),
|
||||||
|
shtcx_i2c_address(dev));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int shtcx_read_words(const struct device *dev, uint16_t cmd, uint16_t *data,
|
||||||
|
uint16_t num_words, uint16_t max_duration_us)
|
||||||
|
{
|
||||||
|
const struct shtcx_config *cfg = dev->config;
|
||||||
|
int status = 0;
|
||||||
|
uint32_t raw_len = num_words * (SHTCX_WORD_LEN + SHTCX_CRC8_LEN);
|
||||||
|
uint16_t temp16;
|
||||||
|
uint8_t rx_buf[SHTCX_MAX_READ_LEN];
|
||||||
|
int dst = 0;
|
||||||
|
|
||||||
|
status = shtcx_write_command(dev, cmd);
|
||||||
|
if (status != 0) {
|
||||||
|
LOG_DBG("Failed to initiate read");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cfg->clock_stretching) {
|
||||||
|
k_sleep(K_USEC(max_duration_us));
|
||||||
|
}
|
||||||
|
|
||||||
|
status = i2c_read(shtcx_i2c_bus(dev), rx_buf, raw_len,
|
||||||
|
shtcx_i2c_address(dev));
|
||||||
|
if (status != 0) {
|
||||||
|
LOG_DBG("Failed to read data");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < raw_len; i += (SHTCX_WORD_LEN + SHTCX_CRC8_LEN)) {
|
||||||
|
temp16 = sys_get_be16(&rx_buf[i]);
|
||||||
|
if (shtcx_compute_crc(temp16) != rx_buf[i+2]) {
|
||||||
|
LOG_DBG("invalid received invalid crc");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
data[dst++] = temp16;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int shtcx_sleep(const struct device *dev)
|
||||||
|
{
|
||||||
|
if (shtcx_write_command(dev, SHTCX_CMD_SLEEP) < 0) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int shtcx_wakeup(const struct device *dev)
|
||||||
|
{
|
||||||
|
if (shtcx_write_command(dev, SHTCX_CMD_WAKEUP)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_sleep(K_USEC(100));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int shtcx_sample_fetch(const struct device *dev,
|
||||||
|
enum sensor_channel chan)
|
||||||
|
{
|
||||||
|
struct shtcx_data *data = dev->data;
|
||||||
|
const struct shtcx_config *cfg = dev->config;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);
|
||||||
|
|
||||||
|
if (cfg->chip == SHTC3) {
|
||||||
|
if (shtcx_wakeup(dev)) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shtcx_read_words(dev,
|
||||||
|
measure_cmd[cfg->measure_mode][cfg->clock_stretching],
|
||||||
|
(uint16_t *)&data->sample, 2,
|
||||||
|
measure_wait_us[cfg->chip][cfg->measure_mode]) < 0) {
|
||||||
|
LOG_DBG("Failed read measurements!");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg->chip == SHTC3) {
|
||||||
|
if (shtcx_sleep(dev)) {
|
||||||
|
LOG_DBG("Failed to initiate sleep");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int shtcx_channel_get(const struct device *dev,
|
||||||
|
enum sensor_channel chan,
|
||||||
|
struct sensor_value *val)
|
||||||
|
{
|
||||||
|
const struct shtcx_data *data = dev->data;
|
||||||
|
|
||||||
|
if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
|
||||||
|
shtcx_temperature_from_raw(data->sample.temp, val);
|
||||||
|
} else if (chan == SENSOR_CHAN_HUMIDITY) {
|
||||||
|
shtcx_humidity_from_raw(data->sample.temp, val);
|
||||||
|
} else {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct sensor_driver_api shtcx_driver_api = {
|
||||||
|
.sample_fetch = shtcx_sample_fetch,
|
||||||
|
.channel_get = shtcx_channel_get,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int shtcx_init(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct shtcx_config *cfg = dev->config;
|
||||||
|
uint16_t product_id;
|
||||||
|
|
||||||
|
if (device_is_ready(cfg->bus) == 0) {
|
||||||
|
LOG_DBG("i2c bus is not ready");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_sleep(K_USEC(SHTCX_POWER_UP_TIME_US));
|
||||||
|
if (cfg->chip == SHTC3) {
|
||||||
|
if (shtcx_wakeup(dev)) {
|
||||||
|
LOG_ERR("Wakeup failed");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shtcx_write_command(dev, SHTCX_CMD_SOFT_RESET) < 0) {
|
||||||
|
LOG_ERR("soft reset failed");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_sleep(K_USEC(SHTCX_SOFT_RESET_TIME_US));
|
||||||
|
if (shtcx_read_words(dev, SHTCX_CMD_READ_ID, &product_id, 1, 0) < 0) {
|
||||||
|
LOG_ERR("Failed to read product id!");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg->chip == SHTC1) {
|
||||||
|
if ((product_id & SHTC1_ID_MASK) != SHTC1_ID_VALUE) {
|
||||||
|
LOG_ERR("Device is not a SHTC1");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cfg->chip == SHTC3) {
|
||||||
|
if ((product_id & SHTC3_ID_MASK) != SHTC3_ID_VALUE) {
|
||||||
|
LOG_ERR("Device is not a SHTC3");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
shtcx_sleep(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Clock-stretching enabled: %d", cfg->clock_stretching);
|
||||||
|
LOG_DBG("Measurement mode: %d", cfg->measure_mode);
|
||||||
|
LOG_DBG("Init SHTCX");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SHTCX_CONFIG(inst) \
|
||||||
|
{ \
|
||||||
|
.bus = DEVICE_DT_GET(DT_INST_BUS(inst)), \
|
||||||
|
.base_address = DT_INST_REG_ADDR(inst), \
|
||||||
|
.chip = DT_ENUM_IDX(DT_DRV_INST(inst), chip), \
|
||||||
|
.measure_mode = DT_ENUM_IDX(DT_DRV_INST(inst), measure_mode), \
|
||||||
|
.clock_stretching = DT_INST_PROP(inst, clock_stretching) \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SHTCX_DEFINE(inst) \
|
||||||
|
static struct shtcx_data shtcx_data_##inst; \
|
||||||
|
static struct shtcx_config shtcx_config_##inst = \
|
||||||
|
SHTCX_CONFIG(inst); \
|
||||||
|
DEVICE_DT_INST_DEFINE(inst, \
|
||||||
|
shtcx_init, \
|
||||||
|
NULL, \
|
||||||
|
&shtcx_data_##inst, \
|
||||||
|
&shtcx_config_##inst, \
|
||||||
|
POST_KERNEL, \
|
||||||
|
CONFIG_SENSOR_INIT_PRIORITY, \
|
||||||
|
&shtcx_driver_api);
|
||||||
|
|
||||||
|
DT_INST_FOREACH_STATUS_OKAY(SHTCX_DEFINE)
|
82
drivers/sensor/shtcx/shtcx.h
Normal file
82
drivers/sensor/shtcx/shtcx.h
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Thomas Stranger
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_DRIVERS_SENSOR_SHTCX_SHTCX_H_
|
||||||
|
#define ZEPHYR_DRIVERS_SENSOR_SHTCX_SHTCX_H_
|
||||||
|
|
||||||
|
#include <device.h>
|
||||||
|
#include <devicetree.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <drivers/gpio.h>
|
||||||
|
|
||||||
|
/* common cmds */
|
||||||
|
#define SHTCX_CMD_READ_ID 0xEFC8
|
||||||
|
#define SHTCX_CMD_SOFT_RESET 0x805D
|
||||||
|
/* shtc3 only: */
|
||||||
|
#define SHTCX_CMD_SLEEP 0xB098
|
||||||
|
#define SHTCX_CMD_WAKEUP 0x3517
|
||||||
|
|
||||||
|
#define SHTCX_POWER_UP_TIME_US 240U
|
||||||
|
/* Soft reset time is 230us for shtc1 and 240us for shtc3 */
|
||||||
|
#define SHTCX_SOFT_RESET_TIME_US 240U
|
||||||
|
|
||||||
|
#define SHTCX_MAX_READ_LEN 6
|
||||||
|
#define SHTCX_WORD_LEN 2
|
||||||
|
#define SHTCX_CRC8_LEN 1
|
||||||
|
|
||||||
|
#define SHTC3_ID_MASK 0x083F
|
||||||
|
#define SHTC3_ID_VALUE 0x0807
|
||||||
|
#define SHTC1_ID_MASK 0x083F
|
||||||
|
#define SHTC1_ID_VALUE 0x0007
|
||||||
|
|
||||||
|
/* defines matching the related enums DT_ENUM_IDX: */
|
||||||
|
#define CHIP_SHTC1 0
|
||||||
|
#define CHIP_SHTC3 1
|
||||||
|
#define MEASURE_MODE_NORMAL 0
|
||||||
|
#define MEASURE_MODE_LOW_POWER 1
|
||||||
|
|
||||||
|
enum shtcx_chip {
|
||||||
|
SHTC1 = CHIP_SHTC1,
|
||||||
|
SHTC3 = CHIP_SHTC3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum shtcx_measure_mode {
|
||||||
|
NORMAL = MEASURE_MODE_NORMAL,
|
||||||
|
LOW_POWER = MEASURE_MODE_LOW_POWER,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct shtcx_sample {
|
||||||
|
uint16_t temp;
|
||||||
|
uint16_t humidity;
|
||||||
|
} __packed __aligned(2);
|
||||||
|
|
||||||
|
struct shtcx_config {
|
||||||
|
const struct device *bus;
|
||||||
|
uint8_t base_address;
|
||||||
|
enum shtcx_chip chip;
|
||||||
|
enum shtcx_measure_mode measure_mode;
|
||||||
|
bool clock_stretching;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct shtcx_data {
|
||||||
|
struct shtcx_sample sample;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline uint8_t shtcx_i2c_address(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct shtcx_config *dcp = dev->config;
|
||||||
|
|
||||||
|
return dcp->base_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const struct device *shtcx_i2c_bus(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct shtcx_config *dcp = dev->config;
|
||||||
|
|
||||||
|
return dcp->bus;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_DRIVERS_SENSOR_SHTCX_SHTCX_H_ */
|
37
dts/bindings/sensor/sensirion,shtcx.yaml
Normal file
37
dts/bindings/sensor/sensirion,shtcx.yaml
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# Copyright (c) 2021, Thomas Stranger
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
description: Sensirion SHTCx humidity and temperature sensor
|
||||||
|
|
||||||
|
compatible: "sensirion,shtcx"
|
||||||
|
|
||||||
|
include: i2c-device.yaml
|
||||||
|
|
||||||
|
properties:
|
||||||
|
chip:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: |
|
||||||
|
Specifies which chip exactly is used. This is necessary to get exact
|
||||||
|
timing information and supported command set.
|
||||||
|
SHTC3 has an additional sleep mode that is entered between measurements.
|
||||||
|
enum:
|
||||||
|
- "shtc1"
|
||||||
|
- "shtc3"
|
||||||
|
|
||||||
|
measure-mode:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: |
|
||||||
|
Specifies which measurement mode is used.
|
||||||
|
SHTC1 sensor only supports the normal measuremnt mode.
|
||||||
|
enum:
|
||||||
|
- "normal"
|
||||||
|
- "low-power"
|
||||||
|
|
||||||
|
clock-stretching:
|
||||||
|
type: boolean
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
Specifies that the driver should clock stretching i2c communication to
|
||||||
|
read the sensor values.
|
Loading…
Add table
Add a link
Reference in a new issue