Skip to content

Commit 22c8d99

Browse files
str4t0mcfriedt
authored andcommitted
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 <[email protected]>
1 parent fa2d1ea commit 22c8d99

File tree

7 files changed

+400
-0
lines changed

7 files changed

+400
-0
lines changed

drivers/sensor/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ add_subdirectory_ifdef(CONFIG_QDEC_NRFX qdec_nrfx)
6969
add_subdirectory_ifdef(CONFIG_QDEC_SAM qdec_sam)
7070
add_subdirectory_ifdef(CONFIG_TEMP_NRF5 nrf5)
7171
add_subdirectory_ifdef(CONFIG_SBS_GAUGE sbs_gauge)
72+
add_subdirectory_ifdef(CONFIG_SHTCX shtcx)
7273
add_subdirectory_ifdef(CONFIG_SHT3XD sht3xd)
7374
add_subdirectory_ifdef(CONFIG_SI7006 si7006)
7475
add_subdirectory_ifdef(CONFIG_SI7055 si7055)

drivers/sensor/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ source "drivers/sensor/qdec_sam/Kconfig"
178178

179179
source "drivers/sensor/sbs_gauge/Kconfig"
180180

181+
source "drivers/sensor/shtcx/Kconfig"
182+
181183
source "drivers/sensor/sht3xd/Kconfig"
182184

183185
source "drivers/sensor/si7006/Kconfig"

drivers/sensor/shtcx/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
zephyr_library()
4+
5+
zephyr_library_sources_ifdef(CONFIG_SHTCX shtcx.c)

drivers/sensor/shtcx/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# SHTCX temperature and humidity sensor configuration options
2+
3+
# Copyright (c) 2021 Thomas Stranger
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
config SHTCX
7+
bool "SHTCX: SHTC1 and SHTC3 Temperature and Humidity Sensor"
8+
depends on I2C
9+
help
10+
Enable driver for SHTC1 and SHTC3 temperature and humidity sensors.

