mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-13 08:40:26 +01:00
19718: drivers/dht: busy wait reimplementation r=benpicco a=hugueslarrive ### Contribution description In PR #19674, I also provided quick and dirty fixes to restore functionality on esp8266 and enable operation on AVR. While reviewing PR #18591, it became apparent to me that this driver needed a refresh, particularly its migration to ztimer. The cause of the malfunction on esp8266 was that since the default switch to ztimer as the backend for xtimer, XTIMER_BACKOFF was no longer taken into account. Therefore, the correction I provided in PR #19674 simply made explicit what was previously done implicitly with xtimer and now needs to be done explicitly with ztimer (spinning instead of sleeping). Moreover, it was unnecessarily complex to measure the pulse duration in a busy-wait implementation, which required 2 calls to ztimer_now() and 32-bit operations expensive on 8-bit architecture. Instead, it is sufficient to read the state of the bus at the threshold moment. Finally, in practice, it is possible to reduce the read interval (down to less than 0.5s for DHT22) by "harassing" the DHT with start signals until it responds. This re-implementation brings the following improvements: - Many backports from `@maribu's` IRQ based implementation (#18591): - Use of ztimer - Use of errno.h - Use of a dht_data structure to pass arguments, to facilitate integration - Adaptation of the bit parsing technique to parse bits into the data array - Reintroduction of DHT11/DHT22 differentiation. - Separation of `dht_read()` steps into functions for better readability and the ability to share certain functions among different implementations - Sensor presence check in `dht_init()` - ~~Automatic adjustment to a minimum data hold time~~ - Default input mode changed to open drain (a pull-up resistor should be placed close to the output if necessary but not close to the input) - AVR support without platform-specific handling by avoiding ztimer_spin() and using the overflow of an 8-bit variable as a pre-timeout to minimize time-consuming ztimer_now() calls Regarding the changes in the start signal sequence and the removal of the `_reset()` function: ![nano_dht_read_2](https://github.com/RIOT-OS/RIOT/assets/67432403/95966813-2b5f-4a0f-a388-8ac630526ab2) ~~In the previous implementation, there was an unnecessary spike at the beginning of the signal sequence, corresponding to START_HIGH_TIME. This spike has been removed in the re-implementation, as it is unnecessary. Instead, the MCU now simply pulls the signal low for START_LOW_TIME and then releases the bus, which is sufficient for initiating communication with the DHT sensor.~~ Actually, it is necessary to raise the bus level; otherwise, the _wait_for_level() called immediately after to check the response of the DHT may read the port before the signal level is raised, resulting in a false positive. Additionally, the previous implementation had an issue where the MCU switched back to output mode and went high immediately after reading the 40 bits of data. However, the DHT sensor was still transmitting 2 or 3 additional bytes of '0' at that point, causing a conflict. This issue has been resolved in the re-implementation: ![nano_dht_read_optimized](https://github.com/RIOT-OS/RIOT/assets/67432403/ff124839-5ec5-4df3-bab7-5348d8160a25) ~~Regarding the optimization for AVR, I have performed measurements of `_wait_for_level()` until timeout (85 loops):~~ ~~- on esp8266-esp-12x: 264 µs, which is 3.11 µs per loop~~ ~~- on nucleo-f303k8: 319 µs, which is 3.75 µs per loop~~ ~~- on arduino-nano: 3608 µs, which is 42.45 µs per loop~~ ~~Duration measurements on the Arduino Nano:~~ 19737: dist/tools/openocd: start debug-server in background and wait r=benpicco a=fabian18 19746: buildsystem: Always expose CPU_RAM_BASE & SIZE flags r=benpicco a=Teufelchen1 ### Contribution description Hello 🐧 This moves the definition of `CPU_RAM_BASE/SIZE` from being only available in certain situation to be always available. Reason for change is to simplify common code in the cpu folder. In cooperation with `@benpicco` ### Testing procedure Passing CI ### Issues/PRs references First usage will be in the PMP driver. Although there is more code in RIOT that could be refactored to use these defines instead of hacks / hardcoded values. Co-authored-by: Hugues Larrive <hlarrive@pm.me> Co-authored-by: Fabian Hüßler <fabian.huessler@ml-pa.com> Co-authored-by: Teufelchen1 <bennet.blischke@outlook.com>
This commit is contained in:
commit
561e19303d
19
dist/tools/openocd/openocd.sh
vendored
19
dist/tools/openocd/openocd.sh
vendored
@ -409,6 +409,17 @@ do_debug() {
|
||||
|
||||
do_debugserver() {
|
||||
test_config
|
||||
# temporary file that saves OpenOCD pid
|
||||
OCD_PIDFILE=$(mktemp -t "openocd_pid.XXXXXXXXXX")
|
||||
# will be called by trap
|
||||
cleanup() {
|
||||
OCD_PID="$(cat $OCD_PIDFILE)"
|
||||
kill ${OCD_PID}
|
||||
rm -f "$OCD_PIDFILE"
|
||||
exit 0
|
||||
}
|
||||
# cleanup after script terminates
|
||||
trap "cleanup ${OCD_PIDFILE}" EXIT
|
||||
# start OpenOCD as GDB server
|
||||
sh -c "${OPENOCD} \
|
||||
${OPENOCD_ADAPTER_INIT} \
|
||||
@ -421,7 +432,13 @@ do_debugserver() {
|
||||
-c 'init' \
|
||||
${OPENOCD_DBG_EXTRA_CMD} \
|
||||
-c 'targets' \
|
||||
-c 'halt'"
|
||||
-c 'halt' & \
|
||||
echo \$! > $OCD_PIDFILE ; \
|
||||
wait \$(cat $OCD_PIDFILE)" &
|
||||
|
||||
while read -r line; do
|
||||
echo "Exit with Ctrl+D"
|
||||
done
|
||||
}
|
||||
|
||||
do_reset() {
|
||||
|
@ -10,7 +10,7 @@ config MODULE_DHT
|
||||
depends on HAS_PERIPH_GPIO
|
||||
depends on TEST_KCONFIG
|
||||
select MODULE_PERIPH_GPIO
|
||||
select MODULE_XTIMER
|
||||
select ZTIMER_USEC
|
||||
|
||||
config HAVE_DHT
|
||||
bool
|
||||
|
@ -1,2 +1,2 @@
|
||||
USEMODULE += xtimer
|
||||
USEMODULE += ztimer_usec
|
||||
FEATURES_REQUIRED += periph_gpio
|
||||
|
@ -2,6 +2,7 @@
|
||||
* Copyright 2015 Ludwig Knüpfer
|
||||
* 2015 Christian Mehlis
|
||||
* 2016-2017 Freie Universität Berlin
|
||||
* 2023 Hugues Larrive
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
@ -19,201 +20,260 @@
|
||||
* @author Ludwig Knüpfer <ludwig.knuepfer@fu-berlin.de>
|
||||
* @author Christian Mehlis <mehlis@inf.fu-berlin.de>
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
* @author Hugues Larrive <hugues.larrive@pm.me>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "assert.h"
|
||||
#include "xtimer.h"
|
||||
#include "timex.h"
|
||||
#include "periph/gpio.h"
|
||||
|
||||
#include "dht.h"
|
||||
#include "dht_params.h"
|
||||
#include "log.h"
|
||||
#include "periph/gpio.h"
|
||||
#include "time_units.h"
|
||||
#include "ztimer.h"
|
||||
|
||||
#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)
|
||||
#define READ_THRESHOLD (40U)
|
||||
/* If an expected pulse is not detected within 85µs, something is wrong */
|
||||
#define SPIN_TIMEOUT (85U)
|
||||
/* The start signal by pulling data low for at least 18 ms for DHT11, at
|
||||
* most 20 ms (AM2301 / DHT22 / DHT21). Then release the bus and the
|
||||
* sensor should respond by pulling data low for 80 µs, then release for
|
||||
* 80µs before start sending data. */
|
||||
#define START_LOW_TIME (19U * US_PER_MS)
|
||||
#define START_THRESHOLD (75U)
|
||||
/* DHTs have to wait for power 1 or 2 seconds depending on the model */
|
||||
#define POWER_WAIT_TIMEOUT (2U * US_PER_SEC)
|
||||
|
||||
static inline void _reset(dht_t *dev)
|
||||
enum {
|
||||
BYTEPOS_HUMIDITY_HIGH = 0,
|
||||
BYTEPOS_HUMIDITY_LOW = 1,
|
||||
BYTEPOS_TEMPERATURE_HIGH = 2,
|
||||
BYTEPOS_TEMPERATURE_LOW = 3,
|
||||
BYTEPOS_CHECKSUM = 4,
|
||||
};
|
||||
|
||||
struct dht_data {
|
||||
gpio_t pin;
|
||||
gpio_mode_t in_mode;
|
||||
uint8_t data[5];
|
||||
int8_t bit_pos;
|
||||
uint8_t bit;
|
||||
};
|
||||
|
||||
static void _wait_for_level(gpio_t pin, bool expected, uint32_t start)
|
||||
{
|
||||
/* Calls to ztimer_now() can be relatively slow on low end platforms.
|
||||
* Mixing in a busy down-counting loop solves issues e.g. on AVR boards. */
|
||||
uint8_t pre_timeout = 0;
|
||||
while (((bool)gpio_read(pin) != expected) && (++pre_timeout
|
||||
|| ztimer_now(ZTIMER_USEC) < start + SPIN_TIMEOUT)) {}
|
||||
}
|
||||
|
||||
static int _send_start_signal(dht_t *dev)
|
||||
{
|
||||
uint32_t start;
|
||||
gpio_init(dev->params.pin, GPIO_OUT);
|
||||
gpio_clear(dev->params.pin);
|
||||
ztimer_sleep(ZTIMER_USEC, START_LOW_TIME);
|
||||
/* sync on device */
|
||||
gpio_set(dev->params.pin);
|
||||
gpio_init(dev->params.pin, dev->params.in_mode);
|
||||
/* check device response (80 µs low then 80 µs high) */
|
||||
start = ztimer_now(ZTIMER_USEC);
|
||||
_wait_for_level(dev->params.pin, 0, start);
|
||||
if (ztimer_now(ZTIMER_USEC) - start > START_THRESHOLD) {
|
||||
DEBUG_PUTS("[dht] error: response low pulse > START_THRESHOLD");
|
||||
return -ENODEV;
|
||||
}
|
||||
_wait_for_level(dev->params.pin, 1, start);
|
||||
start = ztimer_now(ZTIMER_USEC);
|
||||
_wait_for_level(dev->params.pin, 0, start);
|
||||
if (ztimer_now(ZTIMER_USEC) - start < START_THRESHOLD) {
|
||||
DEBUG_PUTS("[dht] error: response high pulse < START_THRESHOLD");
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
static void _bit_parse(struct dht_data *arg)
|
||||
{
|
||||
while (((gpio_read(pin) > 0) != expect) && timeout) {
|
||||
xtimer_usleep(1);
|
||||
timeout--;
|
||||
int8_t pos = arg->bit_pos++;
|
||||
if (arg->bit) {
|
||||
arg->data[pos / 8] |= (0x80U >> (pos % 8));
|
||||
}
|
||||
|
||||
return (timeout > 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int _read(uint8_t *dest, gpio_t pin)
|
||||
static void _busy_wait_read(struct dht_data *arg)
|
||||
{
|
||||
DEBUG("[dht] read\n");
|
||||
uint16_t res = 0;
|
||||
uint32_t start = ztimer_now(ZTIMER_USEC);
|
||||
while (arg->bit_pos != 40) {
|
||||
_wait_for_level(arg->pin, 1, start);
|
||||
start = ztimer_now(ZTIMER_USEC);
|
||||
_wait_for_level(arg->pin, 0, start);
|
||||
arg->bit = (ztimer_now(ZTIMER_USEC) - start > READ_THRESHOLD) ? 1 : 0;
|
||||
_bit_parse(arg);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
uint32_t start, end;
|
||||
res <<= 1;
|
||||
/* measure the length between the next rising and falling flanks (the
|
||||
* time the pin is high - smoke up :-) */
|
||||
if (_wait_for_level(pin, 1, TIMEOUT)) {
|
||||
return -1;
|
||||
}
|
||||
start = xtimer_now_usec();
|
||||
static int _validate_checksum(uint8_t *data)
|
||||
{
|
||||
uint8_t sum = 0;
|
||||
|
||||
if (_wait_for_level(pin, 0, TIMEOUT)) {
|
||||
return -1;
|
||||
}
|
||||
end = xtimer_now_usec();
|
||||
for (uint_fast8_t i = 0; i < 4; i++) {
|
||||
sum += data[i];
|
||||
}
|
||||
if (sum != data[BYTEPOS_CHECKSUM]) {
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if the high phase was more than 40us, we got a 1 */
|
||||
if ((end - start) > PULSE_WIDTH_THRESHOLD) {
|
||||
res |= 0x0001;
|
||||
static int _parse_raw_values(dht_t *dev, uint8_t *data)
|
||||
{
|
||||
bool is_negative;
|
||||
|
||||
switch (dev->params.type) {
|
||||
case DHT11:
|
||||
case DHT11_2022:
|
||||
DEBUG_PUTS("[dht] parse raw values with DHT11 data format");
|
||||
dev->last_val.humidity = data[BYTEPOS_HUMIDITY_HIGH] * 10
|
||||
+ data[BYTEPOS_HUMIDITY_LOW];
|
||||
/* MSB for integral temperature byte gives sign, remaining is
|
||||
* abs() of value (beware: this is not two's complement!) */
|
||||
is_negative = data[BYTEPOS_TEMPERATURE_LOW] & 0x80;
|
||||
data[BYTEPOS_TEMPERATURE_LOW] &= ~0x80;
|
||||
/* 2022-12 aosong.com data sheet uses interprets low bits as
|
||||
* 0.01°C per LSB */
|
||||
if (dev->params.type == DHT11_2022) {
|
||||
data[BYTEPOS_TEMPERATURE_LOW] /= 10;
|
||||
}
|
||||
if (data[BYTEPOS_TEMPERATURE_LOW] >= 10) {
|
||||
return -ERANGE;
|
||||
}
|
||||
dev->last_val.temperature = data[BYTEPOS_TEMPERATURE_HIGH] * 10
|
||||
+ data[BYTEPOS_TEMPERATURE_LOW];
|
||||
break;
|
||||
/* AM2301 == DHT21 == DHT22 (same value in enum),
|
||||
* so all are handled here */
|
||||
case DHT22:
|
||||
DEBUG_PUTS("[dht] parse raw values with DHT22 data format");
|
||||
dev->last_val.humidity = (int16_t)(
|
||||
(data[BYTEPOS_HUMIDITY_HIGH] << 8)
|
||||
| data[BYTEPOS_HUMIDITY_LOW]);
|
||||
is_negative = data[BYTEPOS_TEMPERATURE_HIGH] & 0x80;
|
||||
data[BYTEPOS_TEMPERATURE_HIGH] &= ~0x80;
|
||||
dev->last_val.temperature = (int16_t)(
|
||||
(data[BYTEPOS_TEMPERATURE_HIGH] << 8)
|
||||
| data[BYTEPOS_TEMPERATURE_LOW]);
|
||||
break;
|
||||
default:
|
||||
return -ENOSYS; /* ENOSYS 38 Function not implemented */
|
||||
}
|
||||
|
||||
if (is_negative) {
|
||||
dev->last_val.temperature = -dev->last_val.temperature;
|
||||
}
|
||||
|
||||
*dest = res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dht_init(dht_t *dev, const dht_params_t *params)
|
||||
{
|
||||
DEBUG("[dht] dht_init\n");
|
||||
int16_t timeout;
|
||||
|
||||
DEBUG_PUTS("[dht] dht_init");
|
||||
|
||||
/* check parameters and configuration */
|
||||
assert(dev && params);
|
||||
/* AM2301 == DHT21 == DHT22 (same value in enum) */
|
||||
assert((params->type == DHT11) || (params->type == DHT11_2022)
|
||||
|| (params->type == DHT22));
|
||||
|
||||
memset(dev, 0, sizeof(dht_t));
|
||||
dev->params = *params;
|
||||
|
||||
_reset(dev);
|
||||
/* The 2-second delay mentioned in the datasheet is only required
|
||||
* after a power cycle. */
|
||||
timeout = POWER_WAIT_TIMEOUT / US_PER_MS;
|
||||
gpio_init(dev->params.pin, GPIO_IN);
|
||||
while (!gpio_read(dev->params.pin) && timeout--) {
|
||||
ztimer_sleep(ZTIMER_USEC, US_PER_MS);
|
||||
}
|
||||
if (timeout < 0) {
|
||||
DEBUG_PUTS("[dht] dht_init: error: Invalid cross-device link");
|
||||
return -EXDEV;
|
||||
}
|
||||
else {
|
||||
DEBUG("\n[dht] dht_init: power-up duration: %" PRIi16 " ms\n",
|
||||
(int16_t)(POWER_WAIT_TIMEOUT / US_PER_MS) - timeout);
|
||||
}
|
||||
/* The previous test does not ensure the sensor presence in case an
|
||||
* external pull-up resistor is used. */
|
||||
while (_send_start_signal(dev) == -ENODEV
|
||||
&& (timeout -= START_LOW_TIME / US_PER_MS) > 0) {}
|
||||
if (timeout < 0) {
|
||||
DEBUG_PUTS("[dht] dht_init: error: No such device");
|
||||
return -ENODEV;
|
||||
}
|
||||
else {
|
||||
DEBUG("\n[dht] dht_init: presence check duration: %" PRIi16 " ms\n",
|
||||
(int16_t)(POWER_WAIT_TIMEOUT / US_PER_MS) - timeout);
|
||||
}
|
||||
|
||||
xtimer_msleep(2000);
|
||||
|
||||
DEBUG("[dht] dht_init: success\n");
|
||||
return DHT_OK;
|
||||
DEBUG_PUTS("[dht] dht_init: success");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dht_read(dht_t *dev, int16_t *temp, int16_t *hum)
|
||||
{
|
||||
uint8_t csum;
|
||||
uint8_t raw_temp_i, raw_temp_d, raw_hum_i, raw_hum_d;
|
||||
int ret;
|
||||
|
||||
assert(dev);
|
||||
|
||||
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);
|
||||
struct dht_data data = {
|
||||
.pin = dev->params.pin,
|
||||
.in_mode = dev->params.in_mode,
|
||||
.bit_pos = 0,
|
||||
};
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
if (_wait_for_level(dev->params.pin, 0, TIMEOUT)) {
|
||||
_reset(dev);
|
||||
return DHT_TIMEOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* data is read in sequentially, highest bit first:
|
||||
* 40 .. 24 23 .. 8 7 .. 0
|
||||
* [humidity][temperature][checksum]
|
||||
*/
|
||||
|
||||
/* read the humidity, temperature, and checksum bits */
|
||||
if (_read(&raw_hum_i, dev->params.pin)) {
|
||||
_reset(dev);
|
||||
return DHT_TIMEOUT;
|
||||
}
|
||||
if (_read(&raw_hum_d, dev->params.pin)) {
|
||||
_reset(dev);
|
||||
return DHT_TIMEOUT;
|
||||
}
|
||||
if (_read(&raw_temp_i, dev->params.pin)) {
|
||||
_reset(dev);
|
||||
return DHT_TIMEOUT;
|
||||
}
|
||||
if (_read(&raw_temp_d, dev->params.pin)) {
|
||||
_reset(dev);
|
||||
return DHT_TIMEOUT;
|
||||
}
|
||||
|
||||
if (_read(&csum, dev->params.pin)) {
|
||||
_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_i) + (raw_temp_d) + (raw_hum_i) + (raw_hum_d);
|
||||
if (sum != csum) {
|
||||
DEBUG("[dht] error: checksum doesn't match\n");
|
||||
return DHT_NOCSUM;
|
||||
}
|
||||
|
||||
/* parse the RAW values */
|
||||
DEBUG("[dht] RAW values: temp: %2i.%i hum: %2i.%i\n", (int)raw_temp_i,
|
||||
(int)raw_temp_d, (int)raw_hum_i, (int)raw_hum_d);
|
||||
|
||||
dev->last_val.humidity = raw_hum_i * 10 + raw_hum_d;
|
||||
/* MSB set means negative temperature on DHT22. Will always be 0 on DHT11 */
|
||||
if (raw_temp_i & 0x80) {
|
||||
dev->last_val.temperature = -((raw_temp_i & ~0x80) * 10 + raw_temp_d);
|
||||
}
|
||||
else {
|
||||
dev->last_val.temperature = raw_temp_i * 10 + raw_temp_d;
|
||||
}
|
||||
|
||||
/* update time of last measurement */
|
||||
dev->last_read_us = now_us;
|
||||
if (_send_start_signal(dev) == -ENODEV) {
|
||||
DEBUG_PUTS("[dht] error: No response from device");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (temp) {
|
||||
*temp = dev->last_val.temperature;
|
||||
/* read the data */
|
||||
_busy_wait_read(&data);
|
||||
|
||||
if (_validate_checksum(data.data) == -EIO) {
|
||||
DEBUG("[dht] error: checksum doesn't match\n"
|
||||
"[dht] RAW data: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
|
||||
(unsigned)data.data[0], (unsigned)data.data[1],
|
||||
(unsigned)data.data[2], (unsigned)data.data[3],
|
||||
(unsigned)data.data[4]);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (hum) {
|
||||
*hum = dev->last_val.humidity;
|
||||
if ((ret = _parse_raw_values(dev, data.data)) < 0) {
|
||||
if (ret == -ENOSYS) {
|
||||
DEBUG_PUTS("[dht] error: data format not implemented");
|
||||
}
|
||||
else if (ret == -ERANGE) {
|
||||
DEBUG_PUTS("[dht] error: invalid temperature low byte");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
return DHT_OK;
|
||||
*hum = dev->last_val.humidity;
|
||||
*temp = dev->last_val.temperature;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -34,11 +34,15 @@ extern "C" {
|
||||
#ifndef DHT_PARAM_PIN
|
||||
#define DHT_PARAM_PIN (GPIO_PIN(0, 0))
|
||||
#endif
|
||||
#ifndef DHT_PARAM_TYPE
|
||||
#define DHT_PARAM_TYPE (DHT11)
|
||||
#endif
|
||||
#ifndef DHT_PARAM_PULL
|
||||
#define DHT_PARAM_PULL (GPIO_IN_PU)
|
||||
#define DHT_PARAM_PULL (GPIO_IN)
|
||||
#endif
|
||||
#ifndef DHT_PARAMS
|
||||
#define DHT_PARAMS { .pin = DHT_PARAM_PIN, \
|
||||
.type = DHT_PARAM_TYPE, \
|
||||
.in_mode = DHT_PARAM_PULL }
|
||||
#endif
|
||||
#ifndef DHT_SAULINFO
|
||||
|
@ -31,6 +31,7 @@
|
||||
#ifndef DHT_H
|
||||
#define DHT_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "periph/gpio.h"
|
||||
@ -40,27 +41,42 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Possible return codes
|
||||
* @brief Possible return codes
|
||||
*
|
||||
* @deprecated The functions use errno codes instead now
|
||||
*/
|
||||
enum {
|
||||
DHT_OK = 0, /**< all good */
|
||||
DHT_NOCSUM = -1, /**< checksum error */
|
||||
DHT_TIMEOUT = -2, /**< communication timed out */
|
||||
DHT_OK = 0, /**< all good */
|
||||
DHT_NOCSUM = -EIO, /**< checksum error */
|
||||
DHT_TIMEOUT = -ETIMEDOUT, /**< communication timed out */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Data type for storing DHT sensor readings
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t humidity; /**< relative percent */
|
||||
uint16_t humidity; /**< relative humidity in deci-percent */
|
||||
uint16_t temperature; /**< temperature in deci-Celsius */
|
||||
} dht_data_t;
|
||||
|
||||
/**
|
||||
* @brief Device type of the DHT device
|
||||
*/
|
||||
typedef enum {
|
||||
DHT11, /**< Older DHT11 variants with either 1 °C or
|
||||
* 0.1 °C resolution */
|
||||
DHT11_2022, /**< New DHT11 variant with 0.01 °C resolution */
|
||||
DHT22, /**< DHT22 device identifier */
|
||||
DHT21 = DHT22, /**< DHT21 device identifier */
|
||||
AM2301 = DHT22, /**< AM2301 device identifier */
|
||||
} dht_type_t;
|
||||
|
||||
/**
|
||||
* @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_params_t;
|
||||
@ -71,7 +87,6 @@ typedef struct {
|
||||
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;
|
||||
|
||||
/**
|
||||
@ -80,8 +95,14 @@ typedef struct {
|
||||
* @param[out] dev device descriptor of a DHT device
|
||||
* @param[in] params configuration parameters
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -1 on error
|
||||
* @retval 0 Success
|
||||
* @retval -EXDEV A low level on the input after the sensor's startup
|
||||
* time indicates that either no sensor or pull-up
|
||||
* resistor is connected, or the sensor is physically
|
||||
* poorly connected or powered.
|
||||
* @retval -ENODEV The sensor did not respond to the transmission of a
|
||||
* start signal. Likely there were a pull-up resistor but
|
||||
* no sensor connected on the data line.
|
||||
*/
|
||||
int dht_init(dht_t *dev, const dht_params_t *params);
|
||||
|
||||
@ -95,9 +116,15 @@ 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]
|
||||
*
|
||||
* @retval DHT_OK Success
|
||||
* @retval DHT_NOCSUM Checksum error
|
||||
* @retval DHT_TIMEOUT Reading data timed out (check wires!)
|
||||
* @retval 0 Success
|
||||
* @retval -ENODEV The sensor did not respond to the transmission of a
|
||||
* start signal. Likely the RESPAWN_TIMEOUT is
|
||||
* insufficient.
|
||||
* @retval -EIO The received and the expected checksum didn't match.
|
||||
* @retval -ENOSYS Unable to parse the received data. Likely the data
|
||||
* format is not implemented.
|
||||
* @retval -ERANGE Temperature low byte >= 10. Likely misconfigured
|
||||
* device type (DHT11_2022).
|
||||
*/
|
||||
int dht_read(dht_t *dev, int16_t *temp, int16_t *hum);
|
||||
|
||||
|
@ -119,3 +119,7 @@ CFLAGS += $(filter-out $(OPTIONAL_CFLAGS_BLACKLIST),$(OPTIONAL_CFLAGS))
|
||||
# accept good C practises within `extern "C" { ... }` while enforcing good C++
|
||||
# practises elsewhere. But in absence of this, we disable the warning for now.
|
||||
CXXEXFLAGS += -Wno-missing-field-initializers
|
||||
|
||||
# Reformat the RAM region for usage within code and expose them
|
||||
CFLAGS += -DCPU_RAM_BASE=$(RAM_START_ADDR)
|
||||
CFLAGS += -DCPU_RAM_SIZE=$(shell printf "0x%x" $$(($(RAM_LEN:%K=%*1024))))
|
||||
|
@ -163,11 +163,6 @@ ifneq (,$(filter prng,$(USEMODULE)))
|
||||
include $(RIOTBASE)/sys/random/Makefile.include
|
||||
endif
|
||||
|
||||
ifneq (,$(filter tinyusb_dfu usbus_dfu riotboot_reset,$(USEMODULE)))
|
||||
CFLAGS += -DCPU_RAM_BASE=$(RAM_START_ADDR)
|
||||
CFLAGS += -DCPU_RAM_SIZE=$(shell printf "0x%x" $$(($(RAM_LEN:%K=%*1024))))
|
||||
endif
|
||||
|
||||
ifneq (,$(filter test_utils_netdev_eth_minimal,$(USEMODULE)))
|
||||
CFLAGS += -DCONFIG_NETDEV_REGISTER_SIGNAL
|
||||
endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
include ../Makefile.drivers_common
|
||||
|
||||
USEMODULE += dht
|
||||
USEMODULE += xtimer
|
||||
USEMODULE += ztimer_usec
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
@ -1,4 +1,4 @@
|
||||
# this file enables modules defined in Kconfig. Do not use this file for
|
||||
# application configuration. This is only needed during migration.
|
||||
CONFIG_MODULE_DHT=y
|
||||
CONFIG_MODULE_XTIMER=y
|
||||
CONFIG_ZTIMER_USEC=y
|
||||
|
@ -23,11 +23,10 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "xtimer.h"
|
||||
#include "timex.h"
|
||||
#include "fmt.h"
|
||||
#include "dht.h"
|
||||
#include "dht_params.h"
|
||||
#include "time_units.h"
|
||||
#include "ztimer.h"
|
||||
|
||||
#define DELAY (2 * US_PER_SEC)
|
||||
|
||||
@ -50,6 +49,8 @@ int main(void)
|
||||
|
||||
/* periodically read temp and humidity values */
|
||||
while (1) {
|
||||
ztimer_sleep(ZTIMER_USEC, DELAY);
|
||||
|
||||
if (dht_read(&dev, &temp, &hum) != DHT_OK) {
|
||||
puts("Error reading values");
|
||||
continue;
|
||||
@ -57,8 +58,6 @@ int main(void)
|
||||
|
||||
printf("DHT values - temp: %d.%d°C - relative humidity: %d.%d%%\n",
|
||||
temp/10, temp%10, hum/10, hum%10);
|
||||
|
||||
xtimer_usleep(DELAY);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user