/* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /** library to get time from a DCF77 module (code) * @file rtc_dcf77.c * @author King Kévin * @date 2016-2017 * @note peripherals used: GPIO @ref rtc_dcf77_gpio, timer @ref rtc_dcf77_timer */ /* standard libraries */ #include // standard integer types #include // general utilities /* STM32 (including CM3) libraries */ #include // Cortex M3 utilities #include // interrupt handler #include // real-time control clock library #include // general purpose input output library #include // SPI library #include // timer library #include "rtc_dcf77.h" // RTC DCF77 library API #include "global.h" // common methods /** @defgroup rtc_dcf77_gpio output to enable DCF module and input to capture DCF signal * @{ */ #define RTC_DCF77_ENABLE_PORT A /**< GPIO port to enable the module */ #define RTC_DCF77_ENABLE_PIN 2 /**< GPIO pinto enable the module */ #define RTC_DCF77_SIGNAL_PORT A /**< GPIO port to capture the DCF signal */ #define RTC_DCF77_SIGNAL_PIN 3 /**< GPIO pin to capture the DCF signal */ /** @} */ /** @defgroup rtc_dcf77_timer timer to sample DCF77 signal * @{ */ #define RTC_DCF77_TIMER 4 /**< timer peripheral */ /** @} */ volatile bool rtc_dcf77_time_flag = false; struct rtc_dcf77_time_t rtc_dcf77_time = {false, 0, 0, 0, 0, 0, 0, 0, 0}; /**< the received DCF77 frame bits */ static volatile uint64_t rtc_dcf77_frame = 0; /** values of the DCF77 signal over 10 ms for 1 s (how many times it is high) */ static uint8_t rtc_dcf77_bins[100] = {0}; /** the bin shift for the bit phase */ static uint8_t rtc_dcf77_phase = 0; /** the maximum phase value */ static int16_t rtc_dcf77_phase_max = INT16_MIN; /** if the current phase has been verified */ static bool rtc_dcf77_phase_locked = false; /** number of invalid decoding */ static uint8_t rtc_dcf77_invalid = 0; /** maximum number of invalid decoding before switching off */ #define RTC_DCF77_INVALID_MAX 5 void rtc_dcf77_setup(void) { // setup enable output rcc_periph_clock_enable(RCC_GPIO(RTC_DCF77_ENABLE_PORT)); // enable clock GPIO peripheral gpio_set_mode(GPIO(RTC_DCF77_ENABLE_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(RTC_DCF77_ENABLE_PIN)); // set pin to output push-pull to be able to enable the module rtc_dcf77_off(); // disable module at start // setup signal input rcc_periph_clock_enable(RCC_GPIO(RTC_DCF77_SIGNAL_PORT)); // enable clock for signal input peripheral gpio_set_mode(GPIO(RTC_DCF77_SIGNAL_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(RTC_DCF77_SIGNAL_PIN)); // set signal pin to input // setup timer to sample signal at 1kHz rcc_periph_clock_enable(RCC_TIM(RTC_DCF77_TIMER)); // enable clock for timer peripheral timer_reset(TIM(RTC_DCF77_TIMER)); // reset timer state timer_set_mode(TIM(RTC_DCF77_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up timer_set_prescaler(TIM(RTC_DCF77_TIMER), 1); // set prescaler to divide frequency by two, to be able to have a period of 1 kHz (72MHz/2/2^16=549Hz<1kHz) timer_set_period(TIM(RTC_DCF77_TIMER), rcc_ahb_frequency/2/1000-1); // set period to 1kHz (plus hand tuning) timer_clear_flag(TIM(RTC_DCF77_TIMER), TIM_SR_UIF); // clear update event flag timer_update_on_overflow(TIM(RTC_DCF77_TIMER)); // only use counter overflow as UEV source timer_enable_irq(TIM(RTC_DCF77_TIMER), TIM_DIER_UIE); // enable update interrupt for timer nvic_enable_irq(NVIC_TIM_IRQ(RTC_DCF77_TIMER)); // catch interrupt in service routine } void rtc_dcf77_on(void) { if (!gpio_get(GPIO(RTC_DCF77_ENABLE_PORT), GPIO(RTC_DCF77_ENABLE_PIN))) { // receiver is already turned on return; // do nothing } rtc_dcf77_frame = 0; // reset frame rtc_dcf77_phase_locked = false; // reset phase lock rtc_dcf77_phase_max = INT16_MIN; // restart searching for phase rtc_dcf77_invalid = 0; // reset invalid count gpio_clear(GPIO(RTC_DCF77_ENABLE_PORT), GPIO(RTC_DCF77_ENABLE_PIN)); // enable module by pulling pin low timer_set_counter(TIM(RTC_DCF77_TIMER), 0); // reset timer counter timer_enable_counter(TIM(RTC_DCF77_TIMER)); // start timer to sample signal } void rtc_dcf77_off(void) { gpio_set(GPIO(RTC_DCF77_ENABLE_PORT), GPIO(RTC_DCF77_ENABLE_PIN)); // disable module by pull pin high timer_disable_counter(TIM(RTC_DCF77_TIMER)); // stop timer since we don't need to sample anymore } /** decode rtc_dcf77_frame DCF77 frame into rtc_dcf77_time DCF77 time * @note check valid for validity */ static void rtc_dcf77_decode(void) { rtc_dcf77_time.valid = false; // reset validity if (rtc_dcf77_frame==0) { // no time received yet return; } if (!(rtc_dcf77_frame&((uint64_t)1<<20))) { // start of encoded time should always be 1 return; } // check minute parity uint8_t parity = 0; // to check parity for (uint8_t bit=21; bit<=28; bit++) { if (rtc_dcf77_frame&((uint64_t)1<>21)&(0x1))+2*((rtc_dcf77_frame>>22)&(0x1))+4*((rtc_dcf77_frame>>23)&(0x1))+8*((rtc_dcf77_frame>>24)&(0x1))+10*((rtc_dcf77_frame>>25)&(0x1))+20*((rtc_dcf77_frame>>26)&(0x1))+40*((rtc_dcf77_frame>>27)&(0x1)); // read minute (00-59) if (rtc_dcf77_time.minutes>59) { // minutes should not be more than 59 return; } // check hour parity parity = 0; for (uint8_t bit=29; bit<=35; bit++) { if (rtc_dcf77_frame&((uint64_t)1<>29)&(0x1))+2*((rtc_dcf77_frame>>30)&(0x1))+4*((rtc_dcf77_frame>>31)&(0x1))+8*((rtc_dcf77_frame>>32)&(0x1))+10*((rtc_dcf77_frame>>33)&(0x1))+20*((rtc_dcf77_frame>>34)&(0x1)); // read hour (00-23) if (rtc_dcf77_time.hours>23) { // hours should not be more than 23 return; } // check date parity parity = 0; for (uint8_t bit=36; bit<=58; bit++) { if (rtc_dcf77_frame&((uint64_t)1<>36)&(0x1))+2*((rtc_dcf77_frame>>37)&(0x1))+4*((rtc_dcf77_frame>>38)&(0x1))+8*((rtc_dcf77_frame>>39)&(0x1))+10*((rtc_dcf77_frame>>40)&(0x1))+20*((rtc_dcf77_frame>>41)&(0x1)); // read day of the month (01-31) if (rtc_dcf77_time.day==0 || rtc_dcf77_time.day>31) { // day of the month should be 1-31 return; } rtc_dcf77_time.weekday = 1*((rtc_dcf77_frame>>42)&(0x1))+2*((rtc_dcf77_frame>>43)&(0x1))+4*((rtc_dcf77_frame>>44)&(0x1)); // read day of the week (1=Monday - 7=Sunday) if (rtc_dcf77_time.weekday==0 || rtc_dcf77_time.weekday>7) { // day of the week should be 1-7 return; } rtc_dcf77_time.month = 1*((rtc_dcf77_frame>>45)&(0x1))+2*((rtc_dcf77_frame>>46)&(0x1))+4*((rtc_dcf77_frame>>47)&(0x1))+8*((rtc_dcf77_frame>>48)&(0x1))+10*((rtc_dcf77_frame>>49)&(0x1)); // read month of the year (01-12) if (rtc_dcf77_time.month==0 || rtc_dcf77_time.month>12) { // month of the year should be 1-12 return; } rtc_dcf77_time.year = 1*((rtc_dcf77_frame>>50)&(0x1))+2*((rtc_dcf77_frame>>51)&(0x1))+4*((rtc_dcf77_frame>>52)&(0x1))+8*((rtc_dcf77_frame>>53)&(0x1))+10*((rtc_dcf77_frame>>54)&(0x1))+20*((rtc_dcf77_frame>>55)&(0x1))+40*((rtc_dcf77_frame>>56)&(0x1))+80*((rtc_dcf77_frame>>57)&(0x1)); // read year of the century (00-99) if (rtc_dcf77_time.year>99) { // year should be <100 return; } rtc_dcf77_time.valid = true; // if we managed it until here the decoding is successful } /** find phase of 1 seconds DCF77 signal in the bins * searches the complete second for the highest correlation if the phase it not locked * searches only around the last phase if locked * saves the new phase with the highest correlation */ static void rtc_dcf77_phase_detector(void) { uint8_t integral_i = 0; // which bin has the highest integral/correlation int16_t integral_max = 0; // maximum integral value found for (uint8_t start=0; start<(rtc_dcf77_phase_locked ? 10 : LENGTH(rtc_dcf77_bins)); start++) { // which bin has been used to start the convolution (only use +/- 15 bits of previous phase if locked) int16_t integral = 0; // value of the integral for (uint8_t bin=0; binintegral_max) { // we found a better correlation result integral_max = integral; // save new best result integral_i = (start+rtc_dcf77_phase+LENGTH(rtc_dcf77_bins)-5)%LENGTH(rtc_dcf77_bins); // start new best phase start } } if ((int16_t)(integral_max+40)>rtc_dcf77_phase_max) { // only save new phase if it is better than the last one, with some margin to compensate for the drift (perfect correlation = 100, worst correlation = -800) rtc_dcf77_phase_max = integral_max; // save best phase value rtc_dcf77_phase = integral_i; // save bin index corresponding to start of the phase } } /** interrupt service routine called for timer */ void TIM_ISR(RTC_DCF77_TIMER)(void) { static uint8_t bin_state = 0; // how many samples have been stored in the bin static uint8_t bin_i = 0; // current bin filled static uint8_t bit_i = 0; // current bit in the DCF77 minute frame if (timer_get_flag(TIM(RTC_DCF77_TIMER), TIM_SR_UIF)) { // overflow update event happened timer_clear_flag(TIM(RTC_DCF77_TIMER), TIM_SR_UIF); // clear flag // fill bin with current sample state if (gpio_get(GPIO(RTC_DCF77_SIGNAL_PORT), GPIO(RTC_DCF77_SIGNAL_PIN))) { rtc_dcf77_bins[bin_i]++; // only need to increase if the signal is high } bin_state++; // remember we filled the bin if (bin_state>=10) { // bin has 10x1 ms samples, it is now full bin_i = (bin_i+1)%LENGTH(rtc_dcf77_bins); // go to next bin rtc_dcf77_bins[bin_i] = 0; // restart bin bin_state = 0; // restart collecting } if (0==bin_i && 0==bin_state) { // we have 1 s of samples if (bit_i<59) { rtc_dcf77_phase_detector(); // detect phase in signal // check modulation of first 100 ms uint16_t modulation = 0; for (uint8_t bin=0; bin<10; bin++) { modulation += rtc_dcf77_bins[(rtc_dcf77_phase+bin)%LENGTH(rtc_dcf77_bins)]; } if (modulation<50) { // signal is not modulated, it might be the 60th pause bit bit_i = 0; // restart frame rtc_dcf77_frame = 0; // restart frame rtc_dcf77_phase_max = INT16_MIN; // restart searching for phase rtc_dcf77_phase_locked = false; // unlock phase since the decoding seems wrong } else { // modulation detected // check modulation of next 100 ms modulation = 0; for (uint8_t bin=10; bin<20; bin++) { modulation += rtc_dcf77_bins[(rtc_dcf77_phase+bin)%LENGTH(rtc_dcf77_bins)]; } if (modulation<50) { // it's a 0 // bit is already cleared } else { // it's a 1 rtc_dcf77_frame |= (1ULL<=RTC_DCF77_INVALID_MAX) { // too many invalid decoding rtc_dcf77_off(); // switch off receiver so it can re-tune } bit_i = 0; // restart frame rtc_dcf77_frame = 0; // restart frame } } } else { // no other interrupt should occur while (true); // unhandled exception: wait for the watchdog to bite } }