Forum: Mikrocontroller und Digitale Elektronik DCF77 weiteres Vorgehen


von Florian (Gast)


Lesenswert?

Hi

Ich möchte eine DCF77 Uhr entwickeln. Ich habe mich entschieden diese 
Entwicklung in mehrere Teile aufzuteilen. Als erstes habe ich nun ein 
Programm geschrieben, welches mir auf einem LCD anzeigt ob das Signal, 
welches ich am Eingang habe eine logische Null oder eine logische Eins 
ist. Ausserdem wird angezeigt wie lange das Signal ist. Das habe ich so 
realisiert: Ein Timer wird im CTC Modus betrieben. Dieser Verursacht 
alle 10ms einen Interrupt. Nun wird "nachgeschaut" ob der Pin, an 
welchem das DCF Modul hängt 0 oder 1 ist. Dementsprechend wird eine 
Variable erhöht. Wenn nun 500ms kein Signal mehr am Eingang ist, wird 
ausgewertet ob eine 0 oder 1 übertragen wurde.
Ich verwende einen Atmega32 mit Standard Takt (1 Mhz)

Mein Quellcode:
1
/*
2
 */
3
4
#include <avr/io.h>
5
#include <avr/interrupt.h>
6
#include "lcd-routines.h"
7
8
uint8_t high = 0; // Variablen um die Signalläge zu zählen
9
uint8_t low = 0;
10
11
ISR(TIMER0_COMP_vect) {  //Wird alle 10ms ausgeführt
12
    if(PINA & (1 << 0)) {   // Wenn DCF Eingang = 1,
13
        high++; // erhöhe die high Variable
14
    }
15
16
    if(!(PINA & (1 << 0))) {    // Wenn DCF eingang = 0,
17
        low++; // erhöhe die low Variable
18
    }
19
20
21
}
22
23
int main(void)
24
{
25
26
    TCCR0 |= (1 << WGM01) | (1 << CS02); // CTC Modus, Prescaler 256
27
    TIMSK |= (1 << OCIE0); // Interrupt erlauben
28
    OCR0 = 35; // Wert für Overflow
29
    sei(); // Interrupts global aktivieren
30
31
    lcd_init(); // LCD initialisieren
32
33
34
    while(1) {
35
36
        if(low > 50) { // Wenn seit 500ms der DCF Eingang = 0
37
            low = 0; // Setzte low wieder zurück
38
39
            if(high > 5 && high < 15) { // Wenn das high Signal zwischen 50ms und 150ms lang ist, (Logische 0)
40
                lcd_setcursor(0, 1); // LCD Cursor setzen
41
                lcd_string("0"); // 0 auf dem LCD ausgeben
42
43
                lcd_setcursor(5, 1); // LCD Cursor setzen
44
                char buffer0[20]; // Buffer für utoa
45
                utoa (high, buffer0, 10); // Umwandeln der int Variable in eine Char Variable
46
                lcd_string(buffer0); // Char Variable auf dem LCD ausgeben
47
                high = 0; // high Variable zurücksetzen
48
49
            }
50
51
            else if(high > 15 && high < 25) { // Wenn das high Signal zwischen 150ms und 250ms lang ist, (Logische 1)
52
                lcd_setcursor(0, 1); // LCD Cursor setzen
53
                lcd_string("1"); // 1 auf dem LCD ausgeben
54
55
                lcd_setcursor(5, 1); // LCD Cursor setzen
56
                char buffer1[20]; // Buffer für utoa
57
                utoa (high, buffer1, 10); // Umwandeln der int Variable in eine Char Variable
58
                lcd_string(buffer1); // Char Variable auf dem LCD ausgeben
59
                high = 0; // high Variable zurücksetzen
60
            }
61
        }
62
    }
63
64
    return 0;
65
}

Dieser Code funktioniert einwandfrei, ich weiss allerdings nicht wie ich 
nun weitermachen soll? Ich wäre sehr dankbar für eine Anregung.

Freundliche Grüsse
Florian

PS: Bitte verzeiht mir meine Rechtschreibfehler und meinen (sehr 
wahrscheinlich) nicht sehr "schönen" Code, da ich erst 12 Jahre alt bin. 
Falls es verboten ist, in diesem Alter hier zu posten (Ich konnte dazu 
in den Regeln nichts finden), bitte ich um Entschuldigung und verstehe 
natürlich wenn der Thread von einem Moderator gelöscht wird.$

PPS: Da ich häufiger in diesem Forum Threads lese, weiss ich wie hier 
miteinander umgegangen wird (Was kein Problem für mich darstellt). Ich 
möchte allerdings keine Antworten à la "Geh wieder mit Bauklötzen 
spielen, das habe ich diesem Alter auch gemacht."

von Falk B. (falk)


Lesenswert?

@ Florian (Gast)

>realisiert: Ein Timer wird im CTC Modus betrieben. Dieser Verursacht
>alle 10ms einen Interrupt. Nun wird "nachgeschaut" ob der Pin, an
>welchem das DCF Modul hängt 0 oder 1 ist. Dementsprechend wird eine
>Variable erhöht. Wenn nun 500ms kein Signal mehr am Eingang ist, wird
>ausgewertet ob eine 0 oder 1 übertragen wurde.

