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