PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : DCF77 Funksignal auslesen



cesupa
15.09.2007, 16:30
Hallo,

ich hab mir jetzt mal den DCF-Empfängerbusatz von Conrad zugelegt und versuche nun, mit dessen Hilfe zunächst einmal nur die einzelnen Bits des DCF-Signals einzulesen und über die serielle Schnittstelle auszugeben. Ich glaube ich mache beim einlesen nur ein paar Fehler oder aber ich habe den Wikipedia Artikel zum DCF-Signal etwas falsch interpretiert.
Hier erstmal mein Code:




#include <avr/io.h>

#define BAUD 9600UL

#include "../classes/rs232/UART/UART.h"
#include "../classes/time/time.h"

int main(void)
{
uint8_t i=0;
uint8_t c=0;
char dcf[60];
dcf[0]='\0';

DDRC&=~(1<<PC0); //EINGANG PC0
PORTC|=(1<<PC0); //PULLUP aktivieren

uart_init();

while(1)
{
if(i<=59)
{

while(!(PINC & (1<<PC0))) //wenn Signal (LOW) kommt
{
wait_msec(1);
c++;
}

if(c<=100) //100msec == 0
{
dcf[i]='0';
}

if(c<=200 && c>100) //200msec == 1
{
dcf[i]='1';
}
c=0;

i++;
}
else
{
dcf[i]='\0';
send(dcf);
i=0;
}
}

return 0;
}


Wikipedia habe ich so verstanden, dass wenn 100millisekunden ein LOW-Signal kommt, eine binärische 0 gesendet wird und ab 200millisekunden eine 1. Mein Problem ist nur, dass wenn ich das ganze laufen lasse fast immer die erste Stelle eine 1 ist und alle anderen Stellen eine 0. Was genau mache ich falsch? Kann mir bitte jemand sagen, wie man sowas am Besten machen könnte.

Gruß
cesupa

SprinterSB
15.09.2007, 17:23
Versuch mal folgendes:

Einfach 10ms warten und dann einen Wert hochzählen abhängig vom Portzustand.

Ist der Wert ~10 ist es eine 0, bei ~20 ist es eine 1. Um zu sehen, ob überhaupt ein vernünfitiges Signal ankommt, kannst du in der Warteschleife (oder alle 10ms) den Port-Zustand an einen anderen Port ausgeben, an dem zB eine LED hängt.

Einen etwas komplizierteren DCF-Decoder findest du auf meiner HP wenn das was hilft.

cesupa
15.09.2007, 18:34
okay, ich hab jetzt mal ne LED in die while-Schleife gehangen, die blinkt auch wie es eigentlich sein sollte, aber dennoch erhalte ich die gleichen Werte. Komischerweise wird das Wertepaket viel zu schnell hintereinander ausgegeben.

Gruß
cesupa

SprinterSB
16.09.2007, 08:31
lso was ich überhaupt net verstehe ist


while(!(PINC & (1<<PC0))) //wenn Signal (LOW) kommt

cesupa
16.09.2007, 09:26
Naja, hab ich eigentlich schon erwähnt, dass ich eigentlich noch ein Anfänger bin? Ich hab das halt mit Wiki so verstanden, dass mein DCF-Empfänger von Conrad 100ms kein Signal schickt, wenn eine binäre 0 empfangen wurde und bei 200ms eine 1. Da dachte ich mir halt, dass das Ding nur dann ein Signal schickt, also HIGH, wenn 100ms oder eben 200ms um sind. Ich merke aber gerade, dass das wohl irgendwie nicht hinhauen kann wenn man das in einer Schleife überprüft, da ja in diesen 10ms Wartezeit bereits ein HIGH Signal kommen kann, welches nicht beachtet wir. Ich hab mir die Variable c auch mal ausgeben lassen und festgestellt, dass ein Großteil der c-Werte ständig eine 0 ist. Also anscheinend fehlt mir noch ein wenig grundlegendes Verständnis. Kannst du mir sagen, wie das mit DCF läuft und wie man die Werte am besten registriert?

Gruß
cesupa

cesupa
16.09.2007, 09:33
Ich hab den Code auch nochmal ein wenig geändert, außerdem hab ich noch vergessen zu sagen, dass ich den invertierten DCF-Ausgang benutze.




#include <avr/io.h>

#define BAUD 9600UL

#include "../classes/rs232/UART/UART.h"
#include "../classes/time/time.h"

int main(void)
{
uint8_t i=0;
uint8_t c=0;
char dcf[60];
char buf[256];
buf[0]='\0';
dcf[0]='\0';

DDRC&=~(1<<PC0); //EINGANG PC0
DDRD|=(1<<PD5) | (1<<PD6);
PORTC|=(1<<PC0); //PULLUP aktivieren

uart_init();

i=0;
c=0;
while(1)
{
if(i<=59)
{
while((PINC & (1<<PC0)))
{
PORTD|=(1<<PD5);
PORTD&=~(1<<PD6);
_delay_ms(10);
c++;
}

PORTD&=~(1<<PD5);
PORTD|=(1<<PD6);


if(c>=8 && c<=23)
{
if(c<=10) //100msec == 0
{
dcf[i]='0';
}

if(c>10) //200msec == 1
{
dcf[i]='1';
}


c=0;

i++;
}
}
else
{
dcf[i]='\0';
send(dcf);
send("\r\n");
i=0;
}
}

return 0;
}