Das ist schon mal ein guter Ansatz.

>uint8_t high = 0; // Variablen um die Signalläge zu zählen
>uint8_t low = 0;

Hier fehlt volatile, siehe Interrupt.

>Dieser Code funktioniert einwandfrei, ich weiss allerdings nicht wie ich
>nun weitermachen soll? Ich wäre sehr dankbar für eine Anregung.

Die gesamten 59 Bits des DCF77 sollten in ein Array gespeichert werden, 
das kann man direkt im Interrupt machen. Dazu reichen 60/8 ~ 8 Byte, 
siehe Bitmanipulation. Wenn alle 59 Bit gelesen wurden, gibt es ein 
FLag an das Hauptprogramm, dass die Daten vollständig empfangen wurden. 
Dort wird dann die Funktion zur Dekodierung und Fehlerprüfung 
aufgerufen.

>wahrscheinlich) nicht sehr "schönen" Code, da ich erst 12 Jahre alt bin.

WIRKLICH? Oder nur eine Schutzbehauptung? Glaube niemandem in einem 
Forum bezüglich seiner Person ;-)

>Falls es verboten ist, in diesem Alter hier zu posten (Ich konnte dazu

Nein.

von Holger L. (max5v)


Lesenswert?

Hallo Florian,
das hört sich doch schon mal sehr gut an!

Ich würde dir empfehlen als nächstes die Schaltsekunde, das 59. Bit zu 
detektieren. Diese zeichnet sich dadurch aus das kein Signal übertragen 
wird und zeigt den Minutenbeginn an, was wichtig für die Synchronisation 
ist.

Wenn du das geschafft hast, wäre der nächste Schritt die Kodierung der 
empfangenen Bits in verwertbare Daten umzuwandeln.

Hier sind noch zwei interessante Links zu dem Thema :
http://de.wikipedia.org/wiki/DCF77
http://www.mikrocontroller.net/articles/DCF77-Funkwecker_mit_AVR

Viel Erfolg bei deinem Vorhaben.

von Matthias P. (matthias_p65)


Lesenswert?

Florian schrieb:
> Geh wieder mit Bauklötzen
> spielen, das habe ich diesem Alter auch gemacht.

Habe ich zumindest nicht. Habe auch schon mit 12 gelötet und 
programmiert. ;)

Zu der Zeit waren die Mikrocontroller allerdings bei weitem noch nicht 
so ausgereift und erschwinglich.

von Harald W. (wilhelms)


Lesenswert?

Florian schrieb:

> Ich möchte eine DCF77 Uhr entwickeln.

Ach! Ist das etwas Neues was es vorher noch nicht gegeben hat?

> PS: Bitte verzeiht mir meine Rechtschreibfehler

Schon mal was von Rechtschreibkorrekturprogrammen gehört?

> Falls es verboten ist, in diesem Alter hier zu posten

Natürlich ist das nicht der Fall. Unabhängig vom Alter:
Wenn man ein neues Projekt, z.B. den Bau einer Uhr beginnt,
ist es zweckmäßig, zunächst ein lauffähiges Projekt aufzubauen.
Beispiele gibts massenhaft im INet und auch hier im Forum.
Dazu nimmt man am besten eins mit dokumentiertem Quellcode.
Wenn diese Uhr dann wirklich läuft, kann man sich den Quell-
code vornehmen und die Änderungen einfügen, die man für
sich selbst wünscht und diese in den µC laden. Dann kann man
bei Nichtfunktion durch Umprogrammieren immer wieder den
lauffähigen U(h)r-Zustand wiederherstellen.
Gruss
Harald

von Ingo L. (corrtexx)


Lesenswert?

Falk Brunner schrieb:
> Die gesamten 59 Bits des DCF77 sollten in ein Array gespeichert werden,
> das kann man direkt im Interrupt machen. Dazu reichen 60/8 ~ 8 Byte,
> siehe Bitmanipulation. Wenn alle 59 Bit gelesen wurden, gibt es ein
> FLag an das Hauptprogramm, dass die Daten vollständig empfangen wurden.
> Dort wird dann die Funktion zur Dekodierung und Fehlerprüfung
> aufgerufen.

Ich habe es so gelöst:
1
#define DCF_ZEITINFORMATIONSBITPOSITION      22
2
3
unsigned char DCF_Bitwertigkeiten [] = {
4
  1,
5
  2,
6
  4,
7
  8,
8
  10,
9
  20,
10
  40,
11
  1,
12
  2,
13
  4,
14
  8,
15
  10,
16
  20
17
};
18
19
20
if ( (DCF77.Pulslaenge > DCF_LOGIK_EINS_MIN) && (DCF77.Pulslaenge < DCF_LOGIK_EINS_MAX) ){
21
      if ( (DCF77.BitCounter < 29) && (DCF77.BitCounter >= DCF_ZEITINFORMATIONSBITPOSITION) ) {
22
        DCF77.Minute += DCF_Bitwertigkeiten[DCF77.BitCounter-DCF_ZEITINFORMATIONSBITPOSITION];
23
      }
24
      
25
      if ( (DCF77.BitCounter >= 30) && (DCF77.BitCounter < 36) ){
26
        DCF77.Stunde += DCF_Bitwertigkeiten[DCF77.BitCounter-DCF_ZEITINFORMATIONSBITPOSITION-1];
27
      }
28
    }
