Forum: Mikrocontroller und Digitale Elektronik DCF77 von Peter Danegger


von Jan (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich versuche gerade den Code für einen DCF77 Empfänger von Peter 
Danegger auf einen STM32 zu portieren.

Deswegen habe ich versucht den Code erstmal zu verstehen. Ich finde 
leider nicht mehr die Stelle im Forum, wo ich ihn herruntergeladen habe 
deswegen habe ich ihn noch mal angehängt (war im Rahmen einer Uhr welche 
um drei Uhr nachts dunkeltastet, da dies den Empfang stört). Sicher 
Anfänger Fragen, aber ich wäre sehr froh....

Ich habe mich durch den Code gewühlt folgedes verstehe ich nicht:

Der Timer läuft mit 64 Hz (ca. 15,xx ms) warum auch immer.
Somit müssten 200ms -> 12,8 Takten entsprechen
100ms -> 6,4 Takten
und 1900 ms -> 121,6 Takten

Die Bereiche für 100ms und 200ms passen noch ungefähr (zwischen 3 und 8 
bzw. zwischen 10 und 14). Unsymetrisch sind sie aber auch. Warum wird 
der Snyc (d.h. fehlende Sekundenmarke) mit 120 und 140 ausgewertet?

Warum läuft der Timer nicht in einem geraden Takt z.B. 10ms?


Warum werden die Funktionen scan_dcf77(), clock() in der main aufgerufen 
und nicht direkt an geeigneter Stelle im Interrupt? Wir der dann zu 
lange? Insgesammt müsste es ja "Rechenzeit" sparen.


Wo ich aussteige ist folgende Stelle (insbesondere die letzten 6 Zeilen)
1
  uint8_t PROGMEM BITNO[] = {
2
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,    // minute
3
    0xFF,            // parity
4
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15,      // hour
5
    0xFF,            // parity
6
    0x20, 0x21, 0x22, 0x23, 0x24, 0x25,      // day
7
    0x30, 0x31, 0x32,          // weekday
8
    0x40, 0x41, 0x42, 0x43, 0x44,      // month
9
    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,  // year
10
    0xFF };            // parity
11
  uint8_t PROGMEM BMASK[] = { 1, 2, 4, 8, 10, 20, 40, 80 };
12
13
14
struct time newtime;
15
16
uint8_t dcf77error = 0;
17
uint8_t synchronize = 0;        // successful recieved
18
19
20
void decode_dcf77( uint8_t pulse )
21
{
22
  static uint8_t parity = 0;
23
  uint8_t i;
24
  uint8_t *d;
25
26
  i = newtime.second - 21;
27
  if( i >= sizeof( BITNO ))      // only bit 21 ... 58
28
    return;
29
  parity ^= pulse;        // calculate parity
30
  i = __LPM_classic__(&BITNO[i]);
31
  if( i == 0xFF ){        // test parity
32
    if( parity )
33
      dcf77error = 1;
34
    parity = 0;
35
    return;
36
  }
37
  d = (uint8_t *)&newtime.minute + (i >> 4);    // byte address
38
  i &= 0x0F;          // bit number
39
  if( i == 0 )
40
    *d = 0;          // clear all, if lsb
41
  if( pulse )
42
    *d += __LPM_classic__(&BMASK[i]);      // set bit
43
}

Es ist mir klar was ungefähr passiert. Aber woher kenne ich so genau die 
Adressen von dem newtime Strukt. So 100% verstehe ich es auch nicht. 
klar d wird mit der newtime.minute vorgeladen und dann wird die Adresse 
so weit inkrementiert, dass es gerade immer so passt. Woher weiß man die 
genauen darüberliegenden Adressen? Kann man das auch noch lesbarer 
schreiben? Sicher genialer Code, aber ich habe an mehreren Stellen viel 
Zeit gebraucht.

Danke und viele Grüße
Jan

von foobar (Gast)


Lesenswert?

> Aber woher kenne ich so genau die Adressen von dem newtime Strukt.

Er hat sich das Layout der struct time einmal angeschaut und dann die 
Byteoffsets der Felder in dem Highnibble der BITNO gespeichert. Der Kode 
ist nur bedingt portable - er funktioniert nur auf Systemen mit exakt 
dem gleichen Aufbau der struct time korrekt.

von my2ct (Gast)


Lesenswert?

Jan schrieb:
> Kann man das auch noch lesbarer schreiben? Sicher genialer Code, aber
> ich habe an mehreren Stellen viel Zeit gebraucht.

Früher (tm) war Code-Effizienz wichtiger als Lesbarkeit des Quellcodes.

Heutzutage geht der Trend oft zu total überdimensionieren Prozessoren, 
was dann bis zu 32-Bit Prozessoren für eine simple Uhr mit 
DCF77-Synchronisation reicht.

von W.S. (Gast)


Lesenswert?

Jan schrieb:
> ich versuche gerade den Code für einen DCF77 Empfänger von Peter
> Danegger auf einen STM32 zu portieren.

Das Zeugs ist steinalt und Peter hat in den seit damals verflossenen ca. 
15 Jahren auch dazugelernt, was sinnvolles Strukturieren betrifft.

Mein Vorschlag wäre: selber schreiben anstatt bloß zu kopieren ohne zu 
verstehen.

Eigentlich ist das Ganze doch einfach: richte dir nen Ringpuffer zu 64 
int's ein (ist so ne schöne 2er Potenz), miß die Länge der Pause vom 
letzten DCF77-Impuls und miß die Länge der Impulse, die du dann in den 
Ringpuffer einträgst. Wenn ne Pause deutlich länger ist als 1 Sekunde, 
dann weißt du, daß ne Minute um ist und die zurückliegenden 59 Einträge 
im Ringpuffer ausgewertet werden können. Am Anfang oder bei 
Signalausfall (Pause > 3 Sekunden) füllst du deinen Ringpuffer gnadenlos 
mit 0 und machst weiter. Beim Auswerten siehst du dann ja, ob in den 
zurückliegenden Einträgen Werte zwischen ca. 90 ms und 210 ms liegen und 
damit gültig sind oder 0 und damit ungültig.

Der Rest ist simples Vergleichen auf > 150 ms oder < 150 ms für 1 oder 
0, falls du nicht die Phasenmodulation ausnutzen willst - aber das 
kannst du ohne passenden Empfänger sowieso nicht.

Und aus den Nullen und Einsen solltest du easy deine Zeitinformation 
entnehmen können. Ist m.W. alles BCD.

W.S.

von Wolfgang (Gast)


Lesenswert?

W.S. schrieb:
> Eigentlich ist das Ganze doch einfach: ...

Wo bliebe da der Fortschritt der Welt?

Mit dem STM sollte man doch ausreichend Resourcen haben, um eine 
statistische Signalauswertung zu machen, damit man auch bei schlechtem 
SNR (schwaches Signal, Störungen) das Empfangssignal noch auswerten kann 
und nicht den ganzen Minutendatensatz total verwerfen muss, sobald mal 
in einem Block die Parität nicht stimmte. Irgend einen Vorteil muss so 
ein ein ARM-Prozessor gegenüber einem mindestens 15 Jahre alten 
Vorgänger doch haben ;-)