Mit dem Code erhalte ich nach ca. 3-4 Minuten recht anschauliche Werte. Zumindest, wurden einige Teile, wie zum Beispiel die Minuten korrekt interpretiert.

Gruß
cesupa

cesupa
19.09.2007, 06:41
Hallo SprinterSB,

ich hab mir jetzt mal deinen Code ein wenig angeschaut und hab gesehen, dass du das DCF-Signal alle 10ms einliest. Aber mir ist noch nicht ganz so klar was du mit dem Signal danach machst? Ich hab das Signal jetzt auch mal aus einer ISR heraus alle 10ms eingelesen und mir über RS232 ausgeben lassen. Dabei kam folgendes raus:




10100101101011010111001011010110100100010110101101 0010001111

01011010001101011010100101011010111101011010110001 0110101111

01011010001101011010100101011010111101011010110001 0110101111

11100101101011010101001010110101100010110101011000 1111010110

10001001011010110100011010101101001010110101101001 0110101101



Wie man sieht, sind die doch eigentlich recht unterschiedlich. Eine Uhrzeit konnte ich aus den Bits auch noch nicht (vollständig) auslesen. Muss ich diese Bitfolgen noch irgendwie bearbeiten, um die korrekten Bits zu erhalten???

Gruß
cesupa

Dirk
19.09.2007, 16:51
Hallo cesupa,

willkommen bei den DCF-Decodierern!

Deine Ausgabe der Bits, die du empfangen hast, ist eher ein Zufallsprodukt, weil du nicht den Anfang eines Minutentelegramms hast.

Wichtig ist es, das ENDE des Bitstreams zu erkennen. Das geht nicht über das Detektieren der Impulse (100/200ms), sondern der Pausen (900/800ms). Ich würde also das Prog umschreiben, so dass es die Pausen erkennt.

Das Ende der Minute (und des DCF-Telegramms) ist dann erreicht, wenn du eine Pause von 1800 oder 1900ms erkannt hast. Danach stellst du deinen Bitzähler auf 0 (= Zeilenanfang) und das neue Telegramm beginnt. Das ist auch der Zeitpunkt, wo dein fertiger Decoder später die Uhr stellt.

Wenn du so weit bist, melde dich doch wieder mit einigen empfangenen Bitstreams. Jeder muss automatisch (ohne eine begrenzende Schleife!) genau 59 Bit Länge haben.

Gruß Dirk

cesupa
19.09.2007, 18:10
Okay, danke, sowas kann man z.B. nicht bei Wikipedia oder sonst wo lesen :) Also Danke Dirk, ichwerds gleich mal versuchen.

Gruß
cesupa

cesupa
19.09.2007, 18:46
So, hab mir jetzt mal schnell eine Routine überlegt mit der man das ganze lösen könnte, nur leider funktioniert die nicht so wie sie sollte, dass heißt ich bekomme nur Nullen ausgegeben. Hier die Routine, die aller 0,01ms bearbeitet wird:




t++; //Timer++ (0,01ms)

if(!(PINC & (1<<PC0))) //Kein Impuls==Pause
{

if(t<=80000) //Wenn kleiner gleich 800ms
{
dcf='0';
}

if(t>=90000) //Wenn größer gleich 900ms
{
dcf='1';
}

}
else //Impuls==timer zurücksetzen
{
if(dcf=='0' || dcf=='1') //Wenn Zeichen empfangen
r='2'; //Signal geben zum Senden über RS232
t=0; //Timer zurücksetzen
}


Wie sieht eine Pause eigentlich aus bei einem DCF-Empfänger. Hier im Code hab ich eine Pause jetzt so interpretiert, dass eben gar nichts gesendet wird, also 0V am Eingangspin anliegen. Bei einem Impuls jedoch, also größer als 0V wird das empfangene Zeichen an den PC übergeben und der Timer resetted. Mach ich da schon irgendwas falsch oder wieso erhalte ich nur Nullen?

Gruß
cesupa

Dirk
20.09.2007, 06:43
Moin cesupa,

also eine Pause ist die Phase mit max. Sendeleistung des DCF77-Senders und der Impuls wird mit 25% Leistung gesendet.
Aber das ist egal. Deine Negierung bei der Abfrage wars schon.

Deine Änderung ist so schon gut.

Was du noch machen must:

Du must 4 Zustände erkennen: Pausen 800/900/1800/1900ms.
Du brauchst also 4 Abfragen z.B. der Art:
Pause > 780 and Pause < 820 -> DiesisteinEINSBIT
Pause > 880 and Pause < 920 -> DiesisteinNULLBIT

... und so auch für die beiden anderen Bits (EINSBITENDE/NULLBITENDE).

Wenn du ein ENDE-Bit erreicht hast stellst du die RS232-Ausgabe auf Zeilenanfang um. Mit den Wertebereichen must du experimentieren: Lass sie dir später ausgeben, um die Mitte festzulegen und einen sicheren +-Bereich.

Gruß Dirk

cesupa
20.09.2007, 17:06
Also so langsam krieg ich hier die Krise. Ich hab das ganze jetzt so umgesetzt wie du gesagt hast, aber jetzt bekomme ich gar keine Zeichen mehr ausgegeben. Hier der Code ausm Timer:




