1 /*
2  * Copyright (c) 2016 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT sensirion_sht3xd
8 
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/drivers/sensor.h>
14 #include <zephyr/sys/__assert.h>
15 #include <zephyr/sys/byteorder.h>
16 #include <zephyr/sys/crc.h>
17 #include <zephyr/logging/log.h>
18 
19 #include "sht3xd.h"
20 
21 LOG_MODULE_REGISTER(SHT3XD, CONFIG_SENSOR_LOG_LEVEL);
22 
23 #ifdef CONFIG_SHT3XD_SINGLE_SHOT_MODE
24 static const uint16_t measure_cmd[3] = {
25 	0x2416, 0x240B, 0x2400
26 };
27 #endif
28 #ifdef CONFIG_SHT3XD_PERIODIC_MODE
29 static const uint16_t measure_cmd[5][3] = {
30 	{ 0x202F, 0x2024, 0x2032 },
31 	{ 0x212D, 0x2126, 0x2130 },
32 	{ 0x222B, 0x2220, 0x2236 },
33 	{ 0x2329, 0x2322, 0x2334 },
34 	{ 0x272A, 0x2721, 0x2737 }
35 };
36 #endif
37 
38 static const int measure_wait[3] = {
39 	4000, 6000, 15000
40 };
41 
42 /*
43  * CRC algorithm parameters were taken from the
44  * "Checksum Calculation" section of the datasheet.
45  */
sht3xd_compute_crc(uint16_t value)46 static uint8_t sht3xd_compute_crc(uint16_t value)
47 {
48 	uint8_t buf[2];
49 
50 	sys_put_be16(value, buf);
51 	return crc8(buf, 2, 0x31, 0xFF, false);
52 }
53 
sht3xd_write_command(const struct device * dev,uint16_t cmd)54 int sht3xd_write_command(const struct device *dev, uint16_t cmd)
55 {
56 	const struct sht3xd_config *config = dev->config;
57 	uint8_t tx_buf[2];
58 
59 	sys_put_be16(cmd, tx_buf);
60 	return i2c_write_dt(&config->bus, tx_buf, sizeof(tx_buf));
61 }
62 
sht3xd_write_reg(const struct device * dev,uint16_t cmd,uint16_t val)63 int sht3xd_write_reg(const struct device *dev, uint16_t cmd, uint16_t val)
64 {
65 	const struct sht3xd_config *config = dev->config;
66 	uint8_t tx_buf[5];
67 
68 	sys_put_be16(cmd, &tx_buf[0]);
69 	sys_put_be16(val, &tx_buf[2]);
70 	tx_buf[4] = sht3xd_compute_crc(val);
71 
72 	return i2c_write_dt(&config->bus, tx_buf, sizeof(tx_buf));
73 }
74 
sht3xd_sample_fetch(const struct device * dev,enum sensor_channel chan)75 static int sht3xd_sample_fetch(const struct device *dev,
76 			       enum sensor_channel chan)
77 {
78 	const struct sht3xd_config *config = dev->config;
79 	struct sht3xd_data *data = dev->data;
80 	uint8_t rx_buf[6];
81 	uint16_t t_sample, rh_sample;
82 
83 	__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);
84 
85 #ifdef CONFIG_SHT3XD_SINGLE_SHOT_MODE
86 	/* start single shot measurement */
87 	if (sht3xd_write_command(dev,
88 				 measure_cmd[SHT3XD_REPEATABILITY_IDX])
89 	    < 0) {
90 		LOG_DBG("Failed to set single shot measurement mode!");
91 		return -EIO;
92 	}
93 	k_sleep(K_MSEC(measure_wait[SHT3XD_REPEATABILITY_IDX] / USEC_PER_MSEC));
94 
95 	if (i2c_read_dt(&config->bus, rx_buf, sizeof(rx_buf)) < 0) {
96 		LOG_DBG("Failed to read data sample!");
97 		return -EIO;
98 	}
99 #endif
100 #ifdef CONFIG_SHT3XD_PERIODIC_MODE
101 	uint8_t tx_buf[2];
102 
103 	sys_put_be16(SHT3XD_CMD_FETCH, tx_buf);
104 
105 	if (i2c_write_read_dt(&config->bus, tx_buf, sizeof(tx_buf),
106 			      rx_buf, sizeof(rx_buf)) < 0) {
107 		LOG_DBG("Failed to read data sample!");
108 		return -EIO;
109 	}
110 #endif
111 
112 	t_sample = sys_get_be16(&rx_buf[0]);
113 	if (sht3xd_compute_crc(t_sample) != rx_buf[2]) {
114 		LOG_DBG("Received invalid temperature CRC!");
115 		return -EIO;
116 	}
117 
118 	rh_sample = sys_get_be16(&rx_buf[3]);
119 	if (sht3xd_compute_crc(rh_sample) != rx_buf[5]) {
120 		LOG_DBG("Received invalid relative humidity CRC!");
121 		return -EIO;
122 	}
123 
124 	data->t_sample = t_sample;
125 	data->rh_sample = rh_sample;
126 
127 	return 0;
128 }
129 
sht3xd_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)130 static int sht3xd_channel_get(const struct device *dev,
131 			      enum sensor_channel chan,
132 			      struct sensor_value *val)
133 {
134 	const struct sht3xd_data *data = dev->data;
135 	uint64_t tmp;
136 
137 	/*
138 	 * See datasheet "Conversion of Signal Output" section
139 	 * for more details on processing sample data.
140 	 */
141 	if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
142 		/* val = -45 + 175 * sample / (2^16 -1) */
143 		tmp = (uint64_t)data->t_sample * 175U;
144 		val->val1 = (int32_t)(tmp / 0xFFFF) - 45;
145 		val->val2 = ((tmp % 0xFFFF) * 1000000U) / 0xFFFF;
146 	} else if (chan == SENSOR_CHAN_HUMIDITY) {
147 		/* val = 100 * sample / (2^16 -1) */
148 		uint32_t tmp2 = (uint32_t)data->rh_sample * 100U;
149 		val->val1 = tmp2 / 0xFFFF;
150 		/* x * 100000 / 65536 == x * 15625 / 1024 */
151 		val->val2 = (tmp2 % 0xFFFF) * 15625U / 1024;
152 	} else {
153 		return -ENOTSUP;
154 	}
155 
156 	return 0;
157 }
158 
159 static const struct sensor_driver_api sht3xd_driver_api = {
160 #ifdef CONFIG_SHT3XD_TRIGGER
161 	.attr_set = sht3xd_attr_set,
162 	.trigger_set = sht3xd_trigger_set,
163 #endif
164 	.sample_fetch = sht3xd_sample_fetch,
165 	.channel_get = sht3xd_channel_get,
166 };
167 
sht3xd_init(const struct device * dev)168 static int sht3xd_init(const struct device *dev)
169 {
170 	const struct sht3xd_config *cfg = dev->config;
171 
172 	if (!device_is_ready(cfg->bus.bus)) {
173 		LOG_ERR("I2C bus %s is not ready!", cfg->bus.bus->name);
174 		return -EINVAL;
175 	}
176 
177 	/* clear status register */
178 	if (sht3xd_write_command(dev, SHT3XD_CMD_CLEAR_STATUS) < 0) {
179 		LOG_DBG("Failed to clear status register!");
180 		return -EIO;
181 	}
182 
183 	k_busy_wait(SHT3XD_CLEAR_STATUS_WAIT_USEC);
184 
185 #ifdef CONFIG_SHT3XD_PERIODIC_MODE
186 	/* set periodic measurement mode */
187 	if (sht3xd_write_command(dev,
188 				 measure_cmd[SHT3XD_MPS_IDX][SHT3XD_REPEATABILITY_IDX])
189 	    < 0) {
190 		LOG_DBG("Failed to set measurement mode!");
191 		return -EIO;
192 	}
193 
194 	k_busy_wait(measure_wait[SHT3XD_REPEATABILITY_IDX]);
195 #endif
196 #ifdef CONFIG_SHT3XD_TRIGGER
197 	struct sht3xd_data *data = dev->data;
198 
199 	data->dev = dev;
200 	if (sht3xd_init_interrupt(dev) < 0) {
201 		LOG_DBG("Failed to initialize interrupt");
202 		return -EIO;
203 	}
204 #endif
205 
206 	return 0;
207 }
208 
209 #ifdef CONFIG_SHT3XD_TRIGGER
210 #define SHT3XD_TRIGGER_INIT(inst)						\
211 	.alert_gpio = GPIO_DT_SPEC_INST_GET(inst, alert_gpios),
212 #else
213 #define SHT3XD_TRIGGER_INIT(inst)
214 #endif
215 
216 #define SHT3XD_DEFINE(inst)							\
217 	struct sht3xd_data sht3xd0_data_##inst;					\
218 	static const struct sht3xd_config sht3xd0_cfg_##inst = {		\
219 		.bus = I2C_DT_SPEC_INST_GET(inst),				\
220 		SHT3XD_TRIGGER_INIT(inst)					\
221 	};									\
222 	SENSOR_DEVICE_DT_INST_DEFINE(inst, sht3xd_init, NULL,			\
223 		&sht3xd0_data_##inst, &sht3xd0_cfg_##inst,			\
224 		POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,			\
225 		&sht3xd_driver_api);
226 
227 DT_INST_FOREACH_STATUS_OKAY(SHT3XD_DEFINE)
228