von HildeK (Gast)


Lesenswert?

Jan schrieb:
> Der Timer läuft mit 64 Hz (ca. 15,xx ms) warum auch immer.

Natürlich wäre das auch mit 10ms gegangen. Die 64 sind ev. irgendwo 
geschickter in der Auswertung wegen Zweierpotenz.

> Die Bereiche für 100ms und 200ms passen noch ungefähr (zwischen 3 und 8
> bzw. zwischen 10 und 14). Unsymetrisch sind sie aber auch.
Kann sein, dass er bei seinem Empfänger gemessen hat, dass die 
Ausgangspulse nicht exakt 100ms und 200ms lang sind. Das habe ich auch 
schon beobachtet und hängt eben vom Empfänger ab. Ist aber unkritisch.

> Warum wird
> der Snyc (d.h. fehlende Sekundenmarke) mit 120 und 140 ausgewertet?
Bei etwa 129 sind zwei Sekunden um seit der 58. Sekunde. Das ist der 
Anlass, die empfangene Zeit in die Anzeige zu übernehmen. Die 120...140 
ist das Fenster, in dem er den Puls für Sekunde 0 erwartet. Ist er 
außerhalb, war es nichts.

Ich hab meinen Code damals auf seinen aufgebaut, aber Details ähnlich 
wie du auch nicht ganz verstanden. PeDa schreibt sehr optimierten und 
kompakten Code.
Mein Teil sieht gerade an der Stelle etwas anders aus, vielleicht hilft 
es dir beim Verständnis:
1
const uint8_t wertigkeit[] = {
2
    1, 2, 4, 8, 16, 32, 64,    // minute  7
3
    0x0,            // parity  1
4
    1, 2, 4, 8, 16, 32,      // hour    6
5
    0x0,            // parity  1
6
    1, 2, 4, 8, 16, 32,      // day     6
7
    1, 2, 4,          // weekday 3
8
    1, 2, 4, 8, 16,        // month   5
9
    1, 2, 4, 8, 16, 32, 64, 128,  // year    8
10
    0x0 };            // parity  1
