diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 67ccb63137..fc7f9bee80 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -659,3 +659,9 @@ ifneq (,$(filter xbee,$(USEMODULE))) USEMODULE += xtimer USEMODULE += netif endif + +ifneq (,$(filter dcf77,$(USEMODULE))) + FEATURES_REQUIRED += periph_gpio + FEATURES_REQUIRED += periph_gpio_irq + USEMODULE += xtimer +endif diff --git a/drivers/Makefile.include b/drivers/Makefile.include index 9580832c97..8ec41c5264 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -329,3 +329,7 @@ endif ifneq (,$(filter sds011,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/sds011/include endif + +ifneq (,$(filter dcf77,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/dcf77/include +endif diff --git a/drivers/dcf77/Makefile b/drivers/dcf77/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/dcf77/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/dcf77/dcf77.c b/drivers/dcf77/dcf77.c new file mode 100644 index 0000000000..f2363055ec --- /dev/null +++ b/drivers/dcf77/dcf77.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2019 HAW Hamburg + * + * 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 + * directory for more details. + */ +/** + * @ingroup drivers_dcf77 + * @{ + * + * @file + * @brief Device driver implementation for the dcf 77 + * longwave time signal and standard-frequency radio station + * + * @author Michel Gerlach + * + * @} + */ + +#include +#include + +#include "log.h" +#include "assert.h" +#include "xtimer.h" +#include "periph/gpio.h" + +#include "dcf77.h" +#include "dcf77_params.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* Persistent level longer than 1200ms starts a new cycle */ +#define DCF77_PULSE_START_HIGH_THRESHOLD_US (1200000U) /*~1200ms*/ +/* Every pulse send by the DCF longer than 130ms is interpreted as 1 */ +#define DCF77_PULSE_WIDTH_THRESHOLD_US (140000U) /*~140ms*/ +/* Number of bits in a cycle*/ +#define DCF77_READING_CYCLE (59) + +#define DCF77_MINUTE_MASK (0xFE00000ULL) +#define DCF77_HOUR_MASK (0x7E0000000ULL) +#define DCF77_DATE_MASK (0x1FFFFFC00000000ULL) + +#define DCF77_MINUTE_SHIFT (21) +#define DCF77_HOUR_SHIFT (29) +#define DCF77_DATE_SHIFT (36) + +static void _level_cb_high(dcf77_t *dev) +{ + switch (dev->internal_state) { + case DCF77_STATE_START: + DEBUG("[dcf77] EVENT START 1 !\n"); + if ((xtimer_now_usec() - dev->startTime) > + DCF77_PULSE_START_HIGH_THRESHOLD_US) { + memset(&dev->bitseq.bits, 0, sizeof(dev->bitseq.bits)); + dev->internal_state = DCF77_STATE_RX; + } + else { + dev->internal_state = DCF77_STATE_IDLE; + } + break; + case DCF77_STATE_RX: + DEBUG("[dcf77] EVENT RX 1 !\n"); + dev->startTime = xtimer_now_usec(); + break; + } +} + +static void _level_cb_low(dcf77_t *dev) +{ + switch (dev->internal_state) { + case DCF77_STATE_IDLE: + DEBUG("[dcf77] EVENT IDLE 0 !\n"); + dev->startTime = xtimer_now_usec(); + dev->internal_state = DCF77_STATE_START; + break; + case DCF77_STATE_RX: + DEBUG("[dcf77] EVENT RX 0 !\n"); + if ((xtimer_now_usec() - dev->startTime) > + DCF77_PULSE_WIDTH_THRESHOLD_US) { + dev->bitseq.bits |= 1ULL << dev->bitCounter; + } + + dev->bitCounter++; + + if (dev->bitCounter >= DCF77_READING_CYCLE) { + dev->bitCounter = 0; + dev->startTime = xtimer_now_usec(); + dev->last_bitseq.bits = dev->bitseq.bits; + dev->internal_state = DCF77_STATE_START; + + if (dev->tick_cb) { + dev->tick_cb(dev, dev->tick_cb_args); + } + } + + break; + } +} + +static void _level_cb(void *arg) +{ + dcf77_t *dev = (dcf77_t *)arg; + + if (gpio_read(dev->params.pin)) { + _level_cb_high(dev); + } + else { + _level_cb_low(dev); + } +} + +int dcf77_init(dcf77_t *dev, const dcf77_params_t *params) +{ + DEBUG("dcf77_init\n"); + + /* check parameters and configuration */ + assert(dev && params); + dev->tick_cb = NULL; + dev->params = *params; + dev->internal_state = DCF77_STATE_IDLE; + dev->bitCounter = 0; + if (!gpio_init_int(dev->params.pin, dev->params.in_mode, GPIO_BOTH, + _level_cb, dev)) { + return DCF77_OK; + } + else { + return DCF77_INIT_ERROR; + } +} + +int dcf77_get_time(dcf77_t *dev, struct tm *time) +{ + assert(dev); + + if (dev->last_bitseq.val.mesz == 2) { + time->tm_isdst = 1; + } + else { + time->tm_isdst = 0; + } + + uint8_t minute = 10 * dev->last_bitseq.val.minute_h + + dev->last_bitseq.val.minute_l; + if (__builtin_parity((dev->last_bitseq.bits >> DCF77_MINUTE_SHIFT) & + (DCF77_MINUTE_MASK >> DCF77_MINUTE_SHIFT)) != + dev->last_bitseq.val.minute_par) { + return DCF77_NOCSUM; + } + + uint8_t hour = 10 * dev->last_bitseq.val.hour_h + + dev->last_bitseq.val.hour_l; + if (__builtin_parity((dev->last_bitseq.bits >> DCF77_HOUR_SHIFT) & + (DCF77_HOUR_MASK >> DCF77_HOUR_SHIFT)) != + dev->last_bitseq.val.hour_par) { + return DCF77_NOCSUM; + } + + uint8_t mday = 10 * dev->last_bitseq.val.day_h + dev->last_bitseq.val.day_l; + + uint8_t wday = dev->last_bitseq.val.wday; + + uint8_t month = 10 * dev->last_bitseq.val.month_h + + dev->last_bitseq.val.month_l; + + uint8_t year = 10 * dev->last_bitseq.val.year_h + + dev->last_bitseq.val.year_l; + if (__builtin_parity((dev->last_bitseq.bits >> DCF77_DATE_SHIFT) & + (DCF77_DATE_MASK >> DCF77_DATE_SHIFT)) != + dev->last_bitseq.val.date_par) { + return DCF77_NOCSUM; + } + + time->tm_sec = 0; + time->tm_min = minute; + time->tm_hour = hour; + time->tm_mday = mday; + time->tm_wday = wday; + time->tm_mon = month - 1; + time->tm_year = 100 + year; + + return DCF77_OK; +} + +void dcf77_set_tick_cb(dcf77_t *dev, dcf77_tick_cb_t cb, void *arg) +{ + assert(dev); + + dev->tick_cb_args = arg; + dev->tick_cb = cb; +} diff --git a/drivers/dcf77/include/dcf77_internal.h b/drivers/dcf77/include/dcf77_internal.h new file mode 100644 index 0000000000..99239fdc2d --- /dev/null +++ b/drivers/dcf77/include/dcf77_internal.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 HAW Hamburg + * + * 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 + * directory for more details. + */ + +/** + * @ingroup drivers_dcf77 + * @brief + * @{ + * + * @file + * @brief Bit definitions for DCF77 transmission + * + * @author Michel Gerlach + */ +#ifndef DCF77_INTERNAL_H +#define DCF77_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Timeinformation bitfields for DCF77 devices + */ +typedef union { + /** + * @brief Struct of bitfields + */ + struct values { /**< Struct of bitfields*/ + uint64_t start : 1, /**< Number of Bits for start value*/ + wheater : 14, /**< Number of Bits for weather value */ + calling : 1, /**< Number of Bits for calling value */ + mez_mesz_shift : 1, /**< Number of Bits for shift value */ + mesz : 2, /**< Number of Bits for mesz value */ + shift_sec : 1, /**< Number of Bits for leap-second value */ + start_time : 1, /**< Number of Bits for start_Bit value */ + minute_l : 4, /**< Number of Bits for lower minute value */ + minute_h : 3, /**< Number of Bits for higher minute value */ + minute_par : 1, /**< Number of Bits for minuteparity value */ + hour_l : 4, /**< Number of Bits for lower hour value */ + hour_h : 2, /**< Number of Bits for higher hour value */ + hour_par : 1, /**< Number of Bits for hourparity value */ + day_l : 4, /**< Number of Bits for lower calenderday value */ + day_h : 2, /**< Number of Bits for higher calenderday value */ + wday : 3, /**< Number of Bits for weekday value */ + month_l : 4, /**< Number of Bits for lower month value */ + month_h : 1, /**< Number of Bits for higher month value */ + year_l : 4, /**< Number of Bits for lower year value */ + year_h : 4, /**< Number of Bits for higher year value */ + date_par : 1, /**< Number of Bits for dateparity value */ + buff : 5; /**< Number of Bits for experimental buffer value */ + } val; /**< struct with Bitfields of timeinformation*/ + uint64_t bits; /**< Value of Bits in a received cycle */ +} dcf77_bits_t; /**< Union which contains the Bitfields struct */ + + +/** + * @brief device internal states + */ +enum { + DCF77_STATE_IDLE, /**< Device is in idle state */ + DCF77_STATE_START, /**< Device is searching the start for a new minute */ + DCF77_STATE_RX, /**< Device is in RX mode */ +}; + + +#ifdef __cplusplus +} +#endif + +#endif /* DCF77_INTERNAL_H */ +/** @} */ diff --git a/drivers/dcf77/include/dcf77_params.h b/drivers/dcf77/include/dcf77_params.h new file mode 100644 index 0000000000..f696350a7a --- /dev/null +++ b/drivers/dcf77/include/dcf77_params.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2019 HAW Hamburg + * + * 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 + * directory for more details. + */ + +/** + * @ingroup drivers_dcf77 + * + * @{ + * @file + * @brief Default configuration for DCF77 devices + * + * @author Michel Gerlach + */ + +#ifndef DCF77_PARAMS_H +#define DCF77_PARAMS_H + +#include "board.h" +#include "dcf77.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Set default configuration parameters for the DCF77 devices + * @{ + */ +#ifndef DCF77_PARAM_PIN +#define DCF77_PARAM_PIN (GPIO_PIN(1, 22)) +#endif +#ifndef DCF77_PARAM_PULL +#define DCF77_PARAM_PULL (GPIO_IN) +#endif +#ifndef DCF77_PARAMS +#define DCF77_PARAMS { .pin = DCF77_PARAM_PIN, \ + .in_mode = DCF77_PARAM_PULL } +#endif +/**@}*/ + +/** + * @brief Configure DCF77 devices + */ +static const dcf77_params_t dcf77_params[] = +{ + DCF77_PARAMS +}; + +#ifdef __cplusplus +} +#endif + +#endif /* DCF77_PARAMS_H */ +/** @} */ diff --git a/drivers/include/dcf77.h b/drivers/include/dcf77.h new file mode 100644 index 0000000000..4b5a93865a --- /dev/null +++ b/drivers/include/dcf77.h @@ -0,0 +1,122 @@ +/* + * Copyright 2019 HAW Hamburg + * + * 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 + * directory for more details. + */ + +/** + * @defgroup drivers_dcf77 DCF77 long wave receiver with 77,5 kHz + * @ingroup drivers_sensors + * @brief Device driver long wave receiver with 77,5 kHz + * + * @{ + * + * @file + * @brief Interface definition for the dcf77 sensor driver + * + * @author Michel Gerlach + */ + +#ifndef DCF77_H +#define DCF77_H + +#include +#include +#include +#include "xtimer.h" +#include "time.h" +#include "periph/gpio.h" +#include "dcf77_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* forward-declaration for dcf77_tick_cb_t */ +struct dcf77; + +/** + * @brief Signature for tick callback + * + * @param[in] dev device that triggered the alarm + * @param[in] arg optional argument to put the callback in the right context + */ +typedef void(*dcf77_tick_cb_t)(struct dcf77 *dev, void *arg); + +/** + * @brief Possible return codes + */ +enum { + DCF77_OK = 0, /**< all good */ + DCF77_NOCSUM = -1, /**< checksum error */ + DCF77_TIMEOUT = -2, /**< communication timed out */ + DCF77_INIT_ERROR = -3 /**< Initialization error */ +}; + +/** + * @brief Configuration parameters for DCF77 devices + */ +typedef struct { + gpio_t pin; /**< GPIO pin of the device's data pin */ + gpio_mode_t in_mode; /**< input pin configuration from the device, + * without pull resistor */ +} dcf77_params_t; + + +/** + * @brief Device descriptor for DCF77 sensor devices + */ +typedef struct dcf77 { + dcf77_params_t params; /**< Device parameters */ + dcf77_bits_t bitseq; /**< contains all Bits from a current cycle */ + dcf77_bits_t last_bitseq; /**< contains all Bits from a last cycle */ + uint32_t startTime; /**< Timestamp to measure the term of the level */ + uint8_t internal_state; /**< internal States */ + uint8_t bitCounter; /**< Counter of the Bits in a Bitsequenz */ + dcf77_tick_cb_t tick_cb; /**< Callback to be called if a new minute starts */ + void *tick_cb_args; /**< Arguments for the tick callback */ +} dcf77_t; + +/** + * @brief Initialize a new DCF77 device + * + * @param[out] dev device descriptor of a DCF device + * @param[in] params configuration parameters + * + * @retval `DCF77_OK` Success + * @retval `DCF77_INIT_ERROR` Error in initialization + */ +int dcf77_init(dcf77_t *dev, const dcf77_params_t *params); + +/** + * @brief get a new timestamp from the device. + * + * @note if reading fails or checksum is invalid, last_vaules will be unwritten + * + * @param[in] dev device descriptor of a DCF device + * @param[in] time datastruct for timeinformation + * + * @retval `DCF77_OK` Success + * @retval `DCF77_NOCSUM` Checksum error + */ +int dcf77_get_time(dcf77_t *dev, struct tm *time); + +/** + * @brief Set a tick callback for DCF77. + * + * The registered callback function will be called for every new minute. + * + * @param[in] dev device descriptor of a DCF device + * @param[in] cb Callback executed when a new minute starts. + * @param[in] arg Argument passed to callback. + */ +void dcf77_set_tick_cb(dcf77_t *dev, dcf77_tick_cb_t cb, void *arg); + +#ifdef __cplusplus +} +#endif + +#endif /* DCF77_H */ +/** @} */ diff --git a/tests/driver_dcf77/Makefile b/tests/driver_dcf77/Makefile new file mode 100644 index 0000000000..e132c76b7a --- /dev/null +++ b/tests/driver_dcf77/Makefile @@ -0,0 +1,6 @@ +include ../Makefile.tests_common + +USEMODULE += dcf77 + +FEATURES_BLACKLIST += arch_msp430 +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_dcf77/Makefile.ci b/tests/driver_dcf77/Makefile.ci new file mode 100644 index 0000000000..518b330a9e --- /dev/null +++ b/tests/driver_dcf77/Makefile.ci @@ -0,0 +1,3 @@ +BOARD_INSUFFICIENT_MEMORY := \ + stm32f030f4-demo \ + # diff --git a/tests/driver_dcf77/README.md b/tests/driver_dcf77/README.md new file mode 100644 index 0000000000..6acc5e8b6e --- /dev/null +++ b/tests/driver_dcf77/README.md @@ -0,0 +1,9 @@ +## About + +This is a manual test application for the DCF77 driver. The driver gives you +your current time stamp. + +## Usage + +The application initializes the DCF77 device and displays the current time +every minute when the entire cycle is received correctly. diff --git a/tests/driver_dcf77/main.c b/tests/driver_dcf77/main.c new file mode 100644 index 0000000000..df4a79f789 --- /dev/null +++ b/tests/driver_dcf77/main.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2019 HAW Hamburg + * + * 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 + * directory for more details. + */ + +/** + * @ingroup tests + * @brief Test application for the DCF77 device driver + * @author Michel Gerlach + * + * + */ + +#include +#include +#include "mutex.h" +#include "xtimer.h" + +#include "dcf77_params.h" +#include "dcf77.h" + +static void dcf77_callback(dcf77_t *dev, void *arg) +{ + (void) dev; + mutex_unlock(arg); +} + +int main(void) +{ + dcf77_t sensor; + mutex_t mutex = MUTEX_INIT_LOCKED; + + printf("DCF77 test application\n"); + + /* initialize the sensor with default configuration parameters */ + if (dcf77_init(&sensor, &dcf77_params[0]) != DCF77_OK) { + puts("Initialization failed\n"); + return -1; + } + + printf("DCF77 Module initialized \n"); + + dcf77_set_tick_cb(&sensor, dcf77_callback, &mutex); + + while (1) { + struct tm time; + char buffer[32]; + mutex_lock(&mutex); + + dcf77_get_time(&sensor, &time); + + strftime(buffer, sizeof(buffer), "Date: %d.%m.%Y Time: %H:%M.", &time); + puts (buffer); + + } + + return 0; +}