Hallo zusammen, Für ein Projekt brauchte ich ein DCF77-Interface, allerdings mit etwas anderen Anforderungen, weswegen nix was ich gefunden habe so richtig gepasst hat. Daher eben selbst gemacht. ich wills euch nciht vorenthalten, und bin für Anregungen und kritik sehr dankbar (AVR ist noch etwas Neuland für mich) Man verzeihe mir die "Arduino"-Restln die noch drinnen sind. Meine Anforderungen waren: - DCF-Interface vom Conrad - kein LCD oder so, sondern möglichst "roh" - kein Polling, sondern per Pin Change Interrupt - möglichst wenig Abhängigkeit vom Timer, da dieser (zusammen mit demselben Pin Change Interrupt) noch für weitere Signale gebraucht wird (im Code aber nciht enthalten) - Keine Synchronisierung irgendeiner Uhr, da in weiterer Folge nur "ab und zu" eine batteriegepufferte Echtzeituhr nachsynchronisiert werden soll - deshalb auch keine Sekundensynchronisation - deshalb auch wenig bis gar keine Fehlerkorrektur (bis auf Spike-Unterdrückung), es reicht wenn alle paar Stunden (oder in der Nacht) einmal ein korrektes Signal empfangen wird, und der DS1307 nachgeführt wird. - möglichst wenig Arbeit in der ISR Grundsätzliche Funktionsbeschreibung: Der Pin Change Interrupt wird verwendet, um die Signale vom DCF-Empfänger aufzuzeichnen. Timer 1 läuft "Vollgas" also ohne Vorteiler, mit Overflow bei 65536. Das Board läuft mit 16 MHz, sodass der overflow mit ~244 Hz erhöht wird, bzw. alle ~4msec. Diese 4msec reichen als Auflösung für die DCF-Decodierung, deshalb wird der eigentliche Timer-Wert auch gar nicht verwendet (wird aber ausgelesen, da später für was anderes benötigt) In der PCINT2_vect werden die geänderten Bits ausmaskiert, und der (momentan einzige) Zweig für das DCF-Bit angesprungen (wird später um weitere zweige erweitert) Je nach steigender oder fallender Flanke wird das Timing ermittelt, zu kurze Signale werden als Spike behandelt und ignoriert. Die lange Pause des Minutensignals wird verwendet, um a) den Bit-Zähler zurückzusetzen, und b) falls vorher ein vollständiger und fehlerfreier Bitstrom ermittelt werden konnte, dieser per "valid-Flag" dem Hauptprogramm mitgeteilt. kurze pausen werden als 0 oder 1 erkannt und in den Bit-Puffer geschrieben. Ein zu langes Signal wird als Fehler vermerkt, und der gesamte Frame als fehlerhaft markiert. Im Hauptprogramm braucht man nur warten bis DCF.valid gesetzt ist, dann sollte man (schnell) die Daten verarbeiten, da das nächste erkannte Bit die Daten wieder invalidiert (der selbe Puffer wird wiederverwendet bzw. überschrieben). Mit DCF_validate sollte man vorher noch diverse Plausibilitätskontrollen (Parity, Bereichsprüfungen) durchführen. Eine kleine Besonderheit stellt das Dekodieren des bitstromes dar: Dies erledigt "der Compiler" indem ein entsprechendes großes Bitfeld deklariert wird. So, und nun her mit der Kritik! lg Michi Nachtrag: die Timing-Werte vertragen vielleicht noch massiv Optimierung, momentan wird davon ausgegangen dass ein overflow alle 4 msec auftritt, d.h. ein Wert von 100 entspricht 400 msec....
Michael Reinelt schrieb: > Für ein Projekt brauchte ich ein DCF77-Interface, allerdings mit etwas > anderen Anforderungen, ...und damit für den Rest der Welt unbrauchbar.
holger schrieb: > Michael Reinelt schrieb: >> Für ein Projekt brauchte ich ein DCF77-Interface, allerdings mit etwas >> anderen Anforderungen, > > ...und damit für den Rest der Welt unbrauchbar. Warum????
Michael Reinelt schrieb: > holger schrieb: >> Michael Reinelt schrieb: >>> Für ein Projekt brauchte ich ein DCF77-Interface, allerdings mit etwas >>> anderen Anforderungen, >> >> ...und damit für den Rest der Welt unbrauchbar. > > Warum???? weil Stunden/Minuten in Zehner/Einer getrennt sind und damit nicht sinnvoll weiterverwendet werden können.
holger schrieb: > weil Stunden/Minuten in Zehner/Einer getrennt sind und damit nicht > sinnvoll weiterverwendet werden können. O welch unlösbares Problem für den Rest der Welt... Hast du dich schon mal mit dem DS1307 auseinandergesetzt? Der muss absolut unbrauchbar sein, weil der hat Zehner und Einer getrennt. eigenartig, der scheint aber trotzdem recht weit verbreitet zu sein... im übrigen: von sich selbst auf den Rest der Welt zu schließen, zeugt schon von einer gewissen.. ähh.. aja, wir wollten ja höflich sein :-)
> Der muss absolut unbrauchbar sein, weil der hat Zehner und Einer getrennt.
die "Timekeeper Registers" sind 8 bit breit und BCD kodiert, da ist
nichts getrennt.
Michael Reinelt schrieb: > - kein Polling, sondern per Pin Change Interrupt Ich verwende mit Absicht Polling im Timer, da dann Störflanken besser unterdrückt werden. Außerdem senkt das die CPU-Last, wenn das Signal stark gestört ist. Michael Reinelt schrieb: > - möglichst wenig Abhängigkeit vom Timer Ich realisiere das auch. Einen Timer hat man ja immer laufen und da bietet sich das Polling z.B. alle 10ms geradezu an. Michael Reinelt schrieb: > eine batteriegepufferte Echtzeituhr nachsynchronisiert werden > soll Kannst Du bitte mal erklären, warum? Ich benutzte entweder eine RTC mit Batterie (d.h. T2 am 32kHz Quarz) oder DCF77. Aber beides zusammen macht irgendwie keinen Sinn. Michael Reinelt schrieb: > Dies > erledigt "der Compiler" indem ein entsprechendes großes Bitfeld > deklariert wird. Würde mich mal interessieren, wie riesig der erzeugte Assemblercode dafür ist. Ich mach das Dekodieren für jedes Bit in "knallharter" Echtzeit direkt alle Sekunde. Das ist schön wenig Code und minimalste CPU-Belastung. Peter
holger schrieb: >> Der muss absolut unbrauchbar sein, weil der hat Zehner und Einer getrennt. > > die "Timekeeper Registers" sind 8 bit breit und BCD kodiert, da ist > nichts getrennt. Wo ist jetzt der Unterschied zwischen BCD-codiert und getrennt? Ach vergiss es...
@Peter: kann ich mal deinen Code sehen? ohne diskutiert es sich etwas schwer...
Danke Peter. Werd ich mir morgen früh reinziehen und dann in epischer Breite antworten :-)
Michael Reinelt schrieb: > Daher eben selbst gemacht. Und das klassische Beispiel dafür, daß selbst gemacht nicht immer auch gut gemacht bedeutet... > Wo ist jetzt der Unterschied zwischen BCD-codiert und getrennt? Aua. Wenn du das schon fragen musst...
Arno Müller schrieb: > Michael Reinelt schrieb: >> Daher eben selbst gemacht. > > Und das klassische Beispiel dafür, daß selbst gemacht nicht immer auch > gut gemacht bedeutet... Manno. Kannst du ausser stänkern auch noch was? >> Wo ist jetzt der Unterschied zwischen BCD-codiert und getrennt? > > Aua. Wenn du das schon fragen musst... Manno. Kannst du ausser stänkern auch noch was?
> Manno. Kannst du ausser stänkern auch noch was?
Scheinbar nicht, denn sonst hätte er was besseres präsentiert.
Hallo Peter, ich hab mir nun deinen Code angesehen. Sehr elegant! > Ich verwende mit Absicht Polling im Timer, da dann Störflanken besser > unterdrückt werden. > Außerdem senkt das die CPU-Last, wenn das Signal stark gestört ist. So richtige Störflanken-Unterdrückung hab ich in deinem Code nicht gefunden. Das Problem beim Polling ist halt, dass wenn du grad zufällig während eines Spikes pollst, du ein falsches Ergebnis kriegst. ich hab hier am "Arbeitsplatz" tagsüber einen sehr schlechten Empfang: WLAN-AP daneben, Mobiltelefon daneben, Notebook daneben, Bluetooth... Störungen ohne Ende. Trotzdem krieg ich erstaunlicherweise sehr wenige Störflanken rein (allerdings auch kaum ein gültiges Signal). irgendwie scheint das DCF-Modul da schon einiges zu unterdrücken... > Ich realisiere das auch. Einen Timer hat man ja immer laufen und da > bietet sich das Polling z.B. alle 10ms geradezu an. Ok, verstehe. wie schon gesagt - sehr elegant :-) >> eine batteriegepufferte Echtzeituhr nachsynchronisiert werden >> soll > > Kannst Du bitte mal erklären, warum? > Ich benutzte entweder eine RTC mit Batterie (d.h. T2 am 32kHz Quarz) > oder DCF77. Aber beides zusammen macht irgendwie keinen Sinn. Im Endausbau soll das eine Langzeit-Messung/Überwachung meiner Heizung werden. Da möchte ich einigermaßen verläßliche Zeitinfo haben, die auch batteriegepuffert ist (deswegen der DS1307). Andererseits weiss ich nicht wie genau der DS1307 ist (z.B. Abweichung in einem Jahr) außerdem gibts da ja noch die Sommerzeit... deshalb das DCF77 zusätzlich. Das Ding wird im Keller stehen, da hab ich zwar schlechten Empfang, aber erstaunlicherweise krieg ich doch alle ~15 Minuten einen gültigen (und korrekten) Frame. Das reicht dicke. >> Dies erledigt "der Compiler" indem ein entsprechendes großes Bitfeld >> deklariert wird. > > Würde mich mal interessieren, wie riesig der erzeugte Assemblercode > dafür ist. Ohne den Assembler-Code jetzt angesehen zu haben: beim Schreiben der DCF77-Bits wird das Bitfeld gar nicht verwendet (der alte "union" trick), nur beim Auslesen. Hier gehe ich davon aus dass der Compiler das sehr gut optimiert, jeweils ein shift und ein &, mehr sollte da nicht übrigbleiben. Was ich in deinem Code jetzt nicht gefunden habe: - Sommerzeit-Erkennung (sollte aber sehr einfach nachzurüsten sein) - diverse Plausibilitätsprüfungen (Wertebereiche z.B. für Monat: 1..12) lg Michi
Peter Dannegger schrieb: > Kannst Du bitte mal erklären, warum? > Ich benutzte entweder eine RTC mit Batterie (d.h. T2 am 32kHz Quarz) > oder DCF77. Aber beides zusammen macht irgendwie keinen Sinn. Die meisten (batteriebetriebenen) Funkuhren machen das ebenso, einfach um Strom zu sparen, meist wird dann in der Nacht synchronisiert da dort auch die wenigsten Störungen vorhanden sind.
Hallo Michael, super Projekt :) Ich denke, dass das Projekt durchaus für den Rest der Welt brauchbar ist. Das kritische ist das Herausfiltern von Störpulsen und die korrekte Erkennung von '0' und '1'. Die Auswertung ist nun nicht weiter schwierig. Das Datums- und Zeitformat kann wohl jeder für sich anpassen. Echtzeituhr ist bei mir auch vorhanden und wird zyklisch mit dem DCF abgeglichen. Das zuverlässigste Zeitsignal liefert nun mal die DCF77, von der Sommer- und Winterzeitumstellung mal abgesehen. Also eine durchaus sinnvolle Kombination. Ich habe auch ein Projekt mit ähnlichen Anforderungen. Mein ATxmega hat keinen externen Quarz und läuft mit dem (ungenauen) internen 2MHz Oszillator. Von dem Oversampling bin ich auch wieder weg, da auch Störpulse statistisch gesehen erfasst werden und zu einem falschen Ergebnis führen können. Die genaueste Lösung ist wohl über den Pin-Change IRQ die Pulsbreite auszuwerten und zu kurze Pulse zu verwerfen. Geht besser als das Oversampling. Machen Dir die Pulsbreiten Probleme, die größer als die Spikepulsbreite und kleiner als die minimale Pulsbreite für eine '0' liegen? Wie fängst Du kurze Unterbrechungen der '0' und '1' Pulse ab? Meine Lösung sieht Deiner sehr ähnlich. Meine Software wartet auf zwei aufeinanderfolgende gültige Pulse. Daraus erkenne ich, ob die Starbedingung für eine neue Minute vorliegt oder ob der Sekundenpuls (von steigender Flanke zu steigender Flanke) im Rahmen der Messgenauigkeit zuverlässig war Mein Probleme liegen allerdings darin, - dass die Pulse vom Pollin-Modul in der Breite teilweise stark variieren - der intern verwendeten Oszillator relativ ungenau ist und damit der 10ms-Basistimer auch nicht so genau ist (bei mir 9,7ms statt 10ms) Um die Flanken müssen dann schon große Toleranzbereiche gelegt werden, damit der Algorithmus funktioniert. Meine Überlegungen gehen noch dahin, ob es vielleicht sinnvoll ist die Pulsbreite mit den Capture-Compare IRQ auszumessen. Damit würde der 10ms-Timer nicht mehr benötigt. Ob das Vorteile hat, werde ich nochmal ausprobieren. Karsten
karsten b. schrieb: > Machen Dir die Pulsbreiten Probleme, die größer als die > Spikepulsbreite und kleiner als die minimale Pulsbreite für eine '0' > liegen? Die behandle ich als "Spike" > Wie fängst Du kurze Unterbrechungen der '0' und '1' Pulse ab? Führt entweder zu einer falschen Erkennung, oder wird als Spike ignoriert, wenn die Zeit ausserhalb der gültigen grenzen liegt karsten b. schrieb: > Um die Flanken müssen dann schon große Toleranzbereiche gelegt werden, > damit der Algorithmus funktioniert. Das mach ich auch so. Generell würd ich den Code so nicht mehr schreiben, sondern samplen. Und vor allem: Die Idee mit dem riesigen Bitfeld ist, wie Peter Danegger schon richtig erkannt hat, nicht so gut. Das Auswerten und die Plausibilitätsprüfung sind aufgrund der vielen bit-Operationen sehr aufwändig. Damals habe ich peters Code nciht verstanden, heute würde ich seinen als basis verwenden.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.