if(!(PINC & (1<<PC0))) //Kein Impuls==Pause
{

if(t>78000 && t<82000)
{
if(dcf!='0')
dcf='1';
}

if(t>88000 && t<92000)
{
dcf='0';
}

if(t>170000 && t<180000)
{
dcf_buf[t2]='1'; //ENDEBIT
dcf_buf[t2+1]='\0';
t2=0;
r='2'; //empfangene Zeichenkette über rs232 senden
}

if(t>180000 && t<190000)
{
dcf_buf[t2]='0'; //ENDEBIT
dcf_buf[t2+1]='\0';
t2=0;
r='2'; //empfangene Zeichenkette über rs232 senden
}

t++; //Timer inkrementieren
}
else //Impuls==timer zurücksetzen
{
if(dcf=='0' || dcf=='1') //Wenn Zeichen empfangen
{
dcf_buf[t2]=dcf; //empfangenes Zeichen in zur Zeichenkette hinzufügen
dcf_buf[t2+1]='\0';
t2++; //eine Stelle weiterrücken
}
t=0; //Timer zurücksetzen
}



Ich nehme mal stark an, dass mein Counter t anscheinend nie die eigentlich gewünschten Zahlen erreicht. Sollte ich vielleicht den Voreingestellten Timer, der ja jetzt auf 0,01ms steht auf z.b. mal 10ms sekunden erhöhen? Oder liegt das Problem doch ganz woanders?

Gruß
cesupa

Dirk
20.09.2007, 19:06
Hallo cesupa,


Sollte ich vielleicht den Voreingestellten Timer, der ja jetzt auf 0,01ms steht auf z.b. mal 10ms sekunden erhöhen?

Ich hatte dein Prog wohl nicht richtig angesehen. Poste doch mal das komplette Prog! Eine ISR für 10ms = 0,01s ist völlig ausreichend für die DCF-Decodierung.

Das bedeutet dann, dass in einer Pause von 800ms die Interruptroutine 80x aufgerufen wird. Der Test auf ein 1-Bit wäre dann:
Pause < 78 and Pause > 82 -> EINSBIT

Gruß Dirk

cesupa
21.09.2007, 06:50
So, also hier nochmal mein kompletter code, jegliche Kritik und Verbesserung ist erwünscht, ich bin ja auch noch Anfänger in dieser Sache :):





#include <avr/io.h>
#include <avr/interrupt.h>

#define BAUD 9600UL

#include "../classes/rs232/UART/UART.h"

volatile uint32_t t,t2;
volatile unsigned char dcf=0xff;
volatile unsigned char dcf_buf[128];
volatile unsigned char r;


SIGNAL(SIG_OUTPUT_COMPARE2)
{
if(!(PINC & (1<<PC0))) //Kein Impuls==Pause
{

if(t>78000 && t<82000) //Wenn kleiner 900ms
{
if(dcf!='0')
dcf='1';
}

if(t>88000 && t<92000) //Wenn größer gleich 900ms
{
dcf='0';
}

if(t>170000 && t<180000) //PAUSE für ENDEBIT
{
dcf_buf[t2]='1';
dcf_buf[t2+1]='\0';
t2=0;
r='2';
}

if(t>180000 && t<190000) //Pause für ENDEBIT
{
dcf_buf[t2]='0';
dcf_buf[t2+1]='\0';
t2=0;
r='2';
}

t++;
}
else //Impuls==timer zurücksetzen
{
if(dcf=='0' || dcf=='1') //Wenn Zeichen empfangen
{
dcf_buf[t2]=dcf;
dcf_buf[t2+1]='\0';
dcf=0xff;
t2++;
}
t=0; //Timer zurücksetzen
}

}

void timer_init(void)
{
t2=0;
t=0;
r='0';
TCCR2=(1<<CS22) | (1<<WGM21);
OCR2=160; //0,01ms (?)
TIMSK|=(1<<OCIE2);
sei();
}


int main(void)
{
DDRC&=~(1<<PC0); //Eingangspin DCF-Signal
PORTC|=(1<<PC0); //Pullups aktivieren

timer_init(); //Timer initialisieren

uart_init(); //UART inititalisieren

while(1)
{
if(r=='2') //Wenn Signal zum Senden gesetzt
{
dcf=0xff; //Bit zurücksetzen
r='0'; //Signal zurücksetzen
send("DCF: ");
send(dcf_buf); //Daten an PC senden
send("\r\n");
}
}

return 0;
}



Gruß
cesupa

P.S.: Mein Atmega8 läuft mit einem externen 16MHz Quarz.

Dirk
21.09.2007, 14:20
Hallo cesupa,

ich habe deine Timereinstellung noch nicht nachgerechnet, aber ein Interrupt von 0,01ms = 10us ist viel zu schnell. Da hast du ja nur 10 Taktzyklen in der ISR (bei 1 MHz) und 4 davon gehen schon mit RETI drauf.
Auch bei deinen 16 MHz sind das 160 Zyklen, nach dem Sichern von Registern noch ca. 140-150.
Das ist viel zu wenig für deine Menge an Code in der ISR. D.h. dass du einige Interrupts verpasst, weil dein uC beim folgenden Interrupt noch in der ISR beschäftigt ist.

Nimm eine Abfragefrequenz des DCF-Eingangs von 10ms. Auch 25ms ist noch ausreichend.