11
 
12
13
void decode_dcf77(uint8_t pulse)
14
{
15
  uint8_t i;
16
  static uint8_t parity=0;
17
18
  if (!dcf77error)
19
  {
20
    if((dcf_time.second == 17) && pulse && synchronize) mesz=1;  
21
    if((dcf_time.second == 18) && pulse && synchronize) mez=1;  
22
    if((i = dcf_time.second - 21) > 37) // 22, wenn nur Uhrzeit interessiert
23
    {
24
      parity=0;
25
      return; // Trick mit unsigned Überlauf (37 für alles)
26
    }
27
    if (pulse)    // nur Einsen bearbeiten
28
    {
29
      parity++;
30
      if (i<8) dcf_time.minute += wertigkeit[i];    
31
      else if (i<15) dcf_time.hour += wertigkeit[i]; 
32
      else if (i<21) dcf_time.day += wertigkeit[i]; 
33
      else if (i<24) dcf_time.wday += wertigkeit[i]; 
34
      else if (i<29) dcf_time.month += wertigkeit[i]; 
35
      else if (i<37) dcf_time.year += wertigkeit[i]; 
36
    }
37
    if (i==7)  // Sekunde 28
38
    {   
39
      if ((parity&1) == 1) dcf77error=1;   // Parity error Minuten
40
      parity=0; 
41
    } 
42
    if (i==14)  // Sekunde 35
43
    { 
44
      if ((parity&1) == 1) dcf77error=1;   // Parity error Stunden
45
      parity=0; 
46
    } 
47
    if ((i == 37))  // Sekunde 58
48
    { 
49
      if ((parity&1) == 1) dcf77error=1;  // Parity error Datum
50
      parity=0;
51
    } 
52
    if (dcf77error) LED0 = LED_ON;
53
  }
54
}

von Frank (Gast)


Lesenswert?

my2ct schrieb:
> Jan schrieb:
> Heutzutage geht der Trend oft zu total überdimensionieren Prozessoren,
> was dann bis zu 32-Bit Prozessoren für eine simple Uhr mit
> DCF77-Synchronisation reicht.

Vielleicht legt er die 77.5 kHz gleich noch auf den ADC des STM32 und 
baut sich ein DCF77-SDR. Aber auch da gähnt ein STM32 eigentlich nur.

von Jan (Gast)


Lesenswert?

W.S. schrieb:
> Mein Vorschlag wäre: selber schreiben anstatt bloß zu kopieren ohne zu
> verstehen.

Moin, sehe ich anders... ich habe das schon mal im Studium geschrieben 
und wollte jetzt einfach für ein Geburtstagsgeschenk eine Uhr mit 
speziellen funktionen haben ...
Ich wollte es aber verstehen, denn sooo kompliziert ist es nicht... C 
Code ist doch gerade dafür da dass es portierbar ist.


