CuVoodoo STM32F1 firmware template
rtc_dcf77.c
Go to the documentation of this file.
1 /* This program is free software: you can redistribute it and/or modify
2  * it under the terms of the GNU General Public License as published by
3  * the Free Software Foundation, either version 3 of the License, or
4  * (at your option) any later version.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9  * GNU General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public License
12  * along with this program. If not, see <http://www.gnu.org/licenses/>.
13  *
14  */
22 /* standard libraries */
23 #include <stdint.h> // standard integer types
24 #include <stdlib.h> // general utilities
25 
26 /* STM32 (including CM3) libraries */
27 #include <libopencmsis/core_cm3.h> // Cortex M3 utilities
28 #include <libopencm3/cm3/nvic.h> // interrupt handler
29 #include <libopencm3/stm32/rcc.h> // real-time control clock library
30 #include <libopencm3/stm32/gpio.h> // general purpose input output library
31 #include <libopencm3/stm32/spi.h> // SPI library
32 #include <libopencm3/stm32/timer.h> // timer library
33 
34 #include "rtc_dcf77.h" // RTC DCF77 library API
35 #include "global.h" // common methods
36 
40 #define RTC_DCF77_ENABLE_PORT A
41 #define RTC_DCF77_ENABLE_PIN 2
42 #define RTC_DCF77_SIGNAL_PORT A
43 #define RTC_DCF77_SIGNAL_PIN 3
49 #define RTC_DCF77_TIMER 4
52 volatile bool rtc_dcf77_time_flag = false;
53 struct rtc_dcf77_time_t rtc_dcf77_time = {false, 0, 0, 0, 0, 0, 0, 0, 0};
54 
56 static volatile uint64_t rtc_dcf77_frame = 0;
58 static uint8_t rtc_dcf77_bins[100] = {0};
60 static uint8_t rtc_dcf77_phase = 0;
62 static int16_t rtc_dcf77_phase_max = INT16_MIN;
64 static bool rtc_dcf77_phase_locked = false;
66 static uint8_t rtc_dcf77_invalid = 0;
68 #define RTC_DCF77_INVALID_MAX 5
69 
70 void rtc_dcf77_setup(void)
71 {
72  // setup enable output
73  rcc_periph_clock_enable(RCC_GPIO(RTC_DCF77_ENABLE_PORT)); // enable clock GPIO peripheral
74  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
75  rtc_dcf77_off(); // disable module at start
76 
77  // setup signal input
78  rcc_periph_clock_enable(RCC_GPIO(RTC_DCF77_SIGNAL_PORT)); // enable clock for signal input peripheral
79  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
80 
81  // setup timer to sample signal at 1kHz
82  rcc_periph_clock_enable(RCC_TIM(RTC_DCF77_TIMER)); // enable clock for timer peripheral
83  timer_reset(TIM(RTC_DCF77_TIMER)); // reset timer state
84  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
85  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)
86  timer_set_period(TIM(RTC_DCF77_TIMER), rcc_ahb_frequency/2/1000-1); // set period to 1kHz (plus hand tuning)
87  timer_clear_flag(TIM(RTC_DCF77_TIMER), TIM_SR_UIF); // clear update event flag
88  timer_update_on_overflow(TIM(RTC_DCF77_TIMER)); // only use counter overflow as UEV source
89  timer_enable_irq(TIM(RTC_DCF77_TIMER), TIM_DIER_UIE); // enable update interrupt for timer
90  nvic_enable_irq(NVIC_TIM_IRQ(RTC_DCF77_TIMER)); // catch interrupt in service routine
91 }
92 
93 void rtc_dcf77_on(void)
94 {
95  if (!gpio_get(GPIO(RTC_DCF77_ENABLE_PORT), GPIO(RTC_DCF77_ENABLE_PIN))) { // receiver is already turned on
96  return; // do nothing
97  }
98  rtc_dcf77_frame = 0; // reset frame
99  rtc_dcf77_phase_locked = false; // reset phase lock
100  rtc_dcf77_phase_max = INT16_MIN; // restart searching for phase
101  rtc_dcf77_invalid = 0; // reset invalid count
102  gpio_clear(GPIO(RTC_DCF77_ENABLE_PORT), GPIO(RTC_DCF77_ENABLE_PIN)); // enable module by pulling pin low
103  timer_set_counter(TIM(RTC_DCF77_TIMER), 0); // reset timer counter
104  timer_enable_counter(TIM(RTC_DCF77_TIMER)); // start timer to sample signal
105 }
106 
107 void rtc_dcf77_off(void)
108 {
109  gpio_set(GPIO(RTC_DCF77_ENABLE_PORT), GPIO(RTC_DCF77_ENABLE_PIN)); // disable module by pull pin high
110  timer_disable_counter(TIM(RTC_DCF77_TIMER)); // stop timer since we don't need to sample anymore
111 }
112 
116 static void rtc_dcf77_decode(void)
117 {
118  rtc_dcf77_time.valid = false; // reset validity
119 
120  if (rtc_dcf77_frame==0) { // no time received yet
121  return;
122  }
123  if (!(rtc_dcf77_frame&((uint64_t)1<<20))) { // start of encoded time should always be 1
124  return;
125  }
126 
127  // check minute parity
128  uint8_t parity = 0; // to check parity
129  for (uint8_t bit=21; bit<=28; bit++) {
130  if (rtc_dcf77_frame&((uint64_t)1<<bit)) {
131  parity++; // count the set bits
132  }
133  }
134  if (parity%2) { // parity should be even
135  return;
136  }
137  rtc_dcf77_time.minutes = 1*((rtc_dcf77_frame>>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)
138  if (rtc_dcf77_time.minutes>59) { // minutes should not be more than 59
139  return;
140  }
141 
142  // check hour parity
143  parity = 0;
144  for (uint8_t bit=29; bit<=35; bit++) {
145  if (rtc_dcf77_frame&((uint64_t)1<<bit)) {
146  parity++; // count the set bits
147  }
148  }
149  if (parity%2) { // parity should be even
150  return;
151  }
152  rtc_dcf77_time.hours = 1*((rtc_dcf77_frame>>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)
153  if (rtc_dcf77_time.hours>23) { // hours should not be more than 23
154  return;
155  }
156 
157  // check date parity
158  parity = 0;
159  for (uint8_t bit=36; bit<=58; bit++) {
160  if (rtc_dcf77_frame&((uint64_t)1<<bit)) {
161  parity++; // count the set bits
162  }
163  }
164  if (parity%2) { // parity should be even
165  return;
166  }
167  rtc_dcf77_time.day = 1*((rtc_dcf77_frame>>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)
168  if (rtc_dcf77_time.day==0 || rtc_dcf77_time.day>31) { // day of the month should be 1-31
169  return;
170  }
171  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)
172  if (rtc_dcf77_time.weekday==0 || rtc_dcf77_time.weekday>7) { // day of the week should be 1-7
173  return;
174  }
175  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)
176  if (rtc_dcf77_time.month==0 || rtc_dcf77_time.month>12) { // month of the year should be 1-12
177  return;
178  }
179  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)
180  if (rtc_dcf77_time.year>99) { // year should be <100
181  return;
182  }
183 
184  rtc_dcf77_time.valid = true; // if we managed it until here the decoding is successful
185 }
186 
192 static void rtc_dcf77_phase_detector(void) {
193  uint8_t integral_i = 0; // which bin has the highest integral/correlation
194  int16_t integral_max = 0; // maximum integral value found
195 
196  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)
197  int16_t integral = 0; // value of the integral
198  for (uint8_t bin=0; bin<LENGTH(rtc_dcf77_bins); bin++) { // go through bins to calculate correlation
199  int8_t dfc77_signal = -1; // the signal of the reference DCF77 signal
200  if (bin<10) { // the signal is always high for the first 100 ms
201  dfc77_signal = 1; // use highest values
202  } else if (bin<20) { // the signal has 50% chance of being high for the next 100 ms (encoding the bit)
203  dfc77_signal = 0; // use middle value
204  }
205  // the rest of the time the signal is low (keep lowest value)
206  integral += rtc_dcf77_bins[(start+bin+rtc_dcf77_phase+LENGTH(rtc_dcf77_bins)-5)%LENGTH(rtc_dcf77_bins)]*dfc77_signal; // calculate the correlation at this point and integrate it (start with previous phase - 15 bins)
207  }
208  if (integral>integral_max) { // we found a better correlation result
209  integral_max = integral; // save new best result
210  integral_i = (start+rtc_dcf77_phase+LENGTH(rtc_dcf77_bins)-5)%LENGTH(rtc_dcf77_bins); // start new best phase start
211  }
212  }
213 
214  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)
215  rtc_dcf77_phase_max = integral_max; // save best phase value
216  rtc_dcf77_phase = integral_i; // save bin index corresponding to start of the phase
217  }
218 }
219 
222 {
223  static uint8_t bin_state = 0; // how many samples have been stored in the bin
224  static uint8_t bin_i = 0; // current bin filled
225  static uint8_t bit_i = 0; // current bit in the DCF77 minute frame
226  if (timer_get_flag(TIM(RTC_DCF77_TIMER), TIM_SR_UIF)) { // overflow update event happened
227  timer_clear_flag(TIM(RTC_DCF77_TIMER), TIM_SR_UIF); // clear flag
228  // fill bin with current sample state
230  rtc_dcf77_bins[bin_i]++; // only need to increase if the signal is high
231  }
232  bin_state++; // remember we filled the bin
233 
234  if (bin_state>=10) { // bin has 10x1 ms samples, it is now full
235  bin_i = (bin_i+1)%LENGTH(rtc_dcf77_bins); // go to next bin
236  rtc_dcf77_bins[bin_i] = 0; // restart bin
237  bin_state = 0; // restart collecting
238  }
239 
240  if (0==bin_i && 0==bin_state) { // we have 1 s of samples
241  if (bit_i<59) {
242  rtc_dcf77_phase_detector(); // detect phase in signal
243  // check modulation of first 100 ms
244  uint16_t modulation = 0;
245  for (uint8_t bin=0; bin<10; bin++) {
246  modulation += rtc_dcf77_bins[(rtc_dcf77_phase+bin)%LENGTH(rtc_dcf77_bins)];
247  }
248  if (modulation<50) { // signal is not modulated, it might be the 60th pause bit
249  bit_i = 0; // restart frame
250  rtc_dcf77_frame = 0; // restart frame
251  rtc_dcf77_phase_max = INT16_MIN; // restart searching for phase
252  rtc_dcf77_phase_locked = false; // unlock phase since the decoding seems wrong
253  } else { // modulation detected
254  // check modulation of next 100 ms
255  modulation = 0;
256  for (uint8_t bin=10; bin<20; bin++) {
257  modulation += rtc_dcf77_bins[(rtc_dcf77_phase+bin)%LENGTH(rtc_dcf77_bins)];
258  }
259  if (modulation<50) { // it's a 0
260  // bit is already cleared
261  } else { // it's a 1
262  rtc_dcf77_frame |= (1ULL<<bit_i); // set bit
263  }
264  bit_i++; // go to next bit
265  }
266  } else { // complete DCF77 frame received
267  rtc_dcf77_decode(); // decode frame
268  if (rtc_dcf77_time.valid) { // decoded time is valid
269  rtc_dcf77_time.milliseconds = rtc_dcf77_phase*10; // save milliseconds corresponding to phase
270  rtc_dcf77_phase_locked = true; // lock phase since decoding succeeded
271  rtc_dcf77_invalid = 0; // remember we had an valid decoding
272  rtc_dcf77_time_flag = true; // notify user we have time
273  } else {
274  rtc_dcf77_invalid++; // remember we had an invalid decoding
275  }
276  if (rtc_dcf77_invalid>=RTC_DCF77_INVALID_MAX) { // too many invalid decoding
277  rtc_dcf77_off(); // switch off receiver so it can re-tune
278  }
279  bit_i = 0; // restart frame
280  rtc_dcf77_frame = 0; // restart frame
281  }
282  }
283  } else { // no other interrupt should occur
284  while (true); // unhandled exception: wait for the watchdog to bite
285  }
286 }
#define RTC_DCF77_ENABLE_PIN
GPIO pinto enable the module.
Definition: rtc_dcf77.c:41
static void rtc_dcf77_phase_detector(void)
find phase of 1 seconds DCF77 signal in the bins searches the complete second for the highest correla...
Definition: rtc_dcf77.c:192
#define TIM_ISR(x)
get interrupt service routine for timer base on TIM identifier
Definition: global.h:113
#define RCC_GPIO(x)
get RCC for GPIO based on GPIO identifier
Definition: global.h:105
static bool rtc_dcf77_phase_locked
if the current phase has been verified
Definition: rtc_dcf77.c:64
#define NVIC_TIM_IRQ(x)
get NVIC IRQ for timer base on TIM identifier
Definition: global.h:111
uint8_t minutes
minutes (00-49)
Definition: rtc_dcf77.h:31
void rtc_dcf77_setup(void)
setup DCF77 time receiver module
Definition: rtc_dcf77.c:70
#define RTC_DCF77_SIGNAL_PORT
GPIO port to capture the DCF signal.
Definition: rtc_dcf77.c:42
global definitions and methods (API)
#define GPIO(x)
get GPIO based on GPIO identifier
Definition: global.h:103
void rtc_dcf77_off(void)
switch off DCF77 time receiver module
Definition: rtc_dcf77.c:107
static int16_t rtc_dcf77_phase_max
the maximum phase value
Definition: rtc_dcf77.c:62
#define RCC_TIM(x)
get RCC for timer based on TIM identifier
Definition: global.h:109
uint8_t month
month (01-12)
Definition: rtc_dcf77.h:35
decoded DCF77 time received
Definition: rtc_dcf77.h:27
#define RTC_DCF77_ENABLE_PORT
GPIO port to enable the module.
Definition: rtc_dcf77.c:40
static uint8_t rtc_dcf77_phase
the bin shift for the bit phase
Definition: rtc_dcf77.c:60
#define RTC_DCF77_INVALID_MAX
maximum number of invalid decoding before switching off
Definition: rtc_dcf77.c:68
volatile bool rtc_dcf77_time_flag
set when time information has been received
Definition: rtc_dcf77.c:52
struct rtc_dcf77_time_t rtc_dcf77_time
the received DCF77 frame bits
Definition: rtc_dcf77.c:53
#define RTC_DCF77_SIGNAL_PIN
GPIO pin to capture the DCF signal.
Definition: rtc_dcf77.c:43
uint8_t hours
hours (00-23)
Definition: rtc_dcf77.h:32
uint8_t weekday
day of the week (1-7=Monday-Sunday)
Definition: rtc_dcf77.h:34
bool valid
if the time is valid
Definition: rtc_dcf77.h:28
uint8_t year
year within century (00-99)
Definition: rtc_dcf77.h:36
#define LENGTH(x)
get the length of an array
Definition: global.h:26
#define RTC_DCF77_TIMER
timer peripheral
Definition: rtc_dcf77.c:49
uint8_t day
day of the month (01-31)
Definition: rtc_dcf77.h:33
#define TIM(x)
get TIM based on TIM identifier
Definition: global.h:107
library to get time from a DCF77 module (API)
static void rtc_dcf77_decode(void)
decode rtc_dcf77_frame DCF77 frame into rtc_dcf77_time DCF77 time
Definition: rtc_dcf77.c:116
static uint8_t rtc_dcf77_bins[100]
values of the DCF77 signal over 10 ms for 1 s (how many times it is high)
Definition: rtc_dcf77.c:58
uint8_t milliseconds
milliseconds (00-99)
Definition: rtc_dcf77.h:29
static uint8_t rtc_dcf77_invalid
number of invalid decoding
Definition: rtc_dcf77.c:66
void rtc_dcf77_on(void)
switch on DCF77 time receiver module
Definition: rtc_dcf77.c:93