Ein 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 | Einheit | Minimal | Typisch | Maximal |
Betriebsspannung | V | 1,2 | 3 | 3,3 |
Stromaufnahme | µA | <90 | 120 | |
Empfangsfrequenz | KHz | 40,0 | 77,5 | 100 |
Frequenztoleranz (Antenne) | Hz | -300 | +300 | |
Empfindlichkeit | uV/m | 80 | ||
Arbeitstemperatur | °C | -40 | +85 | |
Lagertemperatur | °C | -55 | +85 |
Pin-Belegung:
Name | Funktion |
VDD | Betriebsspannung |
GND | Masse |
DATA | DCF-Ausgang |
PON | Power 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.
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):
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-Nummer | Beschreibung |
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 |
59 | keine Absenkung der Ampltitude (benötigt zur Synchronisierung) |
Schaltplan
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_*/
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
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