mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #11876 from maribu/dht
drivers/dht: Bugfixes for functionality, error handling, and documentation
This commit is contained in:
commit
a90cc36eb4
@ -38,10 +38,47 @@
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
/* Every pulse send by the DHT longer than 40µs is interpreted as 1 */
|
||||
#define PULSE_WIDTH_THRESHOLD (40U)
|
||||
/* If an expected pulse is not detected within 1000µs, something is wrong */
|
||||
#define TIMEOUT (1000U)
|
||||
/* The DHT sensor cannot measure more than once a second */
|
||||
#define DATA_HOLD_TIME (US_PER_SEC)
|
||||
/* The start signal by pulling data low for at least 18ms and then up for
|
||||
* 20-40µs*/
|
||||
#define START_LOW_TIME (20U * US_PER_MS)
|
||||
#define START_HIGH_TIME (40U)
|
||||
|
||||
static uint16_t read(gpio_t pin, int bits)
|
||||
|
||||
static inline void _reset(dht_t *dev)
|
||||
{
|
||||
gpio_init(dev->params.pin, GPIO_OUT);
|
||||
gpio_set(dev->params.pin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wait until the pin @p pin has level @p expect
|
||||
*
|
||||
* @param pin GPIO pin to wait for
|
||||
* @param expect Wait until @p pin has this logic level
|
||||
* @param timeout Timeout in µs
|
||||
*
|
||||
* @retval 0 Success
|
||||
* @retval -1 Timeout occurred before level was reached
|
||||
*/
|
||||
static inline int _wait_for_level(gpio_t pin, bool expect, unsigned timeout)
|
||||
{
|
||||
while (((gpio_read(pin) > 0) != expect) && timeout) {
|
||||
xtimer_usleep(1);
|
||||
timeout--;
|
||||
}
|
||||
|
||||
return (timeout > 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int _read(uint16_t *dest, gpio_t pin, int bits)
|
||||
{
|
||||
DEBUG("read\n");
|
||||
uint16_t res = 0;
|
||||
|
||||
for (int i = 0; i < bits; i++) {
|
||||
@ -49,16 +86,24 @@ static uint16_t read(gpio_t pin, int bits)
|
||||
res <<= 1;
|
||||
/* measure the length between the next rising and falling flanks (the
|
||||
* time the pin is high - smoke up :-) */
|
||||
while (!gpio_read(pin)) {}
|
||||
if (_wait_for_level(pin, 1, TIMEOUT)) {
|
||||
return -1;
|
||||
}
|
||||
start = xtimer_now_usec();
|
||||
while (gpio_read(pin)) {}
|
||||
|
||||
if (_wait_for_level(pin, 0, TIMEOUT)) {
|
||||
return -1;
|
||||
}
|
||||
end = xtimer_now_usec();
|
||||
|
||||
/* if the high phase was more than 40us, we got a 1 */
|
||||
if ((end - start) > PULSE_WIDTH_THRESHOLD) {
|
||||
res |= 0x0001;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
|
||||
*dest = res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dht_init(dht_t *dev, const dht_params_t *params)
|
||||
@ -66,13 +111,13 @@ int dht_init(dht_t *dev, const dht_params_t *params)
|
||||
DEBUG("dht_init\n");
|
||||
|
||||
/* check parameters and configuration */
|
||||
assert(dev && params &&
|
||||
((dev->type == DHT11) || (dev->type == DHT22) || (dev->type == DHT21)));
|
||||
assert(dev && params);
|
||||
assert((params->type == DHT11) || (params->type == DHT22) || (params->type == DHT21));
|
||||
|
||||
*dev = *params;
|
||||
memset(dev, 0, sizeof(dht_t));
|
||||
dev->params = *params;
|
||||
|
||||
gpio_init(dev->pin, GPIO_OUT);
|
||||
gpio_set(dev->pin);
|
||||
_reset(dev);
|
||||
|
||||
xtimer_usleep(2000 * US_PER_MS);
|
||||
|
||||
@ -80,66 +125,99 @@ int dht_init(dht_t *dev, const dht_params_t *params)
|
||||
return DHT_OK;
|
||||
}
|
||||
|
||||
int dht_read(const dht_t *dev, int16_t *temp, int16_t *hum)
|
||||
int dht_read(dht_t *dev, int16_t *temp, int16_t *hum)
|
||||
{
|
||||
uint8_t csum, sum;
|
||||
uint16_t csum;
|
||||
uint16_t raw_hum, raw_temp;
|
||||
|
||||
assert(dev && temp && hum);
|
||||
assert(dev);
|
||||
|
||||
/* send init signal to device */
|
||||
gpio_clear(dev->pin);
|
||||
xtimer_usleep(20 * US_PER_MS);
|
||||
gpio_set(dev->pin);
|
||||
xtimer_usleep(40);
|
||||
uint32_t now_us = xtimer_now_usec();
|
||||
if ((now_us - dev->last_read_us) > DATA_HOLD_TIME) {
|
||||
/* send init signal to device */
|
||||
gpio_clear(dev->params.pin);
|
||||
xtimer_usleep(START_LOW_TIME);
|
||||
gpio_set(dev->params.pin);
|
||||
xtimer_usleep(START_HIGH_TIME);
|
||||
|
||||
/* sync on device */
|
||||
gpio_init(dev->pin, dev->in_mode);
|
||||
while (!gpio_read(dev->pin)) {}
|
||||
while (gpio_read(dev->pin)) {}
|
||||
/* sync on device */
|
||||
gpio_init(dev->params.pin, dev->params.in_mode);
|
||||
if (_wait_for_level(dev->params.pin, 1, TIMEOUT)) {
|
||||
_reset(dev);
|
||||
return DHT_TIMEOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* data is read in sequentially, highest bit first:
|
||||
* 40 .. 24 23 .. 8 7 .. 0
|
||||
* [humidity][temperature][checksum]
|
||||
*/
|
||||
if (_wait_for_level(dev->params.pin, 0, TIMEOUT)) {
|
||||
_reset(dev);
|
||||
return DHT_TIMEOUT;
|
||||
}
|
||||
|
||||
/* read the humidity, temperature, and checksum bits */
|
||||
raw_hum = read(dev->pin, 16);
|
||||
raw_temp = read(dev->pin, 16);
|
||||
csum = (uint8_t)read(dev->pin, 8);
|
||||
/*
|
||||
* data is read in sequentially, highest bit first:
|
||||
* 40 .. 24 23 .. 8 7 .. 0
|
||||
* [humidity][temperature][checksum]
|
||||
*/
|
||||
|
||||
/* set pin high again - so we can trigger the next reading by pulling it low
|
||||
* again */
|
||||
gpio_init(dev->pin, GPIO_OUT);
|
||||
gpio_set(dev->pin);
|
||||
/* read the humidity, temperature, and checksum bits */
|
||||
if (_read(&raw_hum, dev->params.pin, 16)) {
|
||||
_reset(dev);
|
||||
return DHT_TIMEOUT;
|
||||
}
|
||||
|
||||
/* validate the checksum */
|
||||
sum = (raw_temp >> 8) + (raw_temp & 0xff) + (raw_hum >> 8) + (raw_hum & 0xff);
|
||||
if ((sum != csum) || (csum == 0)) {
|
||||
DEBUG("error: checksum invalid\n");
|
||||
return DHT_NOCSUM;
|
||||
if (_read(&raw_temp, dev->params.pin, 16)) {
|
||||
_reset(dev);
|
||||
return DHT_TIMEOUT;
|
||||
}
|
||||
|
||||
if (_read(&csum, dev->params.pin, 8)) {
|
||||
_reset(dev);
|
||||
return DHT_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Bring device back to defined state - so we can trigger the next reading
|
||||
* by pulling the data pin low again */
|
||||
_reset(dev);
|
||||
|
||||
/* validate the checksum */
|
||||
uint8_t sum = (raw_temp >> 8) + (raw_temp & 0xff) + (raw_hum >> 8)
|
||||
+ (raw_hum & 0xff);
|
||||
if (sum != csum) {
|
||||
DEBUG("error: checksum doesn't match\n");
|
||||
return DHT_NOCSUM;
|
||||
}
|
||||
|
||||
/* parse the RAW values */
|
||||
DEBUG("RAW values: temp: %7i hum: %7i\n", (int)raw_temp, (int)raw_hum);
|
||||
switch (dev->params.type) {
|
||||
case DHT11:
|
||||
dev->last_val.temperature = (int16_t)((raw_temp >> 8) * 10);
|
||||
dev->last_val.humidity = (int16_t)((raw_hum >> 8) * 10);
|
||||
break;
|
||||
/* DHT21 == DHT22 (same value in enum), so both are handled here */
|
||||
case DHT22:
|
||||
dev->last_val.humidity = (int16_t)raw_hum;
|
||||
/* if the high-bit is set, the value is negative */
|
||||
if (raw_temp & 0x8000) {
|
||||
dev->last_val.temperature = (int16_t)((raw_temp & ~0x8000) * -1);
|
||||
}
|
||||
else {
|
||||
dev->last_val.temperature = (int16_t)raw_temp;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return DHT_NODEV; /* this should never be reached */
|
||||
}
|
||||
|
||||
/* update time of last measurement */
|
||||
dev->last_read_us = now_us;
|
||||
}
|
||||
|
||||
/* parse the RAW values */
|
||||
DEBUG("RAW values: temp: %7i hum: %7i\n", (int)raw_temp, (int)raw_hum);
|
||||
switch (dev->type) {
|
||||
case DHT11:
|
||||
*temp = (int16_t)((raw_temp >> 8) * 10);
|
||||
*hum = (int16_t)((raw_hum >> 8) * 10);
|
||||
break;
|
||||
case DHT22:
|
||||
*hum = (int16_t)raw_hum;
|
||||
/* if the high-bit is set, the value is negative */
|
||||
if (raw_temp & 0x8000) {
|
||||
*temp = (int16_t)((raw_temp & ~0x8000) * -1);
|
||||
}
|
||||
else {
|
||||
*temp = (int16_t)raw_temp;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return DHT_NODEV; /* this should never be reached */
|
||||
if (temp) {
|
||||
*temp = dev->last_val.temperature;
|
||||
}
|
||||
|
||||
if (hum) {
|
||||
*hum = dev->last_val.humidity;
|
||||
}
|
||||
|
||||
return DHT_OK;
|
||||
|
@ -30,38 +30,25 @@
|
||||
|
||||
#include "saul.h"
|
||||
#include "dht.h"
|
||||
#include "xtimer.h"
|
||||
|
||||
#define DHT_SAUL_HOLD_TIME (1000 * 1000U) /* 1s */
|
||||
|
||||
static int16_t temp, hum;
|
||||
static uint32_t last = 0;
|
||||
|
||||
static int check_and_read(const void *dev, phydat_t *res, int16_t *val, uint8_t unit)
|
||||
static int read_temp(const void *dev, phydat_t *res)
|
||||
{
|
||||
uint32_t now = xtimer_now_usec();
|
||||
|
||||
if ((now - last) > DHT_SAUL_HOLD_TIME) {
|
||||
dht_read((const dht_t *)dev, &temp, &hum);
|
||||
last = now;
|
||||
if (dht_read((dht_t *)dev, &res->val[0], NULL)) {
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
res->val[0] = *val;
|
||||
res->val[1] = 0;
|
||||
res->val[2] = 0;
|
||||
res->unit = unit;
|
||||
res->unit = UNIT_TEMP_C;
|
||||
res->scale = -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int read_temp(const void *dev, phydat_t *res)
|
||||
{
|
||||
return check_and_read(dev, res, &temp, UNIT_TEMP_C);
|
||||
}
|
||||
|
||||
static int read_hum(const void *dev, phydat_t *res)
|
||||
{
|
||||
return check_and_read(dev, res, &hum, UNIT_PERCENT);
|
||||
if (dht_read((dht_t *)dev, NULL, &res->val[0])) {
|
||||
return -ECANCELED;
|
||||
}
|
||||
res->unit = UNIT_PERCENT;
|
||||
res->scale = -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const saul_driver_t dht_temp_saul_driver = {
|
||||
|
@ -45,7 +45,8 @@ extern "C" {
|
||||
enum {
|
||||
DHT_OK = 0, /**< all good */
|
||||
DHT_NOCSUM = -1, /**< checksum error */
|
||||
DHT_NODEV = -2 /**< device type not defined */
|
||||
DHT_TIMEOUT = -2, /**< communication timed out */
|
||||
DHT_NODEV = -3 /**< device type not defined */
|
||||
};
|
||||
|
||||
/**
|
||||
@ -66,19 +67,24 @@ typedef enum {
|
||||
} dht_type_t;
|
||||
|
||||
/**
|
||||
* @brief Device descriptor for DHT sensor devices
|
||||
* @brief Configuration parameters for DHT devices
|
||||
*/
|
||||
typedef struct {
|
||||
gpio_t pin; /**< GPIO pin of the device's data pin */
|
||||
dht_type_t type; /**< type of the DHT device */
|
||||
gpio_mode_t in_mode; /**< input pin configuration, with or without pull
|
||||
* resistor */
|
||||
} dht_t;
|
||||
|
||||
} dht_params_t;
|
||||
|
||||
/**
|
||||
* @brief Configuration parameters for DHT devices
|
||||
* @brief Device descriptor for DHT sensor devices
|
||||
*/
|
||||
typedef dht_t dht_params_t;
|
||||
typedef struct {
|
||||
dht_params_t params; /**< Device parameters */
|
||||
dht_data_t last_val; /**< Values of the last measurement */
|
||||
uint32_t last_read_us; /**< Time of the last measurement */
|
||||
} dht_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize a new DHT device
|
||||
@ -101,11 +107,12 @@ int dht_init(dht_t *dev, const dht_params_t *params);
|
||||
* @param[out] temp temperature value [in °C * 10^-1]
|
||||
* @param[out] hum relative humidity value [in percent * 10^-1]
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -1 on checksum error
|
||||
* @return -2 on parsing error
|
||||
* @retval `DHT_OK` Success
|
||||
* @retval `DHT_NOCSUM` Checksum error
|
||||
* @retval `DHT_TIMEOUT` Reading data timed out (check wires!)
|
||||
* @retval `DHT_NODEV` Unsupported device type specified
|
||||
*/
|
||||
int dht_read(const dht_t *dev, int16_t *temp, int16_t *hum);
|
||||
int dht_read(dht_t *dev, int16_t *temp, int16_t *hum);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user