1 /*
2 * Copyright (c) 2016 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT aosong_dht
8
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/gpio.h>
11 #include <zephyr/sys/byteorder.h>
12 #include <zephyr/sys/util.h>
13 #include <zephyr/drivers/sensor.h>
14 #include <string.h>
15 #include <zephyr/kernel.h>
16 #include <zephyr/logging/log.h>
17
18 #include "dht.h"
19
20 LOG_MODULE_REGISTER(DHT, CONFIG_SENSOR_LOG_LEVEL);
21
22 /**
23 * @brief Measure duration of signal send by sensor
24 *
25 * @param drv_data Pointer to the driver data structure
26 * @param active Whether current signal is active
27 *
28 * @return duration in usec of signal being measured,
29 * -1 if duration exceeds DHT_SIGNAL_MAX_WAIT_DURATION
30 */
dht_measure_signal_duration(const struct device * dev,bool active)31 static int8_t dht_measure_signal_duration(const struct device *dev,
32 bool active)
33 {
34 const struct dht_config *cfg = dev->config;
35 uint32_t elapsed_cycles;
36 uint32_t max_wait_cycles = (uint32_t)(
37 (uint64_t)DHT_SIGNAL_MAX_WAIT_DURATION *
38 (uint64_t)sys_clock_hw_cycles_per_sec() /
39 (uint64_t)USEC_PER_SEC
40 );
41 uint32_t start_cycles = k_cycle_get_32();
42 int rc;
43
44 do {
45 rc = gpio_pin_get_dt(&cfg->dio_gpio);
46 elapsed_cycles = k_cycle_get_32() - start_cycles;
47
48 if ((rc < 0)
49 || (elapsed_cycles > max_wait_cycles)) {
50 return -1;
51 }
52 } while ((bool)rc == active);
53
54 return (uint64_t)elapsed_cycles *
55 (uint64_t)USEC_PER_SEC /
56 (uint64_t)sys_clock_hw_cycles_per_sec();
57 }
58
dht_sample_fetch(const struct device * dev,enum sensor_channel chan)59 static int dht_sample_fetch(const struct device *dev,
60 enum sensor_channel chan)
61 {
62 struct dht_data *drv_data = dev->data;
63 const struct dht_config *cfg = dev->config;
64 int ret = 0;
65 int8_t signal_duration[DHT_DATA_BITS_NUM];
66 int8_t max_duration, min_duration, avg_duration;
67 uint8_t buf[5];
68 unsigned int i, j;
69
70 __ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);
71
72 #if defined(CONFIG_DHT_LOCK_IRQS)
73 /* Get the lock before any pin interaction since irq_lock has a */
74 /* potential of causing delays itself. */
75 int lock = irq_lock();
76 #endif
77
78 /* assert to send start signal */
79 gpio_pin_set_dt(&cfg->dio_gpio, true);
80
81 k_busy_wait(DHT_START_SIGNAL_DURATION);
82
83 /* set the state back to logic LOW (voltage HIGH) so that the */
84 /* subsequent call to dht_measure_signal duration waits for the */
85 /* DHT sensor to set it back to HIGH (voltage LOW). */
86 /* Failure to do this would cause that subsequent call to */
87 /* return immediately. */
88 gpio_pin_set_dt(&cfg->dio_gpio, false);
89
90 /* switch to DIR_IN to read sensor signals */
91 gpio_pin_configure_dt(&cfg->dio_gpio, GPIO_INPUT);
92
93 /* wait for sensor active response */
94 if (dht_measure_signal_duration(dev, false) == -1) {
95 ret = -EIO;
96 goto cleanup;
97 }
98
99 /* read sensor response */
100 if (dht_measure_signal_duration(dev, true) == -1) {
101 ret = -EIO;
102 goto cleanup;
103 }
104
105 /* wait for sensor data start */
106 if (dht_measure_signal_duration(dev, false) == -1) {
107 ret = -EIO;
108 goto cleanup;
109 }
110
111 /* read sensor data */
112 for (i = 0U; i < DHT_DATA_BITS_NUM; i++) {
113 /* Active signal to indicate a new bit */
114 if (dht_measure_signal_duration(dev, true) == -1) {
115 ret = -EIO;
116 goto cleanup;
117 }
118
119 /* Inactive signal duration indicates bit value */
120 signal_duration[i] = dht_measure_signal_duration(dev, false);
121 if (signal_duration[i] == -1) {
122 ret = -EIO;
123 goto cleanup;
124 }
125 }
126
127 /*
128 * the datasheet says 20-40us HIGH signal duration for a 0 bit and
129 * 80us for a 1 bit; however, since dht_measure_signal_duration is
130 * not very precise, compute the threshold for deciding between a
131 * 0 bit and a 1 bit as the average between the minimum and maximum
132 * if the durations stored in signal_duration
133 */
134 min_duration = signal_duration[0];
135 max_duration = signal_duration[0];
136 for (i = 1U; i < DHT_DATA_BITS_NUM; i++) {
137 if (min_duration > signal_duration[i]) {
138 min_duration = signal_duration[i];
139 }
140 if (max_duration < signal_duration[i]) {
141 max_duration = signal_duration[i];
142 }
143 }
144 avg_duration = ((int16_t)min_duration + (int16_t)max_duration) / 2;
145
146 /* store bits in buf */
147 j = 0U;
148 (void)memset(buf, 0, sizeof(buf));
149 for (i = 0U; i < DHT_DATA_BITS_NUM; i++) {
150 if (signal_duration[i] >= avg_duration) {
151 buf[j] = (buf[j] << 1) | 1;
152 } else {
153 buf[j] = buf[j] << 1;
154 }
155
156 if (i % 8 == 7U) {
157 j++;
158 }
159 }
160
161 /* verify checksum */
162 if (((buf[0] + buf[1] + buf[2] + buf[3]) & 0xFF) != buf[4]) {
163 LOG_DBG("Invalid checksum in fetched sample");
164 ret = -EIO;
165 } else {
166 memcpy(drv_data->sample, buf, 4);
167 }
168
169 cleanup:
170 #if defined(CONFIG_DHT_LOCK_IRQS)
171 irq_unlock(lock);
172 #endif
173
174 /* Switch to output inactive until next fetch. */
175 gpio_pin_configure_dt(&cfg->dio_gpio, GPIO_OUTPUT_INACTIVE);
176
177 return ret;
178 }
179
dht_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)180 static int dht_channel_get(const struct device *dev,
181 enum sensor_channel chan,
182 struct sensor_value *val)
183 {
184 struct dht_data *drv_data = dev->data;
185 const struct dht_config *cfg = dev->config;
186
187 __ASSERT_NO_MSG(chan == SENSOR_CHAN_AMBIENT_TEMP
188 || chan == SENSOR_CHAN_HUMIDITY);
189
190 /* see data calculation example from datasheet */
191 if (cfg->is_dht22) {
192 /*
193 * use both integral and decimal data bytes; resulted
194 * 16bit data has a resolution of 0.1 units
195 */
196 int16_t raw_val, sign;
197
198 if (chan == SENSOR_CHAN_HUMIDITY) {
199 raw_val = (drv_data->sample[0] << 8)
200 + drv_data->sample[1];
201 val->val1 = raw_val / 10;
202 val->val2 = (raw_val % 10) * 100000;
203 } else if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
204 raw_val = (drv_data->sample[2] << 8)
205 + drv_data->sample[3];
206
207 sign = raw_val & 0x8000;
208 raw_val = raw_val & ~0x8000;
209
210 val->val1 = raw_val / 10;
211 val->val2 = (raw_val % 10) * 100000;
212
213 /* handle negative value */
214 if (sign) {
215 val->val1 = -val->val1;
216 val->val2 = -val->val2;
217 }
218 } else {
219 return -ENOTSUP;
220 }
221 } else {
222 /* use only integral data byte */
223 if (chan == SENSOR_CHAN_HUMIDITY) {
224 val->val1 = drv_data->sample[0];
225 val->val2 = 0;
226 } else if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
227 val->val1 = drv_data->sample[2];
228 val->val2 = 0;
229 } else {
230 return -ENOTSUP;
231 }
232 }
233
234 return 0;
235 }
236
237 static DEVICE_API(sensor, dht_api) = {
238 .sample_fetch = &dht_sample_fetch,
239 .channel_get = &dht_channel_get,
240 };
241
dht_init(const struct device * dev)242 static int dht_init(const struct device *dev)
243 {
244 int rc = 0;
245 const struct dht_config *cfg = dev->config;
246
247 if (!gpio_is_ready_dt(&cfg->dio_gpio)) {
248 LOG_ERR("GPIO device not ready");
249 return -ENODEV;
250 }
251
252 rc = gpio_pin_configure_dt(&cfg->dio_gpio, GPIO_OUTPUT_INACTIVE);
253
254 return rc;
255 }
256
257 #define DHT_DEFINE(inst) \
258 static struct dht_data dht_data_##inst; \
259 \
260 static const struct dht_config dht_config_##inst = { \
261 .dio_gpio = GPIO_DT_SPEC_INST_GET(inst, dio_gpios), \
262 .is_dht22 = DT_INST_PROP(inst, dht22), \
263 }; \
264 \
265 SENSOR_DEVICE_DT_INST_DEFINE(inst, &dht_init, NULL, \
266 &dht_data_##inst, &dht_config_##inst, \
267 POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &dht_api); \
268
269 DT_INST_FOREACH_STATUS_OKAY(DHT_DEFINE)
270