Spart man sich das riesige Array, obwohl meine erste Uhr auch mit dem 
großen array war.


Ingo

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Harald Wilhelms schrieb:
> Natürlich ist das nicht der Fall. Unabhängig vom Alter:
> Wenn man ein neues Projekt, z.B. den Bau einer Uhr beginnt,
> ist es zweckmäßig, zunächst ein lauffähiges Projekt aufzubauen.

Muss ich verneinen. Es gibt in jedem kleinen Entwickler auch so eine Art 
"Forschungsdrang", d.h. man möchte das Problem auf seine eigene Art und 
Weise lösen.

Dazu erkundigt man sich vielleicht auch nach den Methoden, wie es andere 
machen, um nicht vollkommen neben der Spur zu sein. Aber einfach ein 
fertiges Programm zu nehmen, ist der falsche Weg. Es verbaut einem 
nämlich die freie Sicht auf Alternativen. Ein fertiges Programm dann 
abzuwandeln macht auch keinen großen Spaß. Gerade im Alter des TOs will 
man selber etwas (er)schaffen - egal ob das tausende anderer vielleicht 
schon gemacht haben.

von Wolfgang (Gast)


Lesenswert?

Harald Wilhelms schrieb:
> Beispiele gibts massenhaft im INet und auch hier im Forum.
> Dazu nimmt man am besten eins mit dokumentiertem Quellcode.
> Wenn diese Uhr dann wirklich läuft, kann man sich den Quell-
> code vornehmen und die Änderungen einfügen

Der Lerneffekt ist bestimmt größer, wenn man sich das Programm 
(halbwegs) von unten aufbaut und nicht als erstes in einem Wald von zwar 
funktionierenden, aber unverstandenen Bäumen steht.

von Karl H. (kbuchegg)


Lesenswert?

Sieht doch nocht so schlecht aus.

Eine kleine Anmerkung
1
ISR(TIMER0_COMP_vect) {  //Wird alle 10ms ausgeführt
2
    if(PINA & (1 << 0)) {   // Wenn DCF Eingang = 1,
3
        high++; // erhöhe die high Variable
4
    }
5
6
    if(!(PINA & (1 << 0))) {    // Wenn DCF eingang = 0,
7
        low++; // erhöhe die low Variable
8
    }
9
10
11
}

du musst nicht für alles ein if nehmen. Beim if gibt es auch noch ein 
'else', welches für 'andernfalls' steht.
Wie kannst du das hier benutzen?
Nun, wenn der Eingang nicht auf 1 ist, wenn also die Bedingung in diesem 
if ...
1
    if(PINA & (1 << 0)) {   // Wenn DCF Eingang = 1,
2
        high++; // erhöhe die high Variable
3
    }
... nicht zutrifft, dann ist klar, dass der Eingangspin dann auf 0 sein 
muss. Es gibt keine andere Möglichkeit, denn der Pin kann nur entweder 1 
oder 0 sein. Ist er nicht 1, dann muss er 0 sein. Und umgekehrt: wenn er 
1 ist, dann kann er nicht 0 sein.

D.h. du kannst das so schreiben
1
ISR(TIMER0_COMP_vect) {  //Wird alle 10ms ausgeführt
2
    if(PINA & (1 << 0)) {   // Wenn DCF Eingang = 1,
3
        high++; // erhöhe die high Variable
4
    }
5
    else {
6
        low++; // erhöhe die low Variable
7
    }
8
}
und damit dem µC eine komplette Abfrage samt Auswertung ersparen.


-------- neues Thema

>  Wenn nun 500ms kein Signal mehr am Eingang ist, wird ausgewertet
> ob eine 0 oder 1 übertragen wurde.

Fast.
Sieh dir mal die Spec für die 59. Sekunde in jeder Übertragungsrunde an. 
In dieser Sekunde wird absichtlich überhaupt kein Puls übertragen, damit 
man erkennen kann, dass die nächste Signalflanke der Beginn der nächsten 
Minute ist und man damit dann auch die Zuordnung herstellen kann, mit 
welchem Bit (Bitnamen) man es eigentlich zu tun hat, wenn man erst mal 
weiß, das es 0 oder 1 ist. Denn je nachdem, in welcher Sekunde es 
übertragen wurde, hat ja diese 0 oder 1 eine andere Bedeutung.

D.h. dein nächster SChritt ist es, diesen einen fehlenden Sekundenpuls 
zu finden. In deiner Auswertung bedeutet das, dass du pro Minute 
einmalig einen Low-Puls erkennen wirst, der deutlich länger als 1000 
Millisekunden auf Low sein wird.
Den musst du finden, denn dort startet dann die Zählung der Bits über 
eine Minute.

von Harald W. (wilhelms)


Lesenswert?