Dann ändere ich die Frage:
Hat jemand einen GUTEN DCF77 Code (am liebsten mit double check, d.h. 
zeit wird nur übernommen, wenn sie zwei minuten hintereinander 
übereinstimmt)...

Dank und Gruß
Jan

my2ct schrieb:
> Heutzutage geht der Trend oft zu total überdimensionieren Prozessoren,
> was dann bis zu 32-Bit Prozessoren für eine simple Uhr mit
> DCF77-Synchronisation reicht.

Und das ist auch gut so... Selbst als Bastler will ich schnell was 
umsetzen können und nicht jedes mal den billigsten kleinsten prozessor 
oder was auch immer suchen. Ich benutze nur noch STM32F103 (1,5 Euro mit 
Platine auf Chinaebay und gut ist). Warum soll ich da noch mit nem 
Attiny oder was auch immer anfangen wenn nicht nötig... Immer den selben 
Code auf anwendungszweck angepasst

von Peter D. (peda)


Lesenswert?

Jan schrieb:
> Der Timer läuft mit 64 Hz (ca. 15,xx ms) warum auch immer.

Die 64Hz habe ich damals genommen, damit ich Bits für 2, 4, 8Hz usw. 
habe, z.B. um ne Led oder nen Kursor blinken zu lassen usw.

Jan schrieb:
> Warum werden die Funktionen scan_dcf77(), clock() in der main aufgerufen

Der Aufruf im Main hat den Vorteil, daß die Zeiten immer gültig sind.
Z.B. für einen Weckzeitvergleich kann es nicht passieren, daß ich die 
Minute vergleiche, dann der Interrupt mir die Stunde unterm Hintern 
wegändert und ich somit danach die falsche Stunde vergleiche.
Auch hatte in meinem Programm das Main nicht viel zu tun, so daß ich 
kein Bit verpassen würde.
Man kann die Auswertung natürlich auch im Interrupt machen.

Jan schrieb:
> Aber woher kenne ich so genau die
> Adressen von dem newtime Strukt.

Die Zahlen 0..5 sind keine Adressen, sondern der Index in dem Array. 
Eine Struct ist ja auch nur ein Array, wo die einzelnen Elemente (Bytes) 
Namen haben.

Das Programm mußte schon gut optimiert werden, da Atmel bei den ersten 
AVRs sehr mit Flash geknausert hat.
Die 20-Pinner hatten max 2kB Flash und die 40-Pinner max 8kB.
Bei den 8051 von Atmel sah das zur gleichen Zeit deutlich besser aus 
(z.B. T89C51CC01: 32kB Flash).

von W.S. (Gast)


Lesenswert?

Wolfgang schrieb:
> Mit dem STM sollte man doch ausreichend Resourcen haben, um..

Es geht doch garnicht um die Ressourcen des verwendeten Controllers, 
sondern um die Ressourcen im Kopf des TO. Das ist ein gewisser 
Unterschied, gelle?

W.S.

von W.S. (Gast)


Lesenswert?

Jan schrieb:
> Dann ändere ich die Frage:
> Hat jemand einen GUTEN DCF77 Code

Genau das ist es, was ich hiermit nochmal kritisiere.

W.S.

von Roland F. (rhf)


Lesenswert?

Hallo,
W.S. schrieb:
> Genau das ist es, was ich hiermit nochmal kritisiere.

Und genau das ist das Grundproblem in diesem Forum:

ENTWEDER
man teilt sein Wissen mit Anderen (siehe auch die Artikelübersicht und 
die Tutorials hier) und sorgt so dafür das nicht immer wieder jeder 
alles neu erfindet und er sein Problem mit der Aneinanderreihung von 
Modulen lösen kann, ohne aber ein tieferes Verständnis zu entwickeln.

