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 #if defined(CONFIG_DHT_LOCK_IRQS)
71 	int lock;
72 #endif
73 
74 	__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);
75 
76 	/* assert to send start signal */
77 	gpio_pin_set_dt(&cfg->dio_gpio, true);
78 
79 #if defined(CONFIG_DHT_LOCK_IRQS)
80 	lock = irq_lock();
81 #endif
82 
83 	k_busy_wait(DHT_START_SIGNAL_DURATION);
84 
85 	/* switch to DIR_IN to read sensor signals */
86 	gpio_pin_configure_dt(&cfg->dio_gpio, GPIO_INPUT);
87 
88 	/* wait for sensor active response */
89 	if (dht_measure_signal_duration(dev, false) == -1) {
90 		ret = -EIO;
91 		goto cleanup;
92 	}
93 
94 	/* read sensor response */
95 	if (dht_measure_signal_duration(dev, true) == -1) {
96 		ret = -EIO;
97 		goto cleanup;
98 	}
99 
100 	/* wait for sensor data start */
101 	if (dht_measure_signal_duration(dev, false) == -1) {
102 		ret = -EIO;
103 		goto cleanup;
104 	}
105 
106 	/* read sensor data */
107 	for (i = 0U; i < DHT_DATA_BITS_NUM; i++) {
108 		/* Active signal to indicate a new bit */
109 		if (dht_measure_signal_duration(dev, true) == -1) {
110 			ret = -EIO;
111 			goto cleanup;
112 		}
113 
114 		/* Inactive signal duration indicates bit value */
115 		signal_duration[i] = dht_measure_signal_duration(dev, false);
116 		if (signal_duration[i] == -1) {
117 			ret = -EIO;
118 			goto cleanup;
119 		}
120 	}
121 
122 	/*
123 	 * the datasheet says 20-40us HIGH signal duration for a 0 bit and
124 	 * 80us for a 1 bit; however, since dht_measure_signal_duration is
125 	 * not very precise, compute the threshold for deciding between a
126 	 * 0 bit and a 1 bit as the average between the minimum and maximum
127 	 * if the durations stored in signal_duration
128 	 */
129 	min_duration = signal_duration[0];
130 	max_duration = signal_duration[0];
131 	for (i = 1U; i < DHT_DATA_BITS_NUM; i++) {
132 		if (min_duration > signal_duration[i]) {
133 			min_duration = signal_duration[i];
134 		}
135 		if (max_duration < signal_duration[i]) {
136 			max_duration = signal_duration[i];
137 		}
138 	}
139 	avg_duration = ((int16_t)min_duration + (int16_t)max_duration) / 2;
140 
141 	/* store bits in buf */
142 	j = 0U;
143 	(void)memset(buf, 0, sizeof(buf));
144 	for (i = 0U; i < DHT_DATA_BITS_NUM; i++) {
145 		if (signal_duration[i] >= avg_duration) {
146 			buf[j] = (buf[j] << 1) | 1;
147 		} else {
148 			buf[j] = buf[j] << 1;
149 		}
150 
151 		if (i % 8 == 7U) {
152 			j++;
153 		}
154 	}
155 
156 	/* verify checksum */
157 	if (((buf[0] + buf[1] + buf[2] + buf[3]) & 0xFF) != buf[4]) {
158 		LOG_DBG("Invalid checksum in fetched sample");
159 		ret = -EIO;
160 	} else {
161 		memcpy(drv_data->sample, buf, 4);
162 	}
163 
164 cleanup:
165 #if defined(CONFIG_DHT_LOCK_IRQS)
166 	irq_unlock(lock);
167 #endif
168 
169 	/* Switch to output inactive until next fetch. */
170 	gpio_pin_configure_dt(&cfg->dio_gpio, GPIO_OUTPUT_INACTIVE);
171 
172 	return ret;
173 }
174 
dht_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)175 static int dht_channel_get(const struct device *dev,
176 			   enum sensor_channel chan,
177 			   struct sensor_value *val)
178 {
179 	struct dht_data *drv_data = dev->data;
180 
181 	__ASSERT_NO_MSG(chan == SENSOR_CHAN_AMBIENT_TEMP
182 			|| chan == SENSOR_CHAN_HUMIDITY);
183 
184 	/* see data calculation example from datasheet */
185 	if (IS_ENABLED(DT_INST_PROP(0, dht22))) {
186 		/*
187 		 * use both integral and decimal data bytes; resulted
188 		 * 16bit data has a resolution of 0.1 units
189 		 */
190 		int16_t raw_val, sign;
191 
192 		if (chan == SENSOR_CHAN_HUMIDITY) {
193 			raw_val = (drv_data->sample[0] << 8)
194 				+ drv_data->sample[1];
195 			val->val1 = raw_val / 10;
196 			val->val2 = (raw_val % 10) * 100000;
197 		} else if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
198 			raw_val = (drv_data->sample[2] << 8)
199 				+ drv_data->sample[3];
200 
201 			sign = raw_val & 0x8000;
202 			raw_val = raw_val & ~0x8000;
203 
204 			val->val1 = raw_val / 10;
205 			val->val2 = (raw_val % 10) * 100000;
206 
207 			/* handle negative value */
208 			if (sign) {
209 				val->val1 = -val->val1;
210 				val->val2 = -val->val2;
211 			}
212 		} else {
213 			return -ENOTSUP;
214 		}
215 	} else {
216 		/* use only integral data byte */
217 		if (chan == SENSOR_CHAN_HUMIDITY) {
218 			val->val1 = drv_data->sample[0];
219 			val->val2 = 0;
220 		} else if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
221 			val->val1 = drv_data->sample[2];
222 			val->val2 = 0;
223 		} else {
224 			return -ENOTSUP;
225 		}
226 	}
227 
228 	return 0;
229 }
230 
231 static DEVICE_API(sensor, dht_api) = {
232 	.sample_fetch = &dht_sample_fetch,
233 	.channel_get = &dht_channel_get,
234 };
235 
dht_init(const struct device * dev)236 static int dht_init(const struct device *dev)
237 {
238 	int rc = 0;
239 	const struct dht_config *cfg = dev->config;
240 
241 	if (!gpio_is_ready_dt(&cfg->dio_gpio)) {
242 		LOG_ERR("GPIO device not ready");
243 		return -ENODEV;
244 	}
245 
246 	rc = gpio_pin_configure_dt(&cfg->dio_gpio, GPIO_OUTPUT_INACTIVE);
247 
248 	return rc;
249 }
250 
251 #define DHT_DEFINE(inst)								\
252 	static struct dht_data dht_data_##inst;						\
253 											\
254 	static const struct dht_config dht_config_##inst = {				\
255 		.dio_gpio = GPIO_DT_SPEC_INST_GET(inst, dio_gpios),			\
256 	};										\
257 											\
258 	SENSOR_DEVICE_DT_INST_DEFINE(inst, &dht_init, NULL,				\
259 			      &dht_data_##inst, &dht_config_##inst,			\
260 			      POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &dht_api);	\
261 
262 DT_INST_FOREACH_STATUS_OKAY(DHT_DEFINE)
263