Frank M. schrieb:

> Muss ich verneinen. Es gibt in jedem kleinen Entwickler auch so eine Art
> "Forschungsdrang", d.h. man möchte das Problem auf seine eigene Art und
> Weise lösen.

Das mag für private Zwecke noch sinnvoll sein. Entwickelt man beruflich,
wäre man mit einer solchen Strategie ziemlich unproduktiv.
Aber wenn man genug Zeit hat, kann man natürlich gern ausprobieren,
ob ein achteckiges Rad nicht doch besser als ein rundes funktioniert.
:-)
Gruss
Harald

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Harald Wilhelms schrieb:
> Das mag für private Zwecke noch sinnvoll sein. Entwickelt man beruflich,
> wäre man mit einer solchen Strategie ziemlich unproduktiv.

Ja, natürlich. Privat kann man Räder erfinden, so oft man will. Im Job 
ginge es nur, wenn es das allererste Rad überhaupt wäre ;-)

von Wolfgang (Gast)


Lesenswert?

Harald Wilhelms schrieb:
> Entwickelt man beruflich, wäre man mit einer solchen Strategie ziemlich
> unproduktiv.

Der Haken ist nur, dass man mit dem ewigen Abkupfern innovative Ansätze 
untergräbt. Die großen Firmen haben das schon lange raus: Die lassen die 
kleinen Start-Ups mit neuen kreativen Ideen erstmal machen und wenn die 
Sache läuft, wird die kleine Firma aufgekauft. Die BWLer haben diese 
Vorgehensweise schon seit einiger Zeit erkannt, um die Kosten für 
Forschung zu reduzieren, weil man dadurch Fehlversuche nicht aus eigener 
Tasche bezahlen muss.

Gerade bei DCF77 gibt es, abweichend vom Main Stream, hübsche Ansätze, 
um auch bei nur teilweise fehlerfrei empfangenem Datentelegramm sicher 
zu stellen, dass am Ausgang nur gültige Zeitunformationen rauskommen.

von Florian (Gast)


Lesenswert?

Hi

Danke für die zahlreichen Antworten und das Verständnis!

@Holger L.
Ich werde als nächstes versuchen die Erkennung der neuen Minute zu 
realisieren.

@Falk Brunner
Dies werde ich mache wenn ich die Erkennung der neuen Minute geschafft 
habe.
Nein, es ist keine Schutzbehauptung. Ich erwarte auch keine 
Sonderbehandlung wegen meines Alters :-)

@Harald Wilhelms
Wenn ich mir einen Code aus dem Internet kopiere habe ich
erstens: keinen Lerneffekt und zweitens: wieso sollte ich dann eine 
eigene Uhr bauen? Ich kann dann auch eine günstige kaufen.

@Karl Heinz
Danke für den Hinweis :-)
Ich werde beim weiterprogrammieren beachten.



Ich werde nun versuchen die Erkennung der Schaltsekunde zu realisieren.
Habe mir schon ein paar Gedanken dazu gemacht, allerdings scheint noch 
nicht der richtige darunter gewesen zu sein ;-)

Freundliche Grüsse
Florian

von Karl H. (kbuchegg)


Lesenswert?

Florian schrieb:

> Ich werde nun versuchen die Erkennung der Schaltsekunde zu realisieren.
> Habe mir schon ein paar Gedanken dazu gemacht, allerdings scheint noch
> nicht der richtige darunter gewesen zu sein ;-)

Willst du noch ein bischen grübeln?

