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