MSP430 DCF77 (Empfang und Auswertung)

dcf1_modul_pollin_backEin kleines Beispiel-Programm (geschrieben in C), mit dem ein DCF77 Signal ausgewertet werden kann. Über ein Display (Nokia-Display) werden Uhrzeit und Datum angezeigt. Weiterhin werden die empfangenen Bits während des Empfangs auf dem Display angezeigt (und aktuelle Bit-Nummer bzw. Sekunde). Für den DCF77-Empfang kommt ein fertiges Modul von Pollin zum Einsatz: “DCF-Empfangsmodul DCF1“. Für die Auswertung kommt ein MSP430G2452 zum Einsatz.

Video

DCF-Empfangsmodul DCF1 von Pollin

Die Daten wurden von dem mitgelieferten Datenblatt übernommen. Um einen sauberen Signalverlauf zu erhalten, ist es sinnvoll das Modul über eine Batterie zu betreiben. Ich habe in diesem Beispiel den MSP430 und das DCF77-Modul über eine 9V Block-Batterie am laufen (mit 3V3 LDO).

Technische Daten:

 Parameter EinheitMinimal Typisch Maximal
 BetriebsspannungV 1,233,3
 Stromaufnahme µA <90120
 Empfangsfrequenz KHz40,077,5100
 Frequenztoleranz (Antenne) Hz -300 +300
 Empfindlichkeit uV/m 80  
 Arbeitstemperatur °C -40  +85
 Lagertemperatur°C -55  +85

Pin-Belegung:

NameFunktion
VDDBetriebsspannung
GNDMasse
DATADCF-Ausgang
PONPower On/Down

Hinweise:

  • DATA=GND wenn die Trägeramplitude das Maximum erreicht.
  • DATA=VDD wenn die Trägeramplitude absinkt (moduliert wird).
  • PON=GND -> Empfangsteil ist eingeschaltet.
  • PON=VDD -> Empfangsteil ist ausgeschaltet.
  • Beim Wechsel von PON=VDD zu PON=GND startet das Empfangsteil bei fallender Flanke.
  • Bei ausgeschaltenem Empfangsteil gilt DATA=GND.
  • DATA ist current source/sink mit Iout > 5µA.

dcf1_modul_pollin_front

dcf1_modul_pollin_back

DCF77-Signal

Alle Informationen zum DCF77-Signal finden sich auf Wikipedia und vielen anderen Seiten. Ich beziehe mich hier auf die Infos von Wikipedia.
Insgesamt werden innerhalb einer Minute 59 Bits übertragen, die Gesamtdauer der Übertragung beträgt also 59 Sekunden. Die Periodendauer eines Bits beträgt also 1000ms oder 1s. Um sich mit dem DCF77-Signal zu synchronisieren, gibt es bei dem 59. Bit keine Amplitudenabsenkung. Danach beginnt eine neue Übertragung bzw, eine neue Minute. Um zwischen einer logischen “0” und einer logischen “1” zu unterscheiden, wird die Amplitude für 100ms (logische “0”) oder 200ms (logische “1”) auf 15-25% abgesenkt. Wenn man sich die Hinweise von dem DCF77-Modul von Pollin anschaut, ist das Signal am DATA-Port invertiert. Also muss der µC die Zeit zählen, solange der DATA-Port auf “High” steht (die Low-Zeiten kann man natürlich auch zählen: 800ms bzw. 900ms). Die empfangenen Bits werden in einem  8 Byte großen Array abgespeichert (platz für 64 Bits, 58 Bits werden benötigt).

Der theoretische Verlauf des Signals (als Beispiel):

dcf_signalverlauf_theoretisch

Signal-Aufbau:

Nach jeder Teilinformationen, z.B. Minute, erfolgt ein Paritätsbit (Fehlererkennung). Mit diesem Bit kann die Richtigkeit der übertragenden Information überprüft werden. Die Parität ist beim DCF77-Signal gerade, bedeutet: Bei einer geraden Anzahl von Einsen ist das Paritätsbit = 0.

0011010 => 3 Einsen => ungerade Anzahl => Paritätsbit = 1
1010011 => 4 Einsen => gerade Anzahl => Paritätsbit = 0

