Project 1

Reading time info from a DCF77 radio clock module

Basics

The radio clock signal DCF77 is broadcasted from long wave transmitter located in a village called Mainflingen near Frankfurt (which is only a few kilometers from where I'm living) and can be received within a radius of about 2000km. Thus it's possible to receive a very precise time information in almost every part of Europe. BTW: the name DCF77 is composed from 'D' for Germany ('Deutschland' in German), 'C' as identifier for a long-wave trasmitter, 'F' for Frankfurt and the 77 for the carrier wave frequency (77,5kHz).
Each second the signal amplitude is dropped for a short time period - the length of this period determines whether it should be interpreted as 0 or 1. A low period of 100ms means a logical 0, a period of 200ms a logical 1. The last second of each minute (the 59th) is skipped to mark the beginning of the new minute. The meaning of the resulting 59 bits carrying information is defined as follows:
 
0 begin of minute (always 0?)
1-14 reserved
15 reserve antenna activated
16 announcement: dts(summer time)/no dts change
17 day time saving (summer time) active
18 day time daving (summer time) not active
19 announcement: leap second
20 begin of time information (always 1)
21-27 minute
28 parity minute
29-34 hour
35 parity hour
36-41 day
42-44 weekday
45-49 month
50-57 year
58 parity date

Switching from day time saving to non-dst is announced by setting bit 16 for one hour. Also for an hour bit 19 is set to announce a leap second. The minute in which the leap second is inserted is 61 seconds long - then bit 59 is transmitted as logical 0 and bit 60 is skipped to mark the beginning of the next minute.
The time and date information is BCD encoded which means the bit values are

1, 2, 4, 8, 10, 20, 40, 80
and not as usual
1, 2, 4, 8, 16, 32, 64, 128.
The parity bit states if the sum of bits (each with value 1) is even (0) or odd (1).


Hardware

I used the DCF77 module from Conrad (BN 641138-66/ 19,95 DM) as DCF77 receiver. The output misleadingly called "inverted" in the data sheet has to be connected to Port D3 which can raise an external interrupt 1 (INT1). Then you only need +5V at connector 2 and Ground at connector 1 and you're ready. I didn't need any pull-up resistor - I'm not sure if that's because of the eva board's design or the enabled (?) internal pull-ups.
As usual, I used my STK200 eva board with an 8515 running at 4MHz. To use a 8535, you just need to use the appropriate run time library (crt1-8535). For other frequencies the definition of F_CPU has to be changed accordingly (in TIMER.H). When  using smaller parts without UART you can't use the module UART.c of course. The remaining stuff should run on every AVR part when using appropriate run time libraries.


The AVR program

I tried to split the program in modules, to make these modules re-usable in other projects. A short description of the modules follows:
 
main.c Initializes all functions and provides callback functions.
timer.c, timer.h Initialisation and reading timer 1 for period measurement.
uart.c, uart.h Sending blocks of data to UART.
dcf77.c, dcf77.h Decoding the time signal.
led.h Definitions for LED toggling
global.h globale definitions (e.g. typedefs)

The most important module is DCF77.C of course. I contains the interrupt handler for external INT1 as well as the decoding of the time signal. Without going to deep into details of implementation, just this: because alternated triggering for falling and rising edges is needed, the control register is configured inside the INT1 handler accordingly. The falling edge marks the start of a new second, the passed time until the rising edge is measured and used to dertemine whether a logical 0 (<0.15s) or a logical 1 (<0.25s) was trasmitted. Just so the time between the rising and the next falling edge is measured: if it's >1.5s, the skipped 59th second was detected and the next falling edge marks second 0.
On the rising as well as on the falling edge, callback routines can be called (dcf77_call_fall, dcf77_call_rise). How this works can be examined in the main program. The callback functions are used to send the decoded time info to the UART and to toggle status LEDs. The LEDs are used as follows:
 

LED 0 Shows input signal. 'off' means low input level, 'on' mean high level.
LED 1 Shows if the skipped 59th second was detected and thus the current second is known ('on' = yes).
LED 2 Shows state of last received bit ('on' = 1, 'off' = 0).

The decoded time info is stored in a buffer of 9 bytes length. The bytes have the following meanings:
 

0 dcf77_start starting byte: always 0xfe
1 dcf77_second current second (0-59)
2 dcf77_minute minute (0-59)
3 dcf77_hour hour (0-11)
4 dcf77_day day (1-31)
5 dcf77_wday weekday (1-7, 1: Montag, ... , 7: Sonntag)
6 dcf77_month month (1-12)
7 dcf77_year year (0-99)
8 dcf77_status status bits

Be aware that the values for minute, hour etc. are not valid before the beginning of the next minute. Furthermore, the second variable never reaches 59, since this second is not sent. The 9th byte contains status bit which are defined as follows:
 

0 DCF77_STATUS_BIT last bit received 
1 DCF77_STATUS_INIT skipped 59th bit detected (1 = yes)
2 DCF77_STATUS_MIN_VALID minute information valid (1 = yes)
3 DCF77_STATUS_HOUR_VALID hour information valid (1 = yes)
4 DCF77_STATUS_DATE_VALID date information valid (1 = yes)

The 1st bit (BIT) makes it possible to decode the whole time info outside the module using the current second (dcf77_second). Though this doesn't make much sense, this makes information visible to the outside which are ignored by DCF77 (like announcement of leap second and dst switching).
The bit INIT indicates if the skipped 59th second was detected, which means the seconds are synchronized. If this bit is zero, the value in dcf77_second is not the current second, but just a synchronizing counter which is incremented every second. Accordingly, all other time info is also invalid if this bit is zero.
The bits MIN_VALID, HOUR_VALID and DATE_VALID indicate if the parity bit of the corresponding time information was correct and that the begin of time information was marked tht right way (bit 20 == 1 ?).


The PC program

To make the decoded time information visible, I wrote a small program that receives the data decoded by the microcontroller via the UART and prints a formatted output. Unfortunately for users of Linux, DOS and other operating systems apart from Windows9x/NT, I just did a Windows9x/NT version. Since each OS handles using the serial port differently, the program has to be adjusted accordingly. If you already used the serial port in your own programs under your OS, this should be easy to do. The port settings are: 9600 baud, 8 data bits, 1 stop bit, no parity, no handshaking (neither Xon/Xoff nor RTS/DTR).
The program's first parameter is the com port, the second one the output format (not needed):
dcf77 <comport> [format]
e.g.: dcf77 com2: 2
The format parameter values have the following meanings:
 
0 output bitwise ('*' = 1, '-' = 0).
1 output the 9 received bytes as raw hex numbers.
2 formatted output of date and time [default]

The formatted output shows the currently decoded information. So the minute is increased at the 28th second, while a real clock changes the minute only if the second reaches 0 (this also true for the remaining info of course).
To compile the sources you need either MS Visual C++ (5.0) or CygWin32 (20.1). Project files as well as a makefile for Visual C are included (dcf77.mak); the makefile for Cygwin is named 'Makefile'. By changing the LANGUAGE definition you can create either a German (LANGUAGE=0) or English (LANGUAGE=1) version.


Download

AVR program
PC program



back to the homepage