You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
269 lines
6.8 KiB
269 lines
6.8 KiB
/* |
|
* Copyright (c) 2016 Intel Corporation |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT aosong_dht |
|
|
|
#include <zephyr/device.h> |
|
#include <zephyr/drivers/gpio.h> |
|
#include <zephyr/sys/byteorder.h> |
|
#include <zephyr/sys/util.h> |
|
#include <zephyr/drivers/sensor.h> |
|
#include <string.h> |
|
#include <zephyr/kernel.h> |
|
#include <zephyr/logging/log.h> |
|
|
|
#include "dht.h" |
|
|
|
LOG_MODULE_REGISTER(DHT, CONFIG_SENSOR_LOG_LEVEL); |
|
|
|
/** |
|
* @brief Measure duration of signal send by sensor |
|
* |
|
* @param drv_data Pointer to the driver data structure |
|
* @param active Whether current signal is active |
|
* |
|
* @return duration in usec of signal being measured, |
|
* -1 if duration exceeds DHT_SIGNAL_MAX_WAIT_DURATION |
|
*/ |
|
static int8_t dht_measure_signal_duration(const struct device *dev, |
|
bool active) |
|
{ |
|
const struct dht_config *cfg = dev->config; |
|
uint32_t elapsed_cycles; |
|
uint32_t max_wait_cycles = (uint32_t)( |
|
(uint64_t)DHT_SIGNAL_MAX_WAIT_DURATION * |
|
(uint64_t)sys_clock_hw_cycles_per_sec() / |
|
(uint64_t)USEC_PER_SEC |
|
); |
|
uint32_t start_cycles = k_cycle_get_32(); |
|
int rc; |
|
|
|
do { |
|
rc = gpio_pin_get_dt(&cfg->dio_gpio); |
|
elapsed_cycles = k_cycle_get_32() - start_cycles; |
|
|
|
if ((rc < 0) |
|
|| (elapsed_cycles > max_wait_cycles)) { |
|
return -1; |
|
} |
|
} while ((bool)rc == active); |
|
|
|
return (uint64_t)elapsed_cycles * |
|
(uint64_t)USEC_PER_SEC / |
|
(uint64_t)sys_clock_hw_cycles_per_sec(); |
|
} |
|
|
|
static int dht_sample_fetch(const struct device *dev, |
|
enum sensor_channel chan) |
|
{ |
|
struct dht_data *drv_data = dev->data; |
|
const struct dht_config *cfg = dev->config; |
|
int ret = 0; |
|
int8_t signal_duration[DHT_DATA_BITS_NUM]; |
|
int8_t max_duration, min_duration, avg_duration; |
|
uint8_t buf[5]; |
|
unsigned int i, j; |
|
|
|
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL); |
|
|
|
#if defined(CONFIG_DHT_LOCK_IRQS) |
|
/* Get the lock before any pin interaction since irq_lock has a */ |
|
/* potential of causing delays itself. */ |
|
int lock = irq_lock(); |
|
#endif |
|
|
|
/* assert to send start signal */ |
|
gpio_pin_set_dt(&cfg->dio_gpio, true); |
|
|
|
k_busy_wait(DHT_START_SIGNAL_DURATION); |
|
|
|
/* set the state back to logic LOW (voltage HIGH) so that the */ |
|
/* subsequent call to dht_measure_signal duration waits for the */ |
|
/* DHT sensor to set it back to HIGH (voltage LOW). */ |
|
/* Failure to do this would cause that subsequent call to */ |
|
/* return immediately. */ |
|
gpio_pin_set_dt(&cfg->dio_gpio, false); |
|
|
|
/* switch to DIR_IN to read sensor signals */ |
|
gpio_pin_configure_dt(&cfg->dio_gpio, GPIO_INPUT); |
|
|
|
/* wait for sensor active response */ |
|
if (dht_measure_signal_duration(dev, false) == -1) { |
|
ret = -EIO; |
|
goto cleanup; |
|
} |
|
|
|
/* read sensor response */ |
|
if (dht_measure_signal_duration(dev, true) == -1) { |
|
ret = -EIO; |
|
goto cleanup; |
|
} |
|
|
|
/* wait for sensor data start */ |
|
if (dht_measure_signal_duration(dev, false) == -1) { |
|
ret = -EIO; |
|
goto cleanup; |
|
} |
|
|
|
/* read sensor data */ |
|
for (i = 0U; i < DHT_DATA_BITS_NUM; i++) { |
|
/* Active signal to indicate a new bit */ |
|
if (dht_measure_signal_duration(dev, true) == -1) { |
|
ret = -EIO; |
|
goto cleanup; |
|
} |
|
|
|
/* Inactive signal duration indicates bit value */ |
|
signal_duration[i] = dht_measure_signal_duration(dev, false); |
|
if (signal_duration[i] == -1) { |
|
ret = -EIO; |
|
goto cleanup; |
|
} |
|
} |
|
|
|
/* |
|
* the datasheet says 20-40us HIGH signal duration for a 0 bit and |
|
* 80us for a 1 bit; however, since dht_measure_signal_duration is |
|
* not very precise, compute the threshold for deciding between a |
|
* 0 bit and a 1 bit as the average between the minimum and maximum |
|
* if the durations stored in signal_duration |
|
*/ |
|
min_duration = signal_duration[0]; |
|
max_duration = signal_duration[0]; |
|
for (i = 1U; i < DHT_DATA_BITS_NUM; i++) { |
|
if (min_duration > signal_duration[i]) { |
|
min_duration = signal_duration[i]; |
|
} |
|
if (max_duration < signal_duration[i]) { |
|
max_duration = signal_duration[i]; |
|
} |
|
} |
|
avg_duration = ((int16_t)min_duration + (int16_t)max_duration) / 2; |
|
|
|
/* store bits in buf */ |
|
j = 0U; |
|
(void)memset(buf, 0, sizeof(buf)); |
|
for (i = 0U; i < DHT_DATA_BITS_NUM; i++) { |
|
if (signal_duration[i] >= avg_duration) { |
|
buf[j] = (buf[j] << 1) | 1; |
|
} else { |
|
buf[j] = buf[j] << 1; |
|
} |
|
|
|
if (i % 8 == 7U) { |
|
j++; |
|
} |
|
} |
|
|
|
/* verify checksum */ |
|
if (((buf[0] + buf[1] + buf[2] + buf[3]) & 0xFF) != buf[4]) { |
|
LOG_DBG("Invalid checksum in fetched sample"); |
|
ret = -EIO; |
|
} else { |
|
memcpy(drv_data->sample, buf, 4); |
|
} |
|
|
|
cleanup: |
|
#if defined(CONFIG_DHT_LOCK_IRQS) |
|
irq_unlock(lock); |
|
#endif |
|
|
|
/* Switch to output inactive until next fetch. */ |
|
gpio_pin_configure_dt(&cfg->dio_gpio, GPIO_OUTPUT_INACTIVE); |
|
|
|
return ret; |
|
} |
|
|
|
static int dht_channel_get(const struct device *dev, |
|
enum sensor_channel chan, |
|
struct sensor_value *val) |
|
{ |
|
struct dht_data *drv_data = dev->data; |
|
const struct dht_config *cfg = dev->config; |
|
|
|
__ASSERT_NO_MSG(chan == SENSOR_CHAN_AMBIENT_TEMP |
|
|| chan == SENSOR_CHAN_HUMIDITY); |
|
|
|
/* see data calculation example from datasheet */ |
|
if (cfg->is_dht22) { |
|
/* |
|
* use both integral and decimal data bytes; resulted |
|
* 16bit data has a resolution of 0.1 units |
|
*/ |
|
int16_t raw_val, sign; |
|
|
|
if (chan == SENSOR_CHAN_HUMIDITY) { |
|
raw_val = (drv_data->sample[0] << 8) |
|
+ drv_data->sample[1]; |
|
val->val1 = raw_val / 10; |
|
val->val2 = (raw_val % 10) * 100000; |
|
} else if (chan == SENSOR_CHAN_AMBIENT_TEMP) { |
|
raw_val = (drv_data->sample[2] << 8) |
|
+ drv_data->sample[3]; |
|
|
|
sign = raw_val & 0x8000; |
|
raw_val = raw_val & ~0x8000; |
|
|
|
val->val1 = raw_val / 10; |
|
val->val2 = (raw_val % 10) * 100000; |
|
|
|
/* handle negative value */ |
|
if (sign) { |
|
val->val1 = -val->val1; |
|
val->val2 = -val->val2; |
|
} |
|
} else { |
|
return -ENOTSUP; |
|
} |
|
} else { |
|
/* use only integral data byte */ |
|
if (chan == SENSOR_CHAN_HUMIDITY) { |
|
val->val1 = drv_data->sample[0]; |
|
val->val2 = 0; |
|
} else if (chan == SENSOR_CHAN_AMBIENT_TEMP) { |
|
val->val1 = drv_data->sample[2]; |
|
val->val2 = 0; |
|
} else { |
|
return -ENOTSUP; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static DEVICE_API(sensor, dht_api) = { |
|
.sample_fetch = &dht_sample_fetch, |
|
.channel_get = &dht_channel_get, |
|
}; |
|
|
|
static int dht_init(const struct device *dev) |
|
{ |
|
int rc = 0; |
|
const struct dht_config *cfg = dev->config; |
|
|
|
if (!gpio_is_ready_dt(&cfg->dio_gpio)) { |
|
LOG_ERR("GPIO device not ready"); |
|
return -ENODEV; |
|
} |
|
|
|
rc = gpio_pin_configure_dt(&cfg->dio_gpio, GPIO_OUTPUT_INACTIVE); |
|
|
|
return rc; |
|
} |
|
|
|
#define DHT_DEFINE(inst) \ |
|
static struct dht_data dht_data_##inst; \ |
|
\ |
|
static const struct dht_config dht_config_##inst = { \ |
|
.dio_gpio = GPIO_DT_SPEC_INST_GET(inst, dio_gpios), \ |
|
.is_dht22 = DT_INST_PROP(inst, dht22), \ |
|
}; \ |
|
\ |
|
SENSOR_DEVICE_DT_INST_DEFINE(inst, &dht_init, NULL, \ |
|
&dht_data_##inst, &dht_config_##inst, \ |
|
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &dht_api); \ |
|
|
|
DT_INST_FOREACH_STATUS_OKAY(DHT_DEFINE)
|
|
|