ODER
man verlangt das sich Derjenige intensiv mit dem Problem und den 
notwendigen Grundlagen beschäftigt. Das aber kann wiederum zu totalem 
Frust führen, weil sich ein schier unüberwindlich scheinender Berg 
auftut.

rhf

von HildeK (Gast)


Lesenswert?

Dem, Roland, muss ich schon etwas widersprechen. Es sind eben nicht nur 
die beiden "Entweder / Oder" - Fälle.

Roland F. schrieb:
> ENTWEDER
> man teilt sein Wissen mit Anderen (siehe auch die Artikelübersicht und
> die Tutorials hier) und sorgt so dafür das nicht immer wieder jeder
> alles neu erfindet und er sein Problem mit der Aneinanderreihung von
> Modulen lösen kann, ohne aber ein tieferes Verständnis zu entwickeln.
Man bekommt aber schnell ein Erfolgserlebnis. Das beflügelt eventuell 
und weckt beim Anfänger Interesse an der Materie. Und wenn das Projekt 
nicht zu komplex ist, dann beginnt zumindest das Verständnis, wenn 
Interesse vorhanden ist.
Es werden noch genügend Problemchen auftreten und dann sollte man 
unterstützen. Nicht alles ist in Tutorials und aus den Datenblättern zu 
lernen. Das beginnt erst mit einem vorhandenen Basiswissen.

> ODER
> man verlangt das sich Derjenige intensiv mit dem Problem und den
> notwendigen Grundlagen beschäftigt. Das aber kann wiederum zu totalem
> Frust führen, weil sich ein schier unüberwindlich scheinender Berg
> auftut.
Ja, das eher. Hat der Proband aber eine gewisse Ausdauer und man hilft 
ihm z.B. hier, dann ist der Erfolg nachhaltiger.

Der TO hier hat fertigen Code genommen, ok. Er kopiert aber nicht nur, 
sondern versucht ihn zu verstehen, ihn auf eine andere HW anzupassen und 
kann an einigen Stellen den Gedankengang des Autors nicht ohne weiteres 
nachvollziehen. Das passiert öfters ...

Aber gerade hier sucht jemand den Mittelweg zwischen stumpfen Kopieren 
und dem Rad neu erfinden. Solange der Ansatz grundsätzlich auch 
zielführend ist, sollte man dabei unterstützen und nicht nur andere 
Varianten vorschlagen.

Dazu sollte Forum da sein, oder wozu sonst?

von Patrick B. (p51d)


Lesenswert?

Jan schrieb:
> Dann ändere ich die Frage:
> Hat jemand einen GUTEN DCF77 Code (am liebsten mit double check, d.h.
> zeit wird nur übernommen, wenn sie zwei minuten hintereinander
> übereinstimmt)...

Du hast ja schon Ansätze für eine stabile Auswertung:
- Zahlenbereiche Prüfen
- Zahlenfolge über Pakete sinnvoll (1,3,2,6 etc.)
- Stabile Analyse von Impulslängen mit z.B. Morphologischen Filtern...


Da gibt's viele Ansätze. Ob das aber jemand so zur Verfügung stellt 
weiss ich nicht. Dazu kommt noch, dass bei keinem Empfang auch die beste 
Auswertung nichts erfinden kann.

: Bearbeitet durch User
von Jan (Gast)


Lesenswert?

W.S. schrieb:
> Genau das ist es, was ich hiermit nochmal kritisiere.

meine herren, sind hier dummschwätzer unter wegs. Noch mal ich habe so 
einen Code vor langer Zeit schon mal geschrieben. das einzige was jetzt 
zählt ist schnell eine funktionierenden code zusammen zu dengeln. Ich 
will nicht lernen wie man das signal bestmöglich auswertet, ich habe 
keine lust auf fehlersuche etc. ich will einem freund eine freude zum 
geburstag machen. Du trägst nichts dazu bei sondern laberst nur käse. 
such dir bitte einen andern thread zum spielen.

@Peter: Vielen Dank, hier habe ich was gelernt.
@HildeK: Vielen Dank, hier habe ich auch was gelernt.