drivers/sensor/shtcx/shtcx.c

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
/*
2+
* Copyright (c) 2021 Thomas Stranger
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT sensirion_shtcx
8+
9+
#include <device.h>
10+
#include <drivers/i2c.h>
11+
#include <kernel.h>
12+
#include <drivers/sensor.h>
13+
#include <sys/__assert.h>
14+
#include <sys/byteorder.h>
15+
#include <sys/crc.h>
16+
#include <logging/log.h>
17+
18+
#include "shtcx.h"
19+
20+
LOG_MODULE_REGISTER(SHTCX, CONFIG_SENSOR_LOG_LEVEL);
21+
22+
/* all cmds read temp first: cmd[MEASURE_MODE][Clock_stretching_enabled] */
23+
static const uint16_t measure_cmd[2][2] = {
24+
{ 0x7866, 0x7CA2 },
25+
{ 0x609C, 0x6458 },
26+
};
27+
28+
/* measure_wait_us[shtcx_chip][MEASURE_MODE] */
29+
static const uint16_t measure_wait_us[2][2] = {
30+
/* shtc3: 12.1ms, 0.8ms */
31+
{ 1210, 800 }, /* shtc3 */
32+
/* shtc1: 14.4ms, 0.94ms */
33+
{ 14400, 940 }, /* shtc1 */
34+
};
35+
36+
/*
37+
* CRC algorithm parameters were taken from the
38+
* "Checksum Calculation" section of the datasheet.
39+
*/
40+
static uint8_t shtcx_compute_crc(uint16_t value)
41+
{
42+
uint8_t buf[2];
43+
44+
sys_put_be16(value, buf);
45+
return crc8(buf, 2, 0x31, 0xFF, false);
46+
}
47+
48+
/* val = -45 + 175 * sample / (2^16) */
49+
static void shtcx_temperature_from_raw(uint16_t raw, struct sensor_value *val)
50+
{
51+
int32_t tmp;
52+
53+
tmp = (int32_t)raw * 175U - (45 << 16);
54+
val->val1 = tmp / 0x10000;
55+
/* x * 1.000.000 / 65.536 == x * 15625 / 2^10 */
56+
val->val2 = ((tmp % 0x10000) * 15625U) / 1024;
57+
}
58+
59+
/* val = 100 * sample / (2^16) */
60+
static void shtcx_humidity_from_raw(uint16_t raw, struct sensor_value *val)
61+
{
62+
uint32_t tmp;
63+
64+
tmp = (uint32_t)raw * 100U;
65+
val->val1 = tmp / 0x10000;
66+
/* x * 1.000.000 / 65.536 == x * 15625 / 1024 */
67+
val->val2 = (tmp % 0x10000) * 15625U / 1024;
68+
}
69+
70+
static int shtcx_write_command(const struct device *dev, uint16_t cmd)
71+
{
72+
uint8_t tx_buf[2];
73+
74+
sys_put_be16(cmd, tx_buf);
75+
return i2c_write(shtcx_i2c_bus(dev), tx_buf, sizeof(tx_buf),
76+
shtcx_i2c_address(dev));
77+
}
78+
79+
static int shtcx_read_words(const struct device *dev, uint16_t cmd, uint16_t *data,
80+
uint16_t num_words, uint16_t max_duration_us)
81+
{
82+
const struct shtcx_config *cfg = dev->config;
83+
int status = 0;
84+
uint32_t raw_len = num_words * (SHTCX_WORD_LEN + SHTCX_CRC8_LEN);
85+
uint16_t temp16;
86+
uint8_t rx_buf[SHTCX_MAX_READ_LEN];
87+
int dst = 0;
88+
89+
status = shtcx_write_command(dev, cmd);
90+
if (status != 0) {
91+
LOG_DBG("Failed to initiate read");
92+
return -EIO;
93+
}
94+
95+
if (!cfg->clock_stretching) {
96+
k_sleep(K_USEC(max_duration_us));
97+
}
98+
99+
status = i2c_read(shtcx_i2c_bus(dev), rx_buf, raw_len,
100+
shtcx_i2c_address(dev));
101+
if (status != 0) {
102+
LOG_DBG("Failed to read data");
103+
return -EIO;
104+
}
105+
106+
for (int i = 0; i < raw_len; i += (SHTCX_WORD_LEN + SHTCX_CRC8_LEN)) {
107+
temp16 = sys_get_be16(&rx_buf[i]);
108+
if (shtcx_compute_crc(temp16) != rx_buf[i+2]) {
109+
LOG_DBG("invalid received invalid crc");
110+
return -EIO;
111+
}
112+
113+
data[dst++] = temp16;
114+
}
115+
116+
return 0;
117+
}
118+
119+
static int shtcx_sleep(const struct device *dev)
120+
{
121+
if (shtcx_write_command(dev, SHTCX_CMD_SLEEP) < 0) {
122+
return -EIO;
123+
}
124+
125+
return 0;
126+
}
127+
128+
static int shtcx_wakeup(const struct device *dev)
129+
{
130+
if (shtcx_write_command(dev, SHTCX_CMD_WAKEUP)) {
131+
return -EIO;
132+
}
133+
134+
k_sleep(K_USEC(100));
135+
return 0;
136+
}
137+
138+
static int shtcx_sample_fetch(const struct device *dev,
139+
enum sensor_channel chan)
140+
{
141+
struct shtcx_data *data = dev->data;
142+
const struct shtcx_config *cfg = dev->config;
143+
144+
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);
145+
146+
if (cfg->chip == SHTC3) {
147+
if (shtcx_wakeup(dev)) {
148+
return -EIO;
149+
}
150+
}
151+
152+
if (shtcx_read_words(dev,
153+
measure_cmd[cfg->measure_mode][cfg->clock_stretching],
154+
(uint16_t *)&data->sample, 2,
155+
measure_wait_us[cfg->chip][cfg->measure_mode]) < 0) {
156+
LOG_DBG("Failed read measurements!");
157+
return -EIO;
158+
}
159+
160+
if (cfg->chip == SHTC3) {
161+
if (shtcx_sleep(dev)) {
162+
LOG_DBG("Failed to initiate sleep");
163+
return -EIO;
164+
}
165+
}
166+
167+
return 0;
168+
}
169+
170+
static int shtcx_channel_get(const struct device *dev,
171+
enum sensor_channel chan,
172+
struct sensor_value *val)
173+
{
174+
const struct shtcx_data *data = dev->data;
175+
176+
if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
177+
shtcx_temperature_from_raw(data->sample.temp, val);
178+
} else if (chan == SENSOR_CHAN_HUMIDITY) {
179+
shtcx_humidity_from_raw(data->sample.temp, val);
180+
} else {
181+
return -ENOTSUP;
182+
}
183+
184+
return 0;
185+
}
186+
187+
static const struct sensor_driver_api shtcx_driver_api = {
188+
.sample_fetch = shtcx_sample_fetch,
189+
.channel_get = shtcx_channel_get,
190+
};
191+
192+
static int shtcx_init(const struct device *dev)
193+
{
194+
const struct shtcx_config *cfg = dev->config;
195+
uint16_t product_id;
196+
197+
if (device_is_ready(cfg->bus) == 0) {
198+
LOG_DBG("i2c bus is not ready");
199+
return -ENODEV;
200+
}
201+
202+
k_sleep(K_USEC(SHTCX_POWER_UP_TIME_US));
203+
if (cfg->chip == SHTC3) {
204+
if (shtcx_wakeup(dev)) {
205+
LOG_ERR("Wakeup failed");
206+
return -EIO;
207+
}
208+
}
209+
210+
if (shtcx_write_command(dev, SHTCX_CMD_SOFT_RESET) < 0) {
211+
LOG_ERR("soft reset failed");
212+
return -EIO;
213+
}
214+
215+
k_sleep(K_USEC(SHTCX_SOFT_RESET_TIME_US));
216+
if (shtcx_read_words(dev, SHTCX_CMD_READ_ID, &product_id, 1, 0) < 0) {
217+
LOG_ERR("Failed to read product id!");
218+
return -EIO;
219+
}
220+
221+
if (cfg->chip == SHTC1) {
222+
if ((product_id & SHTC1_ID_MASK) != SHTC1_ID_VALUE) {
223+
LOG_ERR("Device is not a SHTC1");
224+
return -EINVAL;
225+
}
226+
}
227+
if (cfg->chip == SHTC3) {
228+
if ((product_id & SHTC3_ID_MASK) != SHTC3_ID_VALUE) {
229+
LOG_ERR("Device is not a SHTC3");
230+
return -EINVAL;
231+
}
232+
shtcx_sleep(dev);
233+
}
234+
235+
LOG_DBG("Clock-stretching enabled: %d", cfg->clock_stretching);
236+
LOG_DBG("Measurement mode: %d", cfg->measure_mode);
237+
LOG_DBG("Init SHTCX");
238+
return 0;
239+
}
240+
241+
#define SHTCX_CONFIG(inst) \
242+
{ \
243+
.bus = DEVICE_DT_GET(DT_INST_BUS(inst)), \
244+
.base_address = DT_INST_REG_ADDR(inst), \
245+
.chip = DT_ENUM_IDX(DT_DRV_INST(inst), chip), \
246+
.measure_mode = DT_ENUM_IDX(DT_DRV_INST(inst), measure_mode), \
247+
.clock_stretching = DT_INST_PROP(inst, clock_stretching) \
248+
}
249+
250+
#define SHTCX_DEFINE(inst) \
251+
static struct shtcx_data shtcx_data_##inst; \
252+
static struct shtcx_config shtcx_config_##inst = \
253+
SHTCX_CONFIG(inst); \
254+
DEVICE_DT_INST_DEFINE(inst, \
255+
shtcx_init, \
256+
NULL, \
257+
&shtcx_data_##inst, \
258+
&shtcx_config_##inst, \
259+
POST_KERNEL, \
260+
CONFIG_SENSOR_INIT_PRIORITY, \
261+
&shtcx_driver_api);
262+
263+
DT_INST_FOREACH_STATUS_OKAY(SHTCX_DEFINE)