Dann kannst du dir die Zählerwerte ja erst mal ohne die 0/1-Bit Zuordnung per RS232 ausgeben. Die Anzeige sollte zwischen 2 Werten schwanken, 1x pro Minute kommt noch ein längerer Wert hinzu.

Danach legst du dann deine Limits für die Bit-Tests fest.

Gruß Dirk

cesupa
21.09.2007, 18:34
Hallo Dirk,

ich glaube, so langsam kann ich meine ersten kleinen Erfolge verbuchen. Ich lasse die Interrupt-Rountine jetzt nur noch alle 10ms laufen und wenn ich mir die Empfangenen Bits anzeigen lasse kommt sowas dabei raus:




11110001111100011111000111110001111100011111000111 1100011111

01111100011111000111110001111100011111000111110001 1111000111

00011111000111110001111100011110000111100011111000 1111100011

10001100000000000000000000000000000000000000000000 0000000000

11100011111000111110001111100011111000111110001111 1000111110

11110000111100011111000111110001111100011111000111 1100011111

01111100011111000111110001111100011111000111110001 1111000111

00011111000111110001111100011111000111110001111100 0111110001

10001111100011111000111110001111100011111000111110 0011111000

11100011111000111110001111100011111000111110001111 1000111110

11111000111110001111100011111000111100011111000111 1100011111
.
.
.
.


Ja, das sieht ja schonmal ziemlich regelmäßig aus, so solls doch auch bestimmt sein, richtig? (Das wäre dann nämlich der Erfolg den ich erwähnt habe O:) )
Aber jetzt stellt sich mir eine neue Frage:

Wenn eine Pause ca. 700-900ms andauert, dann müsste ich ja in meinen Bitfolgen 70-90 gleiche Zeichen hintereinander finden. Dagegen findet man bei genauem hinschauen zwischen den regelmäßigen Bitfolgen auch eine Reihe in der nur Null-Bits stehen, ist das dann vielleicht das Signal für das Endebit?
Wenn ich meinen Pin an den anderen Anschluss des Conrad-DCF-Empfängers anschließe bekomme ich ca. 8 Zeilen Nullen und dann eine regelmäßige Zeile wie oben. Sind diese 8 Zeilen dann vielleicht die Pausen?

Meinen Timer hab ich übrigens wie folgt umgeschrieben:




void timer_init(void)
{
t=0;
t2=0;
TCCR2=(1<<CS22) | (1<<CS20) | (1<<WGM21);
OCR2=156; //10,0ms (?)
TIMSK|=(1<<OCIE2);
sei();
}


Gruß
cesupa

P.S.: Hab mir jetzt mal die Variable t ausgeben lassen. Komischerweise reicht die Zahl ziemlich oft bis ca 200, aber Zahlen zwischen 70 und 90 werden auch erreicht.

Dirk
22.09.2007, 17:54
Hallo cesupa,

ja das sieht ja schon gut aus.

Da sich die Bitfolge um jeweils 1 Bit verschiebt, hast du noch mit der Ende-Erkennung ein Problem.

Du brauchst jetzt die Zählerwerte, um weiter zu kommen.
Du solltest in der 0-Bit- und 1-Bit-Erkennung eine Variable einbauen, die den Wert von t speichert oder du gibt ihn sofort via RS232 aus.
Für das Ende-Bit testest du z.B. nur auf > 1000ms, so dass erst mal beide Ende-Bits (0/1) erfasst werden. Den Wert von t gibst du für das Ende-Bit auch aus.

Dann hast du schon die 3 wesentlichen Werte (normale 0/1-Bits und EIN Ende-Bit, von dem du nicht sicher weißt, ob es Null oder Eins ist. Das hängt vom Datum ab (genauer von der Parität des Datums!). Dieses Ende-Bit bleibt den ganzen Tag lang 1 oder 0 und wechselt meist erst am nächsten Tag (muss aber nicht sein!). Wenn dein t beim Ende-Bit um die 1800ms ergibt, wird es aber ein 1-Bit sein und bei 1900ms ein 0-Bit.

Gruß Dirk

cesupa
22.09.2007, 18:00
Hallo,

hab jetzt mal den Fehler in meinem Timer behoben. Jetzt läuft die ISR wirklich mit 10ms :)

Ich lasse mir grad ein paar Werte ausgeben, unzwar mit folgendendem Code, der in der ISR steht:




if(!(PINC & (1<<PC0)))
{
if(t>70 && t<85)
{
if(dcf!='1')
dcf='1';
}

if(t>85 && t<95)
{
if(dcf!='0')
dcf='0';
}


if(t>172 && t<183)
{
if(dcf!='1')
dcf='1';

if(start!=1)
start=1;
}

if(t>183 && t<192)
{
if(dcf!='0')
dcf=0;

if(start!=0)
start=0;
}

t++;
}
else
{
t=0; //Timer zurücksetzen
t2=1; //Empfangenes Zeichen an PC senden
}



Wenn ich das laufen lasse, erhalte ich ziemlich viele Einsen, jedoch nur recht wenig Nullen(Also hin und wieder mal eine Null zwischen zwei einsen). Ab und zu erscheint auch mal eine längere Folge an Einsen, aber größtenteils erhalte ich solche folgen wie: "01010101..." -Muss das so sein?
Desweiteren empfange ich nie ein ENDEBIT, woran kann das liegen?