ich dengel mal den codeschnipsel hier und den code von peter zusammen 
und hoffe das ich ihn schnell zum laufen bekomme.

werde den code dann vielleicht posten, damit sich leute wieder aufregen 
können wie schlecht er ist und sich wieder aufgeilen können. jeder 
braucht ein ziel im leben :-)

von Manfred (Gast)


Lesenswert?

Roland F. schrieb:
> ... er sein Problem mit der Aneinanderreihung von Modulen lösen kann, ohne aber 
ein tieferes Verständnis zu entwickeln.

Mit dieser Ansicht solltest Du Dir ein anderes Forum suchen.

Es ist schade, dass nun auch das Mikrocontroller.net von Kiddies und 
faulen Studenten überrant wird, der Anspruch war wohl eher, 
Lösungskompetenz und Fachwissen zu vermitteln.

Ich selbst habe bei eigenen Problemchen auch schon heftig unfreundliche 
Antworten bezogen, aber diese waren geeignet, meinen Denkapparat zu 
aktivieren und die Lösung zu finden. Fertig vorgekaut ist hier nicht 
angesagt, Punkt.

Jan schrieb:
> meine herren, sind hier dummschwätzer unter wegs.

Hau ab, dieser Tonfall und zu faul, ordentlich zu schreiben, kommt nicht 
gut an.

von Jan (Gast)


Lesenswert?

Manfred schrieb:
> Roland F. schrieb:
>> ... er sein Problem mit der Aneinanderreihung von Modulen lösen kann, ohne aber
> ein tieferes Verständnis zu entwickeln.
>
> Mit dieser Ansicht solltest Du Dir ein anderes Forum suchen.
>
> Es ist schade, dass nun auch das Mikrocontroller.net von Kiddies und
> faulen Studenten überrant wird, der Anspruch war wohl eher,
> Lösungskompetenz und Fachwissen zu vermitteln.
>
> Ich selbst habe bei eigenen Problemchen auch schon heftig unfreundliche
> Antworten bezogen, aber diese waren geeignet, meinen Denkapparat zu
> aktivieren und die Lösung zu finden. Fertig vorgekaut ist hier nicht
> angesagt, Punkt.
>
> Jan schrieb:
>> meine herren, sind hier dummschwätzer unter wegs.
>
> Hau ab, dieser Tonfall und zu faul, ordentlich zu schreiben, kommt nicht
> gut an.

Manfred, seit wann definierst du den Zweck dieses Forums. Man darf doch 
unterschiedliche Ziele haben (welche ich geäußert habe). Der Text war 
mit dem Handy verfasst, für die Form entschuldige ich mich. Wo machst du 
denn den Punkt? Darfst du die Forumsoftware überhaupt benutzen ohne sie 
selbst geschrieben zu haben? Oder deinen Prozessor baust du dir bestimmt 
aus selbstgebauten Transistoren auf (Silizium ziehst du bestimmt auch 
selbst) :-). Noch mal ich will ein Geschenk schnell zusammenbauen.
Aber.. Danke für deinen Beitrag KOMPLETT ohne sinnvollen Inhalt bzgl. 
des Themas. Auch an dich die Bitte spamme andere Threads voll.

Ich versuche mich morgen mal an den Code zu setzten, dann können wir 
über was fachliches reden....

von Peter D. (peda)


Lesenswert?

Ich mag keine if-else Monster, da kann man zu schnell Fehler machen und 
es sieht immer unübersichtlich aus. Daher versuche ich eine Regel zu 
finden, mit der ich alle Bits gemeinsam behandeln kann.
Die Tabelle würde ich heute als Struct-Array mit 2 Bytes aufbauen, d.h. 
Struct-Element-Index und Bitwertigkeit getrennt. Dann erspart man sich 
die 2. Tabelle. Beim AVR-GCC war das ein ziemliches Gewürge 
(PROGMEM-Macro usw.), konstante Daten im Flash abzulegen, daher dieser 
Umweg über 2 Nibble.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.