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 <zephyr/device.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/drivers/sensor.h>
13 #include <zephyr/sys/__assert.h>
14 #include <zephyr/sys/byteorder.h>
15 #include <zephyr/sys/crc.h>
16 #include <zephyr/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 	/* shtc1: 14.4ms, 0.94ms */
31 	{ 14400, 940 }, /* shtc1 */
32 	/* shtc3: 12.1ms, 0.8ms */
33 	{ 12100, 800 }, /* shtc3 */
34 };
35 
36 /*
37  * CRC algorithm parameters were taken from the
38  * "Checksum Calculation" section of the datasheet.
39  */
shtcx_compute_crc(uint16_t value)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) */
shtcx_temperature_from_raw(uint16_t raw,struct sensor_value * val)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) * 15625) / 1024;
57 }
58 
59 /* val = 100 * sample / (2^16) */
shtcx_humidity_from_raw(uint16_t raw,struct sensor_value * val)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 
shtcx_write_command(const struct device * dev,uint16_t cmd)70 static int shtcx_write_command(const struct device *dev, uint16_t cmd)
71 {
72 	const struct shtcx_config *cfg = dev->config;
73 	uint8_t tx_buf[2];
74 
75 	sys_put_be16(cmd, tx_buf);
76 	return i2c_write_dt(&cfg->i2c, tx_buf, sizeof(tx_buf));
77 }
78 
shtcx_read_words(const struct device * dev,uint16_t cmd,uint16_t * data,uint16_t num_words,uint16_t max_duration_us)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_dt(&cfg->i2c, rx_buf, raw_len);
100 	if (status != 0) {
101 		LOG_DBG("Failed to read data");
102 		return -EIO;
103 	}
104 
105 	for (int i = 0; i < raw_len; i += (SHTCX_WORD_LEN + SHTCX_CRC8_LEN)) {
106 		temp16 = sys_get_be16(&rx_buf[i]);
107 		if (shtcx_compute_crc(temp16) != rx_buf[i+2]) {
108 			LOG_DBG("invalid received invalid crc");
109 			return -EIO;
110 		}
111 
112 		data[dst++] = temp16;
113 	}
114 
115 	return 0;
116 }
117 
shtcx_sleep(const struct device * dev)118 static int shtcx_sleep(const struct device *dev)
119 {
120 	if (shtcx_write_command(dev, SHTCX_CMD_SLEEP) < 0) {
121 		return -EIO;
122 	}
123 
124 	return 0;
125 }
126 
shtcx_wakeup(const struct device * dev)127 static int shtcx_wakeup(const struct device *dev)
128 {
129 	if (shtcx_write_command(dev, SHTCX_CMD_WAKEUP)) {
130 		return -EIO;
131 	}
132 
133 	k_sleep(K_USEC(100));
134 	return 0;
135 }
136 
shtcx_sample_fetch(const struct device * dev,enum sensor_channel chan)137 static int shtcx_sample_fetch(const struct device *dev,
138 			       enum sensor_channel chan)
139 {
140 	struct shtcx_data *data = dev->data;
141 	const struct shtcx_config *cfg = dev->config;
142 
143 	__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);
144 
145 	if (cfg->chip == SHTC3) {
146 		if (shtcx_wakeup(dev)) {
147 			return -EIO;
148 		}
149 	}
150 
151 	if (shtcx_read_words(dev,
152 			     measure_cmd[cfg->measure_mode][cfg->clock_stretching],
153 			     (uint16_t *)&data->sample, 2,
154 			     measure_wait_us[cfg->chip][cfg->measure_mode]) < 0) {
155 		LOG_DBG("Failed read measurements!");
156 		return -EIO;
157 	}
158 
159 	if (cfg->chip == SHTC3) {
160 		if (shtcx_sleep(dev)) {
161 			LOG_DBG("Failed to initiate sleep");
162 			return -EIO;
163 		}
164 	}
165 
166 	return 0;
167 }
168 
shtcx_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)169 static int shtcx_channel_get(const struct device *dev,
170 			      enum sensor_channel chan,
171 			      struct sensor_value *val)
172 {
173 	const struct shtcx_data *data = dev->data;
174 
175 	if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
176 		shtcx_temperature_from_raw(data->sample.temp, val);
177 	} else if (chan == SENSOR_CHAN_HUMIDITY) {
178 		shtcx_humidity_from_raw(data->sample.humidity, val);
179 	} else {
180 		return -ENOTSUP;
181 	}
182 
183 	return 0;
184 }
185 
186 static DEVICE_API(sensor, shtcx_driver_api) = {
187 	.sample_fetch = shtcx_sample_fetch,
188 	.channel_get = shtcx_channel_get,
189 };
190 
shtcx_init(const struct device * dev)191 static int shtcx_init(const struct device *dev)
192 {
193 	const struct shtcx_config *cfg = dev->config;
194 	uint16_t product_id;
195 
196 	if (!device_is_ready(cfg->i2c.bus)) {
197 		LOG_ERR("Bus device is not ready");
198 		return -ENODEV;
199 	}
200 
201 	k_sleep(K_USEC(SHTCX_POWER_UP_TIME_US));
202 	if (cfg->chip == SHTC3) {
203 		if (shtcx_wakeup(dev)) {
204 			LOG_ERR("Wakeup failed");
205 			return -EIO;
206 		}
207 	}
208 
209 	if (shtcx_write_command(dev, SHTCX_CMD_SOFT_RESET) < 0) {
210 		LOG_ERR("soft reset failed");
211 		return -EIO;
212 	}
213 
214 	k_sleep(K_USEC(SHTCX_SOFT_RESET_TIME_US));
215 	if (shtcx_read_words(dev, SHTCX_CMD_READ_ID, &product_id, 1, 0) < 0) {
216 		LOG_ERR("Failed to read product id!");
217 		return -EIO;
218 	}
219 
220 	if (cfg->chip == SHTC1) {
221 		if ((product_id & SHTC1_ID_MASK) != SHTC1_ID_VALUE) {
222 			LOG_ERR("Device is not a SHTC1");
223 			return -EINVAL;
224 		}
225 	}
226 	if (cfg->chip == SHTC3) {
227 		if ((product_id & SHTC3_ID_MASK) != SHTC3_ID_VALUE) {
228 			LOG_ERR("Device is not a SHTC3");
229 			return -EINVAL;
230 		}
231 		shtcx_sleep(dev);
232 	}
233 
234 	LOG_DBG("Clock-stretching enabled: %d", cfg->clock_stretching);
235 	LOG_DBG("Measurement mode: %d", cfg->measure_mode);
236 	LOG_DBG("Init SHTCX");
237 	return 0;
238 }
239 
240 #define SHTCX_CHIP(inst) \
241 	(DT_INST_NODE_HAS_COMPAT(inst, sensirion_shtc1) ? CHIP_SHTC1 : CHIP_SHTC3)
242 
243 #define SHTCX_CONFIG(inst)						       \
244 	{								       \
245 		.i2c = I2C_DT_SPEC_INST_GET(inst),			       \
246 		.chip = SHTCX_CHIP(inst),				       \
247 		.measure_mode = DT_INST_ENUM_IDX(inst, measure_mode),	       \
248 		.clock_stretching = DT_INST_PROP(inst, clock_stretching)       \
249 	}
250 
251 #define SHTCX_DEFINE(inst)						\
252 	static struct shtcx_data shtcx_data_##inst;			\
253 	static struct shtcx_config shtcx_config_##inst =		\
254 		SHTCX_CONFIG(inst);					\
255 	SENSOR_DEVICE_DT_INST_DEFINE(inst,				\
256 			      shtcx_init,				\
257 			      NULL,					\
258 			      &shtcx_data_##inst,			\
259 			      &shtcx_config_##inst,			\
260 			      POST_KERNEL,				\
261 			      CONFIG_SENSOR_INIT_PRIORITY,		\
262 			      &shtcx_driver_api);
263 
264 DT_INST_FOREACH_STATUS_OKAY(SHTCX_DEFINE)
265