Gruß
cesupa

cesupa
22.09.2007, 18:12
So, hab mir jetzt mal ein paar Werte von t ausgeben lassen. Also von ca. 70 bis ca. 90 ist so ziemlich alles dabei. Mittendrin finden sich auch Werte die bis 147 gehen.

Gruß
cesupa

Dirk
22.09.2007, 18:24
Hallo cesupa,

dein Proggi funktioniert noch nicht sauber:

Die Logik sollte z.B. so sein:

Alle 10ms wird der Eingang in der ISR abgefragt.
Ist der Pegel am Eingang gleich wie bei der letzten Abfrage geblieben, wird die Zählvariable inkrementiert und dann passiert nix mehr (Ende der ISR).
Hat sich der Pegel am Eingang geändert, dann ist eine Pause oder ein Impuls beendet und die Zählvariable wird dann mit den 4 Bezugswerten 800/900/1800/1900ms verglichen. Danach wird die Zählvariable auf Null gesetzt.

Die Auswertung der Bits passiert also NUR bei einer Pegeländerung! Bleibt der Pegel gleich, wir nur der Zähler erhöht.
Das heisst natürlich auch, dass du dir immer den letzten Pegel in einer Variable speichern must (wenn er anders als der vorherige war), um ihn beim nächsten Mal vergleichen zu können.

Wenn du das so machst, ist es auch egal, welchen Ausgang (invertiert/ nicht invertiert) des DCF-Empfängers du nimmst, weil jede Impuls-/Pausenlänge gemessen wird, also AUCH die Impulse 100/200ms. Die beachtest du aber einfach nicht.

Gruß Dirk

cesupa
23.09.2007, 07:44
Hallo Dirk,

Ich habs jetzt so gemacht wie du gesagt hast:




get_dcf_bit(); //Bit lesen

if(dcf!=dcf2) //Pegeländerung
{

if(t>69 && t<85)
{
dcf='1';
}

if(t>85 && t<100)
{
dcf='0';
}

if(t>172 && t<182)
{
dcf='1';
//start='1';
}

if(t>182 && t<192)
{
dcf='0';
//start='0';
}

/*send("\r\n");
itoa(t,time,10);
send(time);
send("\r\n");
*/

t2=1; //Daten senden
t=0;
}
else //Pegel gleich geblieben
{
t++;
}

dcf2=dcf; //Pegel kopieren für nächste überprüfung



Wenn ich das ganze laufen lasse kommt sowas dabei raus:




0123456789012345678901234567890123456789

10011100100100101010011010010010101011010010110101 1001000100

0123456789012345678901234567890123456789

10100101101001001010100110100100101001010011010010 1100101101

0123456789012345678901234567890123456789

00101001010010010110110100101100101101101001000010 1001001011

0123456789012345678901234567890123456789

01101001011001011001010100110100100101010001000010 1001010110



Eine exakte Uhrzeit kann ich daraus leider immer noch nicht ablesen. Außerdem empfange ich noch immer keine ENDEBIT, heißt also, wenn ich mir t mal ausgeben lasse, hab ich als größte Zahl meist nur etwas um die 115.

Gruß
cesupa

jar
23.09.2007, 09:47
also, wenn ich mir t mal ausgeben lasse, hab ich als größte Zahl meist nur etwas um die 115.
Gruß
cesupa

dann liegt deine Antenne falsch oder der interne Pullup im Atmel ist nicht gesetzt ?

bei mir liegen je nach Kabellaenge 60-100 ms fuer 0 und 140-170ms fuer 1

ich hab mein erstes DCF Programm am PC entwickelt ist leichter :-) am Gameport die +5V abgreifen und das Signal per Joystickschalterabfrage

Proggi LCC32, in C kann dann auch leichter nach gcc protiert werden, oder du nimmst eines der vielen proggis aus dem i-net

peter dannegger microcontroller.net oder sprut oder .....

ein programm gibt es auch bei wolfgang back

google mal dcf77

cesupa
23.09.2007, 11:02
Hab mir jetzt nochmal die Zeiten am invertierten Pin geben lassen, da kommen in ziemlich kurzen Abständen Zahlen zwischen 180 und 190. Aber ich glaube eher nicht, dass das die Endebits sind, da sie sonst in ziemlich kurzen Abständen erscheinen. Oder müssen die nach ca. jedem 5. "normalen" Wert erscheinen?

Gruß
cesupa

Dirk
23.09.2007, 15:17
Hallo cesupa,

wenn du ein paar Anregungen zur GCC-Programmierung eines DCF-Decoders suchst, schau dir 'mal meine Lösung für den RP6 an:
https://www.roboternetz.de/phpBB2/viewtopic.php?t=34240

Das ist ein Decoder komplett in C, den du gut für dich anpassen kannst.

Gruß Dirk

cesupa
23.09.2007, 19:08
Okay, ich werd mir die ganzen codes bei Gelegenheit mal anschauen.

Danke für eure tolle Hilfe.

Schöne Grüße
cesupa

SprinterSB
03.10.2007, 10:50
Hallo SprinterSB,

ich hab mir jetzt mal deinen Code ein wenig angeschaut und hab gesehen, dass du das DCF-Signal alle 10ms einliest. Aber mir ist noch nicht ganz so klar was du mit dem Signal danach machst? Ich hab das Signal jetzt auch mal aus einer ISR heraus alle 10ms eingelesen und mir über RS232 ausgeben lassen. Dabei kam folgendes raus:
[...]
Wie man sieht, sind die doch eigentlich recht unterschiedlich. Eine Uhrzeit konnte ich aus den Bits auch noch nicht (vollständig) auslesen. Muss ich diese Bitfolgen noch irgendwie bearbeiten, um die korrekten Bits zu erhalten???

Gruß
cesupa

Die bitfolge sieht nicht nach dcf aus, das alle 10ms abgetastet wurde.

beachte.
-- verwende eine schnelle uart ausgabe (irq-getrieben, wenn direkt in der isr ausgegeben werden soll) oder gib via fifo aus. beispiele hats im wiki. ansonsten bisz du nicht sicher, dass es 10ms sind.

-- ist das signal wiklich ok oder evtl. gestört (tft, schaltneztteil, pc, empfangslage, ...)

mein ansatz sst, anhand der möglichen bit-übergänge 00, 01, 10, 11 unterschiedliche aktionen zu machen.

00 und 11 zählen i.w. nen zähler hoch, wobei auch überlängen berücksichtigt werder (mies signal, 59 sek.)

bei 10/10 fängt n neues bit an bzw endet (je nach welches signal du nimmst)

beim bitende wird die dauer ausgewertewt und bitanfang setzt zählrt zurück -- in groben zügen

cesupa
13.05.2008, 15:18
Hallo nach langer Zeit,

ich hab jetzt endlich wieder Lust & Laune gefunden, mich am DCF decodieren ranzuwagen und hab beim Auslesen des DCF Signals schon die ersten Erfolge verbuchen können. Hab das ganze jetzt über Interrupts gesteuert und mein DCF-Empfänger hängt jetzt an INT0 dran. Wenn ich mir das alles über RS232 ausgeben lasse siehts ganz gut aus:




START: 0 1 0 0 0 1 0 1 0 1 1 0 0 1 0 0 0 1 0 0 1 0 1 1 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 0 1 0 0 1 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 START: 0 0 1 0 1 0 0 1 0 1 0 0 0 1 1 0 0 1 0 0 1 0 0 1 0 0 0 0 1 0 1 1 0 1 0 1 0 1 0 0 1 0 0 1 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 START: 0 0 1 0 1 0 0 0 1 0 1 0 1 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 1 1 0 1 0 1 1 0 0 0 1 0 0 1 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 START: 0 0 0 1 1 0 1 1 0 1 0 1 0 0 0 0 0 1 0 0 1 1 0 0 1 0 0 0 0 0 1 1 0 1 0 1 0 1 0 0 1 0 0 1 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 START: 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 1 0 0 1 0 0 0 0 1 0 0 1 0 1 1 0 1 0 1 0 1 0 0 1 0 0 1 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0



So, jetzt muss ich mich nur noch ans eigentliche decodieren ranmachen und da hab ich auch schon eine Frage: Wie macht man das am besten? Ich hab mir schon überlegt, dass man die Bits in ein Array speichern könnte und dann Bit für Bit überprüft um dann daraus die Zeit zu ermitteln, aber das halte ich für sehr Zeitaufwendig und umständlich, da gibt es doch sicherlich eine elegantere Lösung?

Gruß
cesupa

jar_
13.05.2008, 16:20
So, jetzt muss ich mich nur noch ans eigentliche decodieren ranmachen und da hab ich auch schon eine Frage: Wie macht man das am besten? Ich hab mir schon überlegt, dass man die Bits in ein Array speichern könnte und dann Bit für Bit überprüft um dann daraus die Zeit zu ermitteln, aber das halte ich für sehr Zeitaufwendig und umständlich, da gibt es doch sicherlich eine elegantere Lösung?
Gruß
cesupa

http://www.mikrocontroller.net/topic/25071
http://www.mikrocontroller.net/topic/58769#new
http://www.thomas-wedemeyer.de/elektronik/AVR/AVR-Clock_06-05-16.zip

entweder eine der LIB nehmen oder selber machen

SprinterSB
13.05.2008, 18:32
... und noch ne Implementierung...

http://www.gjlay.de/pub/c-code/dcf77.html

-- µC-unabhängiges C
-- Fehlerkorrektur
-- Soft-Clock (falls kein brauchbares DCF-Signal)

cesupa
14.05.2008, 14:35
Hallo,

ich hab da mal was zusammengeschustert, leider funktionierts noch nicht ganz ordentlich. Erstmal wollte ich mir nur die Minuten ausgeben lassen, aber meistens erhalte ich da Werte um die 80 :-k Wenn ich mir die gemessenen Zeitabstände ausgeben lasse kommen da auch Werte zwischen 890 und 1100 raus, dabei müsste es doch so um die 800 und 900 rum liegen, oder? Jedenfalls haben auch ein paar Variationen nicht zum gewünschten Ergebnis geführt, eine korrekte Minutenanzeige hab ich bisher bloß 1, 2 mal hinbekommen. Ich glaube mal, dass mit meinem selbstgeschriebenen Timer was nicht stimmt, könnte einer von euch da mal einen Blick drüber werfen? Ich mach das ganze mit dem 8-Bit-Timer vom Atmega8 bei 16MHz, mit einem Interrupt, der jede ms ausgeführt werden soll, wird ein counter hochgezählt, der dann für die Messung des DCF-Signals verwendet wird. Wahrscheinlich läuft der counter nicht ordentlich...

Gruß
cesupa

cesupa
14.05.2008, 18:26
Hey super, ich habs hingekriegt. Das Problem lag einfach daran, dass ich die Bits vor der eigentlichen Zeit vernachlässigt hab, dadurch hab ich die Daten 20Bits zu früh in meine struct eingelesen.
Danke euch allen für eure große Hilfe :)

Schöne Grüße
cesupa

Wasserkäfer
05.06.2008, 21:25
Hallo,

bin auch noch Anfänger und versuche gerade das DCF Signal zu verarbeiten. Ich Frage den Portzustand über einen Interrupt alle 20ms ab. Das erkennen der Pausen 800/900/1800/1900 klappt soweit auch sehr gut. Bekomme immer Werte +- 20 ms per RS232 am PC angezeigt.

Wie ich jetzt die Pausenlänge den 0/1en zuordnen soll usw. versteh ich jetzt aber net. Hab schon bissl was probiert aber nichts hat geklappt. Per if im Hauptprogramm mit <= und >= Abfragen hat das nicht geklappt. Wäre sehr nett wenn ihr mir da einen Tipp geben könntet und euch mal das Programm bis jetzt anschaut.

Lässt sich bestimmt einiges noch verbessern aber bin schonmal ganz glücklich das die richtigen Pausen erkannt werden ^^


#include "avr/io.h"
#include "function.h"
#include <avr/interrupt.h>

volatile uint16_t dcf_time = 0;
uint8_t dcf_before = 1;
uint8_t dcf_flag = 0;
char dcf_t[4];


int main (void)
{

//Port Konfiguration DCF77 Empfänger

dcf_ddr &= ~(1<<dcf_bit); //PinX als Eingang definieren
dcf_port |= (1<<dcf_bit); //Pullup Widerstand für diesen Pin aktivieren



//Debug Konfiguration, LED1, LED2


DDRC |= (1<<0) | (1<<1); //Bit0 PortC Ausgang
PORTC |= (1<<0) | (1<<1); //Led an PortC aus

//Timer 1 starten, Globale Interrups aktivieren
init_timer();
//USART initialisieren
init_USART();


/*###############################################
# #
# Hauptprogramm #
# #
################################################*/

while(1)
{


//##################### DEBUG ### LED1 zeigt DCF77 Signal#####################
if (dcf_pin & (1<<dcf_bit))
PORTC |= (1<<0); // Ausführen wenn HIGH
else
PORTC &= ~(1<<0); // Ausführen wenn LOW
//################################################## ##########################

}
return 0;
}





//Portzustand DCF77 einlesen und in dcf_flag schreiben
int get_dcf_bit()
{
if (dcf_pin & (1<<dcf_bit))
{
dcf_flag = 1;

}else{
dcf_flag = 0;

}
return(dcf_flag);

}

SIGNAL (SIG_OUTPUT_COMPARE1A)
{
PORTC ^= (1<<1); //DEBUG Zeige Frequenz vom Timer-Interrupt an LED2
get_dcf_bit(); //Pegel vom DCF Signal einlesen und in dcf_flag speichern

if(dcf_flag == 1) //Wenn DCF Signal HIGH > Zeit messen
{
if(dcf_flag == dcf_before) //Führe aus, wenn sich Pegel nicht geändert hat
{
dcf_time += 20; //Zeit +20ms
}else{ //Führe aus bei Pegeländerung
itoa(dcf_time, dcf_t, 10);
sendUSART(dcf_t);
sendUSART(" \r\n");
dcf_time = 0; //Zeitvariable zurücksetzen
}
}

dcf_before = dcf_flag; //Alten Pegel in der Variable dcf_before speichern
}



Hier mal ein Ausschnitt vom PC und den Zeiten


800
920
800
880
900
900
800
900
780
900
800
880
900
880
900
880
800
880
800
800
900
900
900
920
880
800
900
900
900
900
1900
900
800
800
880
880


lg
Wasserkäfer

Dirk
05.06.2008, 21:51
Hallo Wasserkäfer,

das sieht doch schon gut aus! Du kannst 0-Bits (880..920) und 1-Bits (780..800) gut erkennen und auch das Minutenbit (1900), das auch ein 0-Bit ist.
Wenn du die mit einer größer/kleiner-Bedingung testest (Grenze z.B. 850: Wenn größer, dann 0-Bit. Wenn kleiner, dann 1-Bit). Für das Protokollende kannst du genauso testen mit der Grenze 1850.

Die Bits (0/1) schiebst du dann in eine Variable, deren Wert du immer dann auswerten must, wenn ein Minuten/Stunden... Wert erreicht ist. Man erkennt das durch einen DCF-Bitzähler, der bei jedem Bit inkrementiert wird und nach dem Minutenbit auf 0 gesetzt wird.

Viel Erfolg. P.S.: In der ISR sollte es keine USART Aufrufe geben, die dauern zu lange.

Gruß Dirk

Wasserkäfer
09.06.2008, 16:17
Vielen Dank Dirk

Mittlerweile emfpange ich das DCF Signal und es klappt auch über die RS232 ausgabe. Die USART Aufrufe stehen nur aus Debugzweck noch in der ISR. Im späteren Programm fällt das natürlich weg.

Da ich mir jetzt mehrere Strings schon per USART anzeigen hab lassen sind gleich zwei weitere Fragen aufgetaucht :P

1. Wie kann ich sicherstellen, das der µC nur korrekt Empfangen Daten verarbeitet? Mir sind da mal 2 Möglichkeiten eingefallen: Über die Parity Bits wo ich aber noch nicht weis wie man die Quersumme bildet ^^ oder erkennen ob die Zeiten innerhalb der normalen Grenzen liegen 800/900 ms etc. Liegt schlechter Empfang vor ist die Zeit meist wesentlich kürzer/länger was mir so aufgefallen ist.

2. Wie wandel ich den String in meine Minuten/Stunden usw. um?
Dachte mir ein Unterprogramm, dass z.B Bit 21 - 27 nach 0 oder 1 abfrägt und dann jeweils 1,2,4,8,10,20,40 addiert.

Momentan speicher ich die kompletten 59 Bits aber eigentlich kann ich mir die ersten 19 Bits doch sparen. Mit den Informationen kann ich ja nichts anfangen.

Mein momentanes Programm und ein paar Empfangene Signale:


#include "avr/io.h"
#include "function.h"
#include <avr/interrupt.h>

volatile uint16_t dcf_time = 0;
uint8_t dcf_before = 1;
uint8_t dcf_flag = 0;
uint8_t dcf_ctr = 0;
uint8_t dcf_end;
char dcf_t[4];
char dcf[60];


int main (void)
{

//Port Konfiguration DCF77 Empfänger

dcf_ddr &= ~(1<<dcf_bit); //PinX als Eingang definieren
dcf_port |= (1<<dcf_bit); //Pullup Widerstand für diesen Pin aktivieren



//Debug Konfiguration, LED1, LED2


DDRC |= (1<<0) | (1<<1); //Bit0 PortC Ausgang
PORTC |= (1<<0) | (1<<1); //Led an PortC aus

//Timer 1 starten, Globale Interrups aktivieren
init_timer();
//USART initialisieren
init_USART();

/*###############################################
# #
# Hauptprogramm #
# #
################################################*/

while(1)
{


//##################### DEBUG ### LED1 zeigt DCF77 Signal#####################
if (dcf_pin & (1<<dcf_bit))
PORTC |= (1<<0); // Ausführen wenn HIGH
else
PORTC &= ~(1<<0); // Ausführen wenn LOW
//################################################## ##########################


}
return 0;
}





//Portzustand DCF77 einlesen und in dcf_flag schreiben
int get_dcf_bit()
{
if (dcf_pin & (1<<dcf_bit))
{
dcf_flag = 1;

}else{
dcf_flag = 0;

}
return(dcf_flag);

}

SIGNAL (SIG_OUTPUT_COMPARE1A)
{
PORTC ^= (1<<1); //DEBUG Zeige Frequenz vom Timer-Interrupt an LED2
get_dcf_bit(); //Pegel vom DCF Signal einlesen und in dcf_flag speichern

if(dcf_flag == 1) //Wenn DCF Signal HIGH > Zeit messen
{
if(dcf_flag == dcf_before) //Führe aus, wenn sich Pegel nicht geändert hat
{
dcf_time += 20; //Zeit +20ms
}else{ //Führe aus bei Pegeländerung

if(dcf_time < 850) //Erkenne Pause von 800 ms = logisch 1
{
dcf[dcf_ctr] = '1';
dcf_ctr++;
}
if(dcf_time > 850 && dcf_time < 1000) //Erkenne Pause von 900 ms = logisch 0
{
dcf[dcf_ctr] = '0';
dcf_ctr++;
}


if(dcf_time > 1000 && dcf_time < 1850) //Erkenne Pause von 1800 ms = logisch 1, Ende der Übertragung
{
dcf[dcf_ctr] = '1';
sendUSART(dcf);
sendUSART("\r\n");
dcf_ctr = 0;
}
if(dcf_time > 1850) //Erkenne Pause von 1900 ms = logisch 0, Ende der Übertragung
{
dcf[dcf_ctr] = '0';
sendUSART(dcf);
sendUSART("\r\n");
dcf_ctr = 0;
}


dcf_time = 0; //Zeitvariable zurücksetzen
}
}
dcf_before = dcf_flag; //Alten Pegel in der Variable dcf_before speichern
}




00011011101101100100110010000111010010010010001100 000100000
00000011000000100100100001001111010010010010001100 000100000
10101000010101100100110001000111010010010010001100 000100000
00010111010011000100101001000111010010010010001100 000100000
00101001000001100100111001001111010010010010001100 000100000
01000011011011100100100101000111010010010010001100 000100000
10000011000111100100110101001111010010010010001100 000100000
00010011011000100100101101001111010010010010001100 000100000
11001001010100000100111101000111010010010010001100 000100000


Vielen Dank schonmal

Dirk
09.06.2008, 18:14
Hallo Wasserkäfer,

hast du dir 'mal meine C-Version für den RP6 angesehen?
In der Lib (RP6BaseDCFLib) kann man gut sehen, wie ich das gemacht habe, z.B. auch mit der Parity.
https://www.roboternetz.de/phpBB2/viewtopic.php?t=34240

Gruß Dirk