DCF77 clock (freeRTOS, Arduino Due)

error reports
pfd document of the project

DCF77 clock, Arduino Due, FreeRTOS


How can one write code for (extremely accurate) clock, without RTOS?



This is homepage of freeRTOS: The FreeRTOS™ Kernel., if you are new to it, read Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.

For my program, chapters about tasks and notification are most important.

Maybe RTOS can scare you, but realize that you need only three, four simple commands and the result will be easier program flow and better control!

I own Arduino Due, so I need freeRTOS port for this board, after some search, I've found this: https://github.com/greiman/FreeRTOS-Arduino.

More about freeRTOS I will tell, when I come to tell about the program.

Programming language, programming environment

My first idea, was to learn Ada, but I realized that this is my very first project, that Ada has probably no so good support as C++ (that I already master), so this is C/C++.

I was try the Eclipse but failed. I have installed Atmel Studio – it works, but the examples are very difficult (for me) to modify – best and easiest is Visual Studio 2017 with vMicro (Arduino) add-in.


DCF77 is a German long-wave time signal and standard-frequency radio station, wikipedia.org/wiki/DCF77.
Some details of leap-second: dcf77-time-code.html.

The DCF77 use as well as AM modulation a phase-modulation, this is very interesting and will be my next project, an example demodulator, see here: zeit_und_frequenz/pdf/5_1988_Hetzel_-_Proc_EFTF_88.pdf.

As an illustration, let see output from my current program, received bits writes from left to right:

recived bits
Note that while the clock shows 21:36, the data indicates the next minute, i.e. 37.

A little more interesting than to see for incoming bits is to register all errors, I do it now.

If you need detailed knowledge about CDF77 signal, see dcf77logs.de.

The program

Program's design


An important part of the system is (DS3231 (rtc)) – this is extremely accurate (±2 pm) I2C real-time clock (RTC). The program uses four rtc's properties:

  1. Is the source of time, when the clock is out of sync.
  2. It's 32768 Hz output is used as the source of the interrupts.
  3. Hour, day, week_day, month, and year from rtc is used to count expected bit sequence, which is used to find minute start.
  4. Possibility to compensate for small frequency drift..


Use of FreeRTOS is very easy and gives lots of benefits – think of „Arduino loop” (task in freeRTOS), which has priority, can communicate with other loops (tasks).
Copy FreeRTOS files under library catalog; in the *.ino file add #include "FreeRTOS_ARM.h", and in the setup function create tasks and start scheduler:

        #include "FreeRTOS_ARM.h"
        // ...
        void setup( )
        // ...

	    // as interrupt signal clock 2^15 Hz=32768  from CS3231 RTC
	    attachInterrupt( hardware::irqPin, process_one_sample, FALLING );

	    // freeRTOS code
	    res = xTaskCreate( get_delay_task, (const char* const) "Delay", 500, NULL, 3, &get_delay_task_handle );
	    configASSERT( res == pdPASS );

	    res = xTaskCreate( minut_sync_task, (const char* const) "Min_sync", 1000, NULL, 3, &min_sync_task_handle );
	    configASSERT( res == pdPASS );

	    res = xTaskCreate( flush_task, (const char* const) "Flush", 500, NULL, 3, &flush_task_handle );
	    configASSERT( res == pdPASS );

	    res = xTaskCreate( maintaining_task, "bgr", 500, NULL, 2, NULL );
	    configASSERT( res == pdPASS );

	    /* Start the scheduler so the created tasks start executing. */
	    vTaskStartScheduler( );
	    Serial.println( "Die" );
        // no code here
        void loop {}

Tasks get_delay_task, minut_sync_task and flush_task are waiting for a notification from Interrupt service routine. Code template is:

        /*  task is a regular void C-function with void* parameter
         *  following OS function says: wait for notification
         *  ulEventsToProcess = ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
        void get_delay_task( void *pvParameters )
            volatile static UBaseType_t integral = 0;
            UBaseType_t ulEventsToProcess;

            // forever
            for (;;)
                // wait until be notified
                ulEventsToProcess = ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
                if (ulEventsToProcess != 0)
                    // do jobb

The program's flow diagram is clean, compact ISR fills the bins, and make sums for bit encoding. 200 ms, 500 ms and 990ms after second start, tasks are notify to do they job. The fourth task is working independly and correct rtc's frequency drift.

will continue ...

clock prototype


Visby, Sweden

2019 © by kris jaxa: jaxasoft