Einen klitzekleinen Hinweis kann ich mir trotzdem nicht verkneifen.
In deinem System ist das nur eine kleine zusätzliche Abfrage, die hier
1
....
2
    while(1) {
3
4
        if(low > 50) { // Wenn seit 500ms der DCF Eingang = 0
5
            low = 0; // Setzte low wieder zurück
6
7
....
in diesem Bereich angesiedelt ist.

Denk drann: die Low-zeit ist dann deutlich länger als 50
Und überdenk auch mal, was man mit dem else in eine mif noch so alles 
machen kann
1
   if( wert > 200 )
2
      // wert war auf jeden Fall größer als 200
3
      ....
4
5
   else if( wert > 100 )
6
      // wert ist zwar größer als 100, aber auch auf jeden Fall kleiner
7
      // bzw,. gleich 200. Denn wenn Wert größer als 200 gewesen wäre
8
      // dann würden wir nicht hier landen, sondern der if davor
9
      // hätte schon 'zugeschlagen'
10
      ....
11
12
   else
13
      // Wert muss hier kleiner oder gleich 100 sein.
14
      ....

: Bearbeitet durch User
von Florian (Gast)


Lesenswert?

Hi

Ich habe es jetzt lange versucht. Leider habe ich es immer noch nicht 
hinbekommen. Deshalb wäre ich froh, wenn noch jemand einen Tipp hätte.

Freundliche Grüsse
Florian

von Tom V. (vzczc)


Lesenswert?

Hallo Florian,

schau Dir nochmal intensiv den Hinweis von Karl Heinz an. Um den 
Minutenanfang zu erkennen, musst Du wissen, wann Deine "Low"-Zeit 
deutlich länger als 900ms ist. Das kannst Du mit Deinem derzeitigen 
Ansatz aber nicht, da Du...

Florian schrieb:
> if(low > 50) { // Wenn seit 500ms der DCF Eingang = 0
>             low = 0; // Setzte low wieder zurück

...nach 500ms Deinen "low"-Zähler bereits wieder zurücksetzt. Du musst 
Dir überlegen, ob Du nicht eine andere Stelle im Programmablauf findest, 
an der Du Deinen Zähler zurücksetzt...

Reicht Dir der Tipp?

Gruß
Tom

von ...hüstel (Gast)


Lesenswert?

Ingo L. schrieb:
> Spart man sich das riesige Array

mit der fast nicht zu beherrschenden größe von 8 Bytes ;-)

von Florian (Gast)


Lesenswert?

Hi

Danke für die Antwort!
Ich habe nun versucht zu erkennen ob ein neuer Minutenanfang ist oder 
nicht. Doch leider habe ich, wie du beschrieben hast das Problem, wenn 
ich einfach nur die low Variable auswerte, nicht weiss ob nicht eine 
high Sequenz im low Signal war. Deshalb muss ich wohl einen zweiten 
Parameter benutzen. Ich habe mir das so überlegt: Ich schaue ob die low 
Variable grösser 1,5 Sekunden ist und ob die high Variable gleich 0 ist. 
Doch leider funktioniert es nicht. Ist meine Überlegung falsch?

Freundliche Grüsse
Florian

von ...hüstel (Gast)


Lesenswert?

Harald Wilhelms schrieb:
> Entwickelt man beruflich,

Er ist 12 Jahre alt, will selbst was auf die Beine stellen und eigene 
Lösungen finden. Da ist doch eine DCF-Uhr nicht das schlechteste. In dem 
Falle sollte man keine professionellen Ansprüche voraussetzen, denn wie 
die anderen Poster sagten, damit versperrt man sich nur selbst den Weg, 
eigene Lösungen zu finden. Und ich meine, die "copy & paste" - 
Generation ist schon viel zu weit fortgeschritten. Selbst Gedanken 
machen finde ich für einen jungen Menschen viel besser.

von Florian (Gast)


Lesenswert?

Ich habe vergessen meinen Code anzuhängen:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include "lcd-routines.h"
4
5
uint8_t high = 0; // Variablen um die Signalläge zu zählen
6
uint8_t low = 0;
7
8
ISR(TIMER0_COMP_vect) {  //Wird alle 10ms ausgeführt
9
    if(PINA & (1 << 0)) {   // Wenn DCF Eingang = 1,
10
        high++; // erhöhe die high Variable
11
    }
12
13
    if(!(PINA & (1 << 0))) {    // Wenn DCF eingang = 0,
14
        low++; // erhöhe die low Variable
15
    }
16
17
18
}
19
20
int main(void)
21
{
22
23
    TCCR0 |= (1 << WGM01) | (1 << CS02); // CTC Modus, Prescaler 256
24
    TIMSK |= (1 << OCIE0); // Interrupt erlauben
25
    OCR0 = 35; // Wert für Overflow
26
    sei(); // Interrupts global aktivieren
27
28
    lcd_init(); // LCD initialisieren
29
30
31
    while(1) {
32
33
        if(low > 50) { // Wenn seit 500ms der DCF Eingang = 0
34
35
            if(low > 150 && high == 0) {
36
                lcd_setcursor(15, 1);
37
                lcd_string("A");
38
            }
39
40
            if(high > 5 && high < 15) { // Wenn das high Signal zwischen 50ms und 150ms lang ist, (Logische 0)
41
                lcd_setcursor(0, 1); // LCD Cursor setzen
42
                lcd_string("0"); // 0 auf dem LCD ausgeben
43
44
                lcd_setcursor(5, 1); // LCD Cursor setzen
45
                char buffer0[20]; // Buffer für utoa
46
                utoa (high, buffer0, 10); // Umwandeln der int Variable in eine Char Variable
47
                lcd_string(buffer0); // Char Variable auf dem LCD ausgeben
48
                high = 0; // high Variable zurücksetzen
49
                low = 0;
50
51
            }
52
53
            else if(high > 15 && high < 25) { // Wenn das high Signal zwischen 150ms und 250ms lang ist, (Logische 1)
54
                lcd_setcursor(0, 1); // LCD Cursor setzen
55
                lcd_string("1"); // 1 auf dem LCD ausgeben
56
57
                lcd_setcursor(5, 1); // LCD Cursor setzen
58
                char buffer1[20]; // Buffer für utoa
59
                utoa (high, buffer1, 10); // Umwandeln der int Variable in eine Char Variable
60
                lcd_string(buffer1); // Char Variable auf dem LCD ausgeben
61
                high = 0; // high Variable zurücksetzen
62
                low = 0;
63
            }
64
        }
65
    }
66
67
    return 0;
68
}

Ich setze die low Variable jetzt nach dem Auswerten zurück und wenn eine 
neue Minute beginnt sollte ein A auf dem Display erscheinen. Dies 
funktioniert leider nicht. Zum einen wird kein A angezeigt und die 
Ausgabe der low Werte spielt verrückt. Es werden Werte von 10 - 90 
angezeigt. (umgerechnet = 100 - 900ms)

Freundliche Grüsse
Florian

von Alex D. (allu)


Lesenswert?

Hallo Florian,

das DCF-Signal mit einem CTC-Timer abzutasten ist meiner Meinung nach 
ein guter Ansatz. Einen Impulsdauerzähler hast Du ja auch schon 
eingebaut.

Die Zeitinformation ist aber nicht nur in den 100msec oder 200msec 
Pausen enthalten, sondern auch in den restlichen Trägerzeiten.
Wenn Du diese Zeiten ausmisst wird es einfacher.

Gehen wir davon aus, dass die Pause eine 0 und für den Träger eine 1 
steht.

Zwei Informationen werden benötigt:

1. Erkennung der Flanke von 1 nach 0, danach wird ausgewertet.
2. Ein Zähler der die Zeitdauer der 1 bestimmt.

Jetzt genügen zwei Abfragen, um zu bestimmen, ob eine 0 oder eine 1 
gesendet wurde oder die 59. Sekunde ausgeblieben ist.

Eine 0 ist dann 900mse (Zählerstend ca. 90 und eine 1 800msec 
(Zählerstand ca. 80) lang. Die erste Schwelle wäre dann bei ca. 85. 
Alles was kleiner als 85 ist, ist dann eine 1.

Wenn die 59. Sekunde ausbleibt, läuft der Zähler dann auf 1,8 bis 1,9 
Sekunden hoch (ca. 180 bis 190). Hier komm die 2. Abfrage ins Spiel, 
alles was größer als 150 ist, zeigt mit der nächsten negativen Flanke 
den Beginn einer neuen Minute an.

Gruß  Alex

(Das Prinzip funktioniert, bei mir in Bascom mit einer 20msec-Abtastung)

von R. F. (rfr)


Lesenswert?

Die Detektion der Sekundenanfangsflanke und das Messen der Dauer kann 
man doch einfach zusammenfassen:

---detektiere den Sekundenanfang
---warte 75 ms, if Signal == H then ()
----------------if signal == low-->error

---warte  weitere 50 ms
----------------if signal == H then  --> entscheid folgt
----------------if signal == L then  --> anscheinend NULLbit

---warte weitere 25 ms
----------------if signal == H then-->error
----------------if signal == L then-->anscheinen EINSbit

Du wirst nicht davon ausgehen, dass dein Empfang vollkommen frei von 
Störungen ist.Du wirst nicht davon ausgehen, dass dein Empfang 
vollkommen frei von Störungen ist.Du wirst nicht davon ausgehen, dass 
dein Empfang vollkommen frei von Störungen ist.Du wirst nicht davon 
ausgehen, dass dein Empfang vollkommen frei von Störungen ist.Du wirst 
nicht davon ausgehen, dass dein Empfang vollkommen frei von Störungen 
ist.
Grüsse

Robert

: Bearbeitet durch User
von LostInMusic (Gast)


Lesenswert?

Zwei Zähler (high, low) machen die Sache nur unnötig kompliziert. Ein 
Zähler, der bei jeder steigenden Flanke zurückgesetzt wird, genügt. 
Ausgewertet werden muss der Zählerstand bei jeder steigenden und 
fallenden Flanke (bei der steigenden natürlich vor dem Zurücksetzen). 
Das liefert alle benötigten Informationen (kurzer Puls, langer Puls, 
Minuten-Syncmarke).

von Florian (Gast)


Lesenswert?

Hallo

Danke für die zahlreichen Antworten!
@Alex D.
Leider sehe ich den Vorteil/Nutzen zur meiner Möglichkeit nicht?!

@R. Freitag
Inwiefern ist deine Möglichkeit besser gegen Störungen geschützt als 
meine?

@all
Leider verstehe ich immernoch nicht wieso ich solch hohe Werte bei 
meiner Ausgabe erhalte. Es wird zwar eine Null oder eine Eins angezeigt, 
aber der Wert, welcher die Länge angibt stimmt nicht. Kann es sein das 
der Fehler bei meiner utoa Funktion liegt? Ich wäre wirklich sehr 
dankbar wenn mir jemand diese Frage beantworten könnte.

Freundliche Grüsse
Florian

von Joachim B. (jar)


Lesenswert?

Florian schrieb:
> @all
> Leider verstehe ich immernoch nicht wieso ich solch hohe Werte bei
> meiner Ausgabe erhalte. Es wird zwar eine Null oder eine Eins angezeigt,
> aber der Wert, welcher die Länge angibt stimmt nicht.

ich habe gerade mal wieder ein neueres Pollin Modul am testen.

Auf dem Oszi sehe ich hier das das Teil erhebliche 
Empfanfschwierigkeiten hat.
Das Datenblatt sagt auch bis zu 20 Minuten kann die Syncronisierung 
dauern, das nervt. Frühere Module liefen sofort los. Wichtig scheint 
auch die Filterung der Versorgungsspannung!
Auch mit Filter habe ich hier Probleme, 100 Ohm auf 4,7µF

Ich muss mir das Montag auf dem Oszi noch mal ansehen.

Mir ist aufgefallen das weitere Entfernung vom Modul zur MC Schaltung 
hilft, vielleicht ein Grund warum die Grundig Sono Clock ihren DCF 
Empfänger extern mit 1m Kabel liefert.

von R. F. (rfr)


Lesenswert?

Du schriebst:

@R. Freitag
Inwiefern ist deine Möglichkeit besser gegen Störungen geschützt als
meine?

Du musst mit Störungen aller Art rechnen. Die Störung weiss aber nicht, 
was wir an Signal brauchen. Die für uns interessanten Bereiche sind die 
Bereiche, an denen sich etwas ändern kann. Hier muss oft abgefragt 
werden, um durch Mittelwertbildung eine Aussage zu erhalten. Die 
Bereiche, an denen nichts passiert, müssen wir noch nicht einmal 
abfragen.

Das bedeutet, dass wir im Bereich um die Flankenerkennung, um das Ende 
einer Null umd um das Ende einer Eins oft abfragen müssen.

Dieses ist bei den anderen Lösungen noch nicht gemacht worden.

Überlege doch mal, was zu erwarten ist, wenn deine Uhr auf einem (altem) 
TV-Röhrengerät steht und dort die Zeit anzeigen soll. Welche Probleme 
werden auftrweten?

Gruss

Robert

von Florian (Gast)


Lesenswert?

Hi

@R. Freitag und Joachim B.
Zum Entwickeln verwende ich diesen Simulator:
Beitrag "DCF77 Simulator Generator Encoder Atmega8 Assembler"
Ich habe einen Simulator gewählt, um die von euch genannten Störungen 
bei der Entwicklung ausschliessen zu können.

@all
Ich konnte nun beobachten das wenn ein zu hoher Wert bei der logischen 0 
angezeigt wird, entweder eine Zahl zwischen 60 und 65 oder zwischen 90 
und 95. Hat vielleicht jemand eine Idee weshalb das so ist?

Freundliche Grüsse
Florian

von Joachim B. (jar)


Lesenswert?

Florian schrieb:
> @all
> Ich konnte nun beobachten das wenn ein zu hoher Wert bei der logischen 0
> angezeigt wird, entweder eine Zahl zwischen 60 und 65 oder zwischen 90
> und 95. Hat vielleicht jemand eine Idee weshalb das so ist?

entweder falsche Vorteiler oder
verschliffene Impulse, aber damit kann man leben

wer keinen Ozsi hat um das zu überprüfen....

also stze dein low 100ms zwischen 50 und 70 dein high auf 80-150

und beobachte ob DCF richtig erkannt wurde, Uhrzeit, Datum, Paraty

von Florian (Gast)


Lesenswert?

Hi

@all
Ich habe gemerkt das die falschen Werte für die Dauer von der 
Zurücksetzung der low Variable kommen.
Dieser Code funktioniert:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include "lcd-routines.h"
4
5
uint8_t high = 0; // Variablen um die Signalläge zu zählen
6
uint8_t low = 0;
7
8
ISR(TIMER0_COMP_vect) {  //Wird alle 10ms ausgeführt
9
    if(PINA & (1 << 0)) {   // Wenn DCF Eingang = 1,
10
        high++; // erhöhe die high Variable
11
    }
12
13
    if(!(PINA & (1 << 0))) {    // Wenn DCF eingang = 0,
14
        low++; // erhöhe die low Variable
15
    }
16
17
18
}
19
20
int main(void)
21
{
22
23
    TCCR0 |= (1 << WGM01) | (1 << CS02); // CTC Modus, Prescaler 256
24
    TIMSK |= (1 << OCIE0); // Interrupt erlauben
25
    OCR0 = 35; // Wert für Overflow
26
    sei(); // Interrupts global aktivieren
27
28
    lcd_init(); // LCD initialisieren
29
30
31
    while(1) {
32
33
        if(low > 50) { // Wenn seit 500ms der DCF Eingang = 0
34
            low = 0; // Setzte low wieder zurück
35
36
            if(high > 5 && high < 15) { // Wenn das high Signal zwischen 50ms und 150ms lang ist, (Logische 0)
37
                lcd_setcursor(0, 1); // LCD Cursor setzen
38
                lcd_string("0"); // 0 auf dem LCD ausgeben
39
40
                lcd_setcursor(5, 1); // LCD Cursor setzen
41
                char buffer0[20]; // Buffer für utoa
42
                utoa (high, buffer0, 10); // Umwandeln der int Variable in eine Char Variable
43
                lcd_string(buffer0); // Char Variable auf dem LCD ausgeben
44
                high = 0; // high Variable zurücksetzen
45
46
            }
47
48
            else if(high > 15 && high < 25) { // Wenn das high Signal zwischen 150ms und 250ms lang ist, (Logische 1)
49
                lcd_setcursor(0, 1); // LCD Cursor setzen
50
                lcd_string("1"); // 1 auf dem LCD ausgeben
51
52
                lcd_setcursor(5, 1); // LCD Cursor setzen
53
                char buffer1[20]; // Buffer für utoa
54
                utoa (high, buffer1, 10); // Umwandeln der int Variable in eine Char Variable
55
                lcd_string(buffer1); // Char Variable auf dem LCD ausgeben
56
                high = 0; // high Variable zurücksetzen
57
            }
58
        }
59
    }
60
61
    return 0;
62
}

dieser Code ruft das Problem hervor:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include "lcd-routines.h"
4
5
uint8_t high = 0; // Variablen um die Signalläge zu zählen
6
uint8_t low = 0;
7
8
ISR(TIMER0_COMP_vect) {  //Wird alle 10ms ausgeführt
9
    if(PINA & (1 << 0)) {   // Wenn DCF Eingang = 1,
10
        high++; // erhöhe die high Variable
11
    }
12
13
    if(!(PINA & (1 << 0))) {    // Wenn DCF eingang = 0,
14
        low++; // erhöhe die low Variable
15
    }
16
17
18
}
19
20
int main(void)
21
{
22
23
    TCCR0 |= (1 << WGM01) | (1 << CS02); // CTC Modus, Prescaler 256
24
    TIMSK |= (1 << OCIE0); // Interrupt erlauben
25
    OCR0 = 35; // Wert für Overflow
26
    sei(); // Interrupts global aktivieren
27
28
    lcd_init(); // LCD initialisieren
29
30
31
    while(1) {
32
33
        if(low > 50) { // Wenn seit 500ms der DCF Eingang = 0
34
35
            if(high > 5 && high < 15) { // Wenn das high Signal zwischen 50ms und 150ms lang ist, (Logische 0)
36
                lcd_setcursor(0, 1); // LCD Cursor setzen
37
                lcd_string("0"); // 0 auf dem LCD ausgeben
38
39
                lcd_setcursor(5, 1); // LCD Cursor setzen
40
                char buffer0[20]; // Buffer für utoa
41
                utoa (high, buffer0, 10); // Umwandeln der int Variable in eine Char Variable
42
                lcd_string(buffer0); // Char Variable auf dem LCD ausgeben
43
                high = 0; // high Variable zurücksetzen
44
                low = 0;
45
46
            }
47
48
            else if(high > 15 && high < 25) { // Wenn das high Signal zwischen 150ms und 250ms lang ist, (Logische 1)
49
                lcd_setcursor(0, 1); // LCD Cursor setzen
50
                lcd_string("1"); // 1 auf dem LCD ausgeben
51
52
                lcd_setcursor(5, 1); // LCD Cursor setzen
53
                char buffer1[20]; // Buffer für utoa
54
                utoa (high, buffer1, 10); // Umwandeln der int Variable in eine Char Variable
55
                lcd_string(buffer1); // Char Variable auf dem LCD ausgeben
56
                high = 0; // high Variable zurücksetzen
57
                low = 0;
58
            }
59
        }
60
    }
61
62
    return 0;
63
}

von Florian (Gast)


Lesenswert?

Hi

Leider habe ich ausversehen auf absenden und nicht auf Vorschau 
geklickt.
Was ich noch schreiben wollte:
Wahrscheinlich hängt es also mit der Rücksetzung auf Null bei der low 
Variable zusammen. Ich kann allerdings nicht verstehen wieso dieses 
Verhalten auftritt. Wie könnte ich das lösen? Die erste Möglichkeit kann 
ich nicht verwenden, weil ich sonst nicht auswerten kann ob eine neue 
Minute begonnen hat. Das hat schon jemand weiter oben geschrieben, ich 
müsse das ändern.

von LostInMusic (Gast)


Lesenswert?

Soweit ich das sehe hast Du derzeit überhaupt keine Flankenerkennung in 
Deinem Programm. Ob Du Deine Aufgabe auf diese Weise lösen kannst, wage 
ich zu bezweifeln.

von eProfi (Gast)


Lesenswert?

versuche es mal damit:
1
ISR(TIMER0_COMP_vect) {  //Wird alle 10ms ausgeführt
2
    if(PINA & (1 << 0)) {   // Wenn DCF Eingang = 1,
3
        hi++;
4
        loMerk=low;low=0;
5
    }
6
    else{ // Wenn DCF eingang = 0,
7
        lo++;
8
        hiMerk=high;hi=0;
9
    }
10
}
Im Hauptprogramm loMerk und hiMerk auswerten.

von eProfi (Gast)


Lesenswert?

Korrektur:
        loMerk=low;low=0;
-->     loMerk=lo;lo=0;

        hiMerk=high;hi=0;
-->     hiMerk=hi;hi=0;

von Alex D. (allu)


Lesenswert?

Florian schrieb:
> Leider sehe ich den Vorteil/Nutzen zur meiner Möglichkeit nicht?!

Es ist eine von vielen Möglichkeitn an die Information des DCF zu 
kommen. Die von mir beschriebene Lösung kommt mit nur einem Zähler und 
nur einer Flankenerkennung aus.

Ich empfehle Dir aber, die Lösung umzusetzen, von der Du überzeugt bist.

Gruß   Alex

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.