253 lines
6.2 KiB
C
253 lines
6.2 KiB
C
/*
|
|
* Copyright (c) 2016 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT aosong_dht
|
|
|
|
#include <device.h>
|
|
#include <drivers/gpio.h>
|
|
#include <sys/byteorder.h>
|
|
#include <sys/util.h>
|
|
#include <drivers/sensor.h>
|
|
#include <string.h>
|
|
#include <zephyr.h>
|
|
#include <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)
|
|
{
|
|
struct dht_data *drv_data = dev->data;
|
|
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(drv_data->gpio, cfg->pin);
|
|
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);
|
|
|
|
/* assert to send start signal */
|
|
gpio_pin_set(drv_data->gpio, cfg->pin, true);
|
|
|
|
k_busy_wait(DHT_START_SIGNAL_DURATION);
|
|
|
|
gpio_pin_set(drv_data->gpio, cfg->pin, false);
|
|
|
|
/* switch to DIR_IN to read sensor signals */
|
|
gpio_pin_configure(drv_data->gpio, cfg->pin,
|
|
GPIO_INPUT | cfg->flags);
|
|
|
|
/* 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:
|
|
/* Switch to output inactive until next fetch. */
|
|
gpio_pin_configure(drv_data->gpio, cfg->pin,
|
|
GPIO_OUTPUT_INACTIVE | cfg->flags);
|
|
|
|
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;
|
|
|
|
__ASSERT_NO_MSG(chan == SENSOR_CHAN_AMBIENT_TEMP
|
|
|| chan == SENSOR_CHAN_HUMIDITY);
|
|
|
|
/* see data calculation example from datasheet */
|
|
if (IS_ENABLED(DT_INST_PROP(0, 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 { /* 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 {
|
|
/* use only integral data byte */
|
|
if (chan == SENSOR_CHAN_HUMIDITY) {
|
|
val->val1 = drv_data->sample[0];
|
|
val->val2 = 0;
|
|
} else { /* chan == SENSOR_CHAN_AMBIENT_TEMP */
|
|
val->val1 = drv_data->sample[2];
|
|
val->val2 = 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct sensor_driver_api dht_api = {
|
|
.sample_fetch = &dht_sample_fetch,
|
|
.channel_get = &dht_channel_get,
|
|
};
|
|
|
|
static int dht_init(const struct device *dev)
|
|
{
|
|
int rc = 0;
|
|
struct dht_data *drv_data = dev->data;
|
|
const struct dht_config *cfg = dev->config;
|
|
|
|
drv_data->gpio = device_get_binding(cfg->ctrl);
|
|
if (drv_data->gpio == NULL) {
|
|
LOG_ERR("Failed to get GPIO device %s.", cfg->ctrl);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = gpio_pin_configure(drv_data->gpio, cfg->pin,
|
|
GPIO_OUTPUT_INACTIVE | cfg->flags);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static struct dht_data dht_data;
|
|
static const struct dht_config dht_config = {
|
|
.ctrl = DT_INST_GPIO_LABEL(0, dio_gpios),
|
|
.flags = DT_INST_GPIO_FLAGS(0, dio_gpios),
|
|
.pin = DT_INST_GPIO_PIN(0, dio_gpios),
|
|
};
|
|
|
|
DEVICE_DT_INST_DEFINE(0, &dht_init, device_pm_control_nop,
|
|
&dht_data, &dht_config,
|
|
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &dht_api);
|