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