Table of Contents
DCF77 Radio Clock
In this example a decoder for the DCF77 standard-frequency radio signal is realised based on two cooperating state machines. You will learn how to use the features of the code generator.
- Machines based on conditional and event based triggers
- Adding own variables to the machine instance data
- Adding own code to the generated state machine file
- Using an own event type that includes the event and data
DCF77 Basics
The transmitter is located nearby Frankfurt in Germany and continuously transmits a 77.5 kHz signal that encodes the local time using amplitude and phase modulation. The sender is controlled by the high-precision atomic clocks of the German institute of standards (Physikalische Bundesanstalt in Braunschweig). The signal can be received almost everywhere in central Europe with very simple and cheap receivers. A short description about the motivation and the encoding of the DCF-77 signal can be found here here.
The DCF-77 modulation reduces the signal amplitude for 0.1 seconds (short pulse, 0-bit) or 0.2 seconds (long pulse, 1-bit) at the beginning of each second, allowing to transmit 1 bit of data per second. A simple BCD-encoding scheme is used to transmit the hours, minutes, days, and data information for the next following minute. Encoding scheme of the time information transmitted with DCF77 is presented here.
To allow synchronization into the continuously sent signal, no pulse is send during the 59th second, so that the start of the next pulse exactly marks the beginning of the minute.
The receiver application was splitted into two separate state machines.
The pulse detection state machine
The pulse detection state machine runs within a cyclic timer interrupt service routine. It initially searches for the minute start 'M
' and then detects the long and short pulses. This state machine runs cyclically and does not need any external triggers (events). Instead all state transitions are based by the DCF77 output signal level. We call such state machines “conditional machines” because state transitions are based on some input conditions or variable values. The types of transitions are described here in the section about transitions.
The CPU is normally in low-power state. But when a pulse or minute start is detected the decoder machine and wakes up the CPU.
#pragma vector=TIMERA0_VECTOR __interrupt void Timer_A0(void){ // set debug output high if pin of dcf77 signal is high if((P1IN & 0x01) == 1U) P1OUT |= 0x04; else P1OUT &= ~(0x04); // clr debug pin P1OUT |= 0x08; // set debug len of irq dcf77_irq(&irqSM); if(irqSM.msg==evTick || irqSM.msg==evMinStart){ // wake up main CPU, got valid pulse __bic_SR_register_on_exit(LPM3_bits); } P1OUT &= ~(0x08); // clr debug len of irq }
The state machine was created with the integrated state diagram editor. A screenshot is shown here. The editor allows to create state machine diagrams in very efficient way. A multipart tutorial is available here.
The state diagram editor allows to directly add code to various sections of the state machine. In this example some helper functions and variables were added to the begin of the state machine file. More details about the offered possibilities can be found in the tutorial part I in figure 5 . In addition is is also possible to add variables to the state machine instance type. These variables can then be used for data exchange, storing local data etc. In this example we use this possibility to store several internal variables used for the pulse detection state machine.
The decoder state machine
The second state machine runs in the endless main loop and executes once if a pulse was detected. Initially it waits for the minute begin. Once found the state machine processes the events coming from the pulse detection machine (in opposite to the above conditional state machine). The first 20 bits contain special information and are ignored. Then once the minutes bits come the time and date information is captured and decoded. And finally the clock is set before the cycle starts again.
The following code shows the main loop which only runs if a new pulse is available for processing. The state machine uses an own event type (see below) which is filled before the machine is called. Then the low power mode is entered again.
while(1) { _BIS_SR(LPM3_bits + GIE); // Enter LPM3 __no_operation(); P3OUT |= 0x8; // set D2 debug output // update event information and call the decoder decoderEvt.msg = irqSM.msg; decoderEvt.len = irqSM.len; decoder(&decoderSM, &decoderEvt); P3OUT &= ~(0x8); }
Event Type
This example presents another feature of the code generator. The state machine handler can have different signatures. Usually a pointer to the instance data and the event to process, is handed over. Here we have defined an own event type (OWN_DECODER_EVENT_T) which contains not just the event but also some data (the length of the detected pulse).
For this state machine handler signature:
void decoder(DECODER_INSTANCEDATA_T *instanceVar, OWN_DECODER_EVENT_T *userData){...
the following parameters must be set in the configuration file. Note: the integrated editor has as helper function to find the right parameters for you (Code→Handler Signature).
UseInstancePointer=yes HsmFunctionWithEventParameter=yes InlineChangeToStateCode=yes HsmFunctionUserDefinedEventParameter=OWN_DECODER_EVENT_T
Debugging Trace
To better understand the timing of the design some debug signals are generated in the irq handler and the main loop. The following figure shows the generated debug signals. D0 shows a received dcf77 pulse with a len of 100ms. D0 shows the duration of the irq handler routine. And finally D2 shows the execution time of the main loop. You can see that the most time the CPU can stay in sleep mode.
Organization of the Code
Each state machine is in its own folder. Within each folder you will find the state machine model file (*.xml) and the corresponding configuration file. You can open and edit the files easily using the state machine executable (right-click on the file and open with sinelabore.exe). In the tool you can then regenerate the code in the case you made changes. In main.c
the board is initialised and the state machines called.
Conclusion
This tutorial presented how to use conditional and event based state machines to realise a complete application on a small embedded system. The whole code fits in 2kB of code and 110 bytes of ram.
The complete source code and model files are available for download. Feedback is very recommended.