drivers/sensor/shtcx/shtcx.h

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright (c) 2021 Thomas Stranger
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef ZEPHYR_DRIVERS_SENSOR_SHTCX_SHTCX_H_
8+
#define ZEPHYR_DRIVERS_SENSOR_SHTCX_SHTCX_H_
9+
10+
#include <device.h>
11+
#include <devicetree.h>
12+
#include <kernel.h>
13+
#include <drivers/gpio.h>
14+
15+
/* common cmds */
16+
#define SHTCX_CMD_READ_ID 0xEFC8
17+
#define SHTCX_CMD_SOFT_RESET 0x805D
18+
/* shtc3 only: */
19+
#define SHTCX_CMD_SLEEP 0xB098
20+
#define SHTCX_CMD_WAKEUP 0x3517
21+
22+
#define SHTCX_POWER_UP_TIME_US 240U
23+
/* Soft reset time is 230us for shtc1 and 240us for shtc3 */
24+
#define SHTCX_SOFT_RESET_TIME_US 240U
25+
26+
#define SHTCX_MAX_READ_LEN 6
27+
#define SHTCX_WORD_LEN 2
28+
#define SHTCX_CRC8_LEN 1
29+
30+
#define SHTC3_ID_MASK 0x083F
31+
#define SHTC3_ID_VALUE 0x0807
32+
#define SHTC1_ID_MASK 0x083F
33+
#define SHTC1_ID_VALUE 0x0007
34+
35+
/* defines matching the related enums DT_ENUM_IDX: */
36+
#define CHIP_SHTC1 0
37+
#define CHIP_SHTC3 1
38+
#define MEASURE_MODE_NORMAL 0
39+
#define MEASURE_MODE_LOW_POWER 1
40+
41+
enum shtcx_chip {
42+
SHTC1 = CHIP_SHTC1,
43+
SHTC3 = CHIP_SHTC3,
44+
};
45+
46+
enum shtcx_measure_mode {
47+
NORMAL = MEASURE_MODE_NORMAL,
48+
LOW_POWER = MEASURE_MODE_LOW_POWER,
49+
};
50+
51+
struct shtcx_sample {
52+
uint16_t temp;
53+
uint16_t humidity;
54+
} __packed __aligned(2);
55+
56+
struct shtcx_config {
57+
const struct device *bus;
58+
uint8_t base_address;
59+
enum shtcx_chip chip;
60+
enum shtcx_measure_mode measure_mode;
61+
bool clock_stretching;
62+
};
63+
64+
struct shtcx_data {
65+
struct shtcx_sample sample;
66+
};
67+
68+
static inline uint8_t shtcx_i2c_address(const struct device *dev)
69+
{
70+
const struct shtcx_config *dcp = dev->config;
71+
72+
return dcp->base_address;
73+
}
74+
75+
static inline const struct device *shtcx_i2c_bus(const struct device *dev)
76+
{
77+
const struct shtcx_config *dcp = dev->config;
78+
79+
return dcp->bus;
80+
}
81+
82+
#endif /* ZEPHYR_DRIVERS_SENSOR_SHTCX_SHTCX_H_ */

0 commit comments

Comments
 (0)