Bei der Übertragung des Paritätsbit kann es natürlich auch zu einen Übertragungsfehler kommen (muss bei einer richtigen Funkuhr natürlich abgefangen werden, z.B. über Vergleich der alten + neuen Zeit).

Bit-NummerBeschreibung
0 neue Minute (immer logisch “0”)
1-14 Wetterinformationen (MeteoTime) + Katastrophenschutz
15 Rufbit
16 logisch “1” = Ende der Stunde MEZ/MESZ Umstellung
17 logisch “0” = MEZ, logisch “1” = MESZ
18 logisch “0” = MESZ, logisch “1” = MEZ
19 logisch “1” = Ende der Stunde wird Schaltsekunde eingefügt
20 Beginn Zeitinformation (immer logisch “1”)
21 Minute, entspricht Dezimal = “1”
22 Minute, entspricht Dezimal = “2”
23 Minute, entspricht Dezimal = “4”
24 Minute, entspricht Dezimal = “8”
25 Minute, entspricht Dezimal = “10”
26 Minute, entspricht Dezimal = “20”
27 Minute, entspricht Dezimal = “40”
28 Minute, Paritätbit
29 Stunde, entspricht Dezimal = “1”
30 Stunde, entspricht Dezimal = “2”
31 Stunde, entspricht Dezimal = “4”
32 Stunde, entspricht Dezimal = “8”
33 Stunde, entspricht Dezimal = “10”
34 Stunde, entspricht Dezimal = “20”
35 Stunde, Paritätbit
36 Kalendertag, entspricht Dezimal = “1”
37 Kalendertag, entspricht Dezimal = “2”
38 Kalendertag, entspricht Dezimal = “4”
39 Kalendertag, entspricht Dezimal = “8”
40 Kalendertag, entspricht Dezimal = “10”
41 Kalendertag, entspricht Dezimal = “20”
42 Wochentag, entspricht Dezimal = “1”
43 Wochentag, entspricht Dezimal = “2”
44 Wochentag, entspricht Dezimal = “4”
45 Monatsnummer, entspricht Dezimal = “1”
46 Monatsnummer, entspricht Dezimal = “2”
47 Monatsnummer, entspricht Dezimal = “4”
48 Monatsnummer, entspricht Dezimal = “8”
49 Monatsnummer, entspricht Dezimal = “10”
50 Jahr, entspricht Dezimal = “1”
51 Jahr, entspricht Dezimal = “2”
52 Jahr, entspricht Dezimal = “4”
53 Jahr, entspricht Dezimal = “8”
54 Jahr, entspricht Dezimal = “10”
55 Jahr, entspricht Dezimal = “20”
56 Jahr, entspricht Dezimal = “40”
57 Jahr, entspricht Dezimal = “80”
58 Jahr, Paritätdatum
59keine Absenkung der Ampltitude (benötigt zur Synchronisierung)

Schaltplan

msp430_dcf77

C-Programm

Kompletter Source-Code: DOWNLOAD SOURCE-CODE

main.c

/*******************************
 *          __                   
 *         /\ \                  
 *  __  _  \_\ \     __    ___   
 * /\ \/'\ /'_' \  /'__'\ /'___\ 
 * \/>  <//\ \ \ \/\  __//\ \__/ 
 *  /\_/\_\ \___,_\ \____\ \____\
 *  \//\/_/\/__,_ /\/____/\/____/
 * 
 *  Author: declis (xdec.de)
 ********************************/ 

#include <msp430g2452.h>
#include "lib_dcf77.h"
#include "lib_lcd.h"
#include "delay.h"

void main(void)
{
	unsigned int count=0,count_low=0;
	unsigned char data[8];
	unsigned char bit_num=0,x=0,y=4;
	unsigned char start=0,no_signal=0;

	WDTCTL=WDTPW+WDTHOLD;					// stop WDT
	BCSCTL1=CALBC1_1MHZ;       				// SMCLK ~1MHz
  	DCOCTL=CALDCO_1MHZ;
	P1DIR&=~dcf_d_port;						// DCF77 data port

  	init_USI();
  	init_LCD();

  	write_char(0,2,antenna_sym,1);			// Antenna symbol

	while(1)
	{
		write_string(x,y,wait_sync,0);
		// wait for start condition
		// (59th bit is always low for 1000ms) 
		while(!start)	
		{		
			// while data port in low state -> count time
			// if "no_signal" is set, we will wait for a changing level at data port 
			while(!(P1IN&dcf_d_port)&&!no_signal)	
			{
				wait_ms(1);					// wait ~1ms
				count++;					// count time (x*1ms)

				if(count>2000)				// if no changing level detected -> no signal
				{							
					count=0;				// reset count (low time)
					no_signal=1;			// set "no_signal"
					write_char(sig_sym_x,sig_sym_y,'x',1);	// "no signal" symbol	
				}
			}
			// if "no_signal" is set, wait for changing level at data port 
			// this will not detect a bad (malfunction) signal
			if(no_signal&&(P1IN&dcf_d_port))
			{
				no_signal=0;				// signal detected, reset no_signal
				count=0;					// reset count (low time)
			}
			// low time must be >1s (59th bit always zero for 1s, plus 0. Bit (800 or 900ms)
			if(count>1000&&count<2000&&!no_signal)
			{								 
				start=1;					// a new minute started
				count=0;		
			}
			// if time is not in range (maybe no sync bit detected or no signal)
			else 
			{
				count=0;					// wait and count again
				if(!no_signal) 				// signal seems to be OK	
					write_char(sig_sym_x,sig_sym_y,sig_sym,1); 	
				else write_char(sig_sym_x,sig_sym_y,'x',1);	// no signal	
			}
		}

		clean_area(x,6*(sizeof(wait_sync)-1),y,y);

		// stop at bit 58
		while(bit_num<=58)
		{
			// count time if data port is high
			while(P1IN&dcf_d_port)			
			{
				wait_ms(1);
				count++;	
			}
			// if counted time ~100ms -> logical 0
			if(count>80&&count<120)			
			{
				data[bit_num/8]&=~(1<<bit_num%8);	
				char2num_out(14,7,bit_num,0);
				bit_num++;		
				count_low=0;
				write_char(x++,y,'0',0);	
			}
			// if counted time ~200ms -> logical 1
			if(count>180&&count<220)		
			{
				data[bit_num/8]|=(1<<bit_num%8);
				char2num_out(14,7,bit_num,0);
				bit_num++;
				count_low=0;
				write_char(x++,y,'1',0);
			}			
			// check low time of data port
			if(!(P1IN&dcf_d_port))
			{
				wait_ms(1);
				count_low++;
				// signal lost, start at sync loop again
				if(count_low>1000)
				{
					count_low=0;
					write_char(sig_sym_x,sig_sym_y,'x',1);
					bit_num=59; // will break while()
					no_signal=1;
				}
			}
			// display width check
			if(x>15)
			{
				 y++; 
				 x=0;
			}	
			count=0;
		}

		if(!no_signal)
		{
			if(parity_test(29,35,data)||
				parity_test(21,28,data)||
				parity_test(36,58,data))
				write_char(15,7,'x',0);		// parity test only for fun
			else
			{
				char2num_out(4,0,bit_decode(29,34,data),2);	// hour
				write_char(8,0,':',2);
				char2num_out(10,0,bit_decode(21,27,data),2);// minute
				char2num_out(3,2,bit_decode(36,41,data),1);	// day
				write_char(5,2,'.',1);
				char2num_out(6,2,bit_decode(45,49,data),1);	// month
				write_char(8,2,'.',1);
				char2num_out(9,2,bit_decode(50,57,data),1);	// year
				write_char(12,2,'(',1);
				write_string(13,2,day_name[bit_decode(42,44,data)-1],1);	
				write_char(15,2,')',1);
			}
		}
		count_low=0;
		count=0;
		start=0;
		bit_num=0;
		x=0;
		y=4;
		clean_area(0,lcd_width-1,y,lcd_height_b-1);
	}
}

lib_dcf77.c

/*******************************
 *          __                   
 *         /\ \                  
 *  __  _  \_\ \     __    ___   
 * /\ \/'\ /'_' \  /'__'\ /'___\ 
 * \/>  <//\ \ \ \/\  __//\ \__/ 
 *  /\_/\_\ \___,_\ \____\ \____\
 *  \//\/_/\/__,_ /\/____/\/____/
 * 
 *  Author: declis (xdec.de)
 ********************************/ 

unsigned char bit_test(unsigned char,unsigned char*);
const char bit_value[]={1,2,4,8,10,20,40,80};
unsigned char value,i;

unsigned char bit_decode(unsigned char bit_start,unsigned char bit_end,unsigned char *array)
{
	value=0;i=0;

	while(bit_start<=bit_end)
	{
		if(bit_test(bit_start,array))
			value+=bit_value[i];
		i++;
		bit_start++;
	}
	return value;
}

unsigned char parity_test(unsigned char bit_start,unsigned char bit_end,unsigned char *array)
{
	i=0;value=bit_test(bit_end,array);

	while(bit_start<bit_end)
	{
		if(bit_test(bit_start,array))
			i++;
		bit_start++;
	}

	if(i%2==value) // even parity => paritybit = 0
		return 0;
	return 1;
}

unsigned char bit_test(unsigned char bit_num,unsigned char *array)
{
	if(array[bit_num/8]&(1<<bit_num%8))
		return 1;
	return 0;
}

lib_dcf77.h

/*******************************
 *          __                   
 *         /\ \                  
 *  __  _  \_\ \     __    ___   
 * /\ \/'\ /'_' \  /'__'\ /'___\ 
 * \/>  <//\ \ \ \/\  __//\ \__/ 
 *  /\_/\_\ \___,_\ \____\ \____\
 *  \//\/_/\/__,_ /\/____/\/____/
 * 
 *  Author: declis (xdec.de)
 ********************************/ 

#ifndef LIB_DCF77_H_
#define LIB_DCF77_H_

#define dcf_d_port		0x0002		// "DCF77 Data Port" at P1.1

#define sig_sym_x		1			// signal symbol x-pos
#define sig_sym_y		0			// signal symbol y-pos
#define sig_sym			2			// signal symbol at address 0x02 (ascii_char.h)
#define antenna_sym		1			// antenna symbol at address 0x01 (ascii_char.h)

const char day_name[][3]={"MO","DI","MI","DO","FR","SA","SO"};
const char wait_sync[]={"wait sync"};

unsigned char bit_decode(unsigned char,unsigned char,unsigned char*);
unsigned char parity_test(unsigned char,unsigned char,unsigned char*);

#endif /*LIB_DCF77_H_*/

2 thoughts on “MSP430 DCF77 (Empfang und Auswertung)

  1. Martin

    Hallo,

    ich habe 2 Fragen zu diesem Projekt.
    1.kannst du mir die Funktionsweise der Pins des LCD (PCD8544) erklären?
    2.und kannst du mir bitte anhand des Programms erklären, warum du die Pins LCD(Nokia) an µC so angeschlossen hast?

    danke

    Reply
  2. declis Post author

    Hi,

    zu 1)
    Alle wichtigen Infos zu den Pins und zum Übertragungsprotokoll finden sich im Datenblatt: PCD8544
    Vcc = positive Versorgungsspannung
    Vss = Ground (Masse)
    RST = Reset
    CLK = Clock Eingang (SPI-Clock)
    SI = Daten Eingang (SPI-Data)
    CSB = Chip Select (muss auf logisch “0” gesetzt werden bevor eine Datenübertragung gestartet werden kann)
    D/C = Data / Command (logische 1 für Daten die im Display-RAM landen, logische 0 für Commands -> Cursor setzen etc.)
    VOUT = Booster

    zu 2)
    Der MSP4302452 besitzt eine Hardware-Schnittstelle. Diese kann als SPI oder I2C konfiguriert werden. In diesem Programm wird die Schnittstelle natürlich im SPI-Modus verwendet, weil das Display SPI besitzt. Wenn man in das Datenblatt von dem µC schaut, kann man sich die verschiedenen Funktionen der GPIOs angucken. Diese muss man dann entsprechend für SPI einstellen:

    P1SEL|=SDIN+SCLK;
    P1SEL2|=SDIN+SCLK;

    siehe:
    MSP430 Nokia 3310 LCD

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

This site uses Akismet to reduce spam. Learn how your comment data is processed.