Forum: Mikrocontroller und Digitale Elektronik ATmega32 DCF77-Signal


von Max S. (deadline)


Lesenswert?

Hallo liebe Community!

Ich versuche mithilfe eines Atmega32 und eines DCF77-Empfängermoduls, 
das DCF77 Signal auszuwerten. Die "Auswertung" soll im Moment nur darin 
bestehen, je nachdem wie lange die letzte Absenkung des Pegels war 
(100ms/200ms) eine rote bzw. grüne LED zum Leuchten zu bringen.
Soweit so gut.

Ich habe das Modul korrekt angeschlossen und zum Laufen bekommen - 
getestet hatte ich das mit nur einer LED, die dann mal kürzer bzw länger 
geblinkt hat. Der Data Anschluss des DCF-Moduls geht dabei in den INT0 
des Atmega32, der bei steigender sowie fallender Flanke einen Interrupt 
auslöst.

Da ich mich zu einer Funkuhr tasten will versuche ich jetzt also die 
verschieden langen Pegelabsenkungen zu erkennen als logische 1(200ms) 
oder 0(100ms). Je nachdem soll die grüne oder die rote LED blinken.

Kleine Information: Habe meinen Atmega32 noch im Default-Zustand, also 
bei 1Mhz.

Aus irgendwelchen Gründen kommt dabei aber ein dauerhaftes Leuchten der 
grünen LED raus, was sicherlich keinen Sinn macht.
Ich wäre für Hilfe außerordentlich dankbar, denn auch die vielen Stunden 
Kopf zerbrechen haben mich der Lösung irgendwie noch nicht näher 
gebracht.
Mein Code befindet sich unterhalb.

Sollte ich auf der vollkommen falschen Fährte sein, so würde ich mich 
über ein bisschen Hilfe ebenso freuen :-)

vielen Dank für alle Reaktionen im Vorraus,
Max

1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
void startTimer(void);
5
void stopTimer(void);
6
void display(void);
7
8
int main(void)
9
{
10
startTimer();
11
12
DDRA = 0b00000011;          // ROTE LED PINA1, GRÜNE LED PINA0 || GRÜN = 0; ROT = 1
13
PORTA = 0b00000000;          // Beide AUS
14
15
GICR   |= (1<<INT0);         // INT0 enable im General Interrupt Control Register
16
MCUCR   |= (1<<ISC00);         // Sowohl Steigende && fallende Flanke lösen Interrupt aus
17
sei();
18
19
  while(1)
20
  {
21
  }
22
}
23
24
ISR(INT0_vect){
25
display();              // Bringt die rote oder gruene LED zum leuchten
26
stopTimer();            // stoppt den Timer
27
startTimer();            // startet ihn neu
28
}
29
30
void startTimer()
31
{
32
TCCR1B |= (1<<CS10) | (1<<CS11);        // TIMER MIT PRESCALE 64 STARTEN
33
}
34
35
void stopTimer()
36
{
37
TCNT1 = 0;
38
TCCR1B |= ~(1<<CS10) | ~(1<<CS11);    // TIMER STOPPEN
39
}
40
41
void display()
42
{
43
44
if(TCNT1 < 2343) // <150ms
45
{
46
      PORTA ^= 1 << PINA0;    // GRUENE LED -> LOGISCHE 0
47
      PORTA |= ~(1<<PINA1);    // rote led aus
48
}
49
50
if(TCNT1 >= 2343 && TCNT1 < 3906) // >150ms aber <250ms
51
{
52
      PORTA ^= 1 << PINA1;    // ROTE LED -> LOGISCHE 1
53
      PORTA |= ~(1<<PINA0);    // gruene led aus
54
}
55
}

von Karl H. (kbuchegg)


Lesenswert?

Das ganze Ansatz ist nicht besonders klug.

Zum einen: lass den Timer durchlaufen! Dieses ganze "Timer stoppen - 
Timer starten - Timer 0 setzen" ist den Aufwand nicht wert!
Lass den Timer durhclaufen!
Wenn du eine Armbanduhr hast und der Skirennläufer startet, wenn der 
Sekundenzeiger bei 12 ist und er geht durchs Ziel, wenn der 
Sekundenzeiger bei 43 ist, dann war er 43 - 12 gleich 31 Sekunden 
unterwegs. Deswegen musst du deine Uhr nicht dauernd stoppen und neu 
starten oder auf 0 setzen.

Zum anderen.
Du bist an der Pulslänge interessiert. Dein Puls beginnt mit der 
ansteigenden Flanke.
Hier
1
ISR(INT0_vect){
2
display();              // Bringt die rote oder gruene LED zum leuchten
3
stopTimer();            // stoppt den Timer
4
startTimer();            // startet ihn neu
5
}
prüfst du aber nichts dergleichen. Du behandelst sowohl steigende als 
auch fallende Flanke gleich.
Interessiert bist du daran, welchen Zählerstand der Timer bei der 
steigenden Flanke hat, das ist deine erste 'Sekundenzeiger-Zeit'. Bei 
der fallenden Flanke nimmst du dann die zweiten 'Selundenzeiger-Zeit' 
und subtrahierst du von der ersten. Damit hast du deine Pulslänge.

Woran ewrkennst du, welche Flanke vorliegt?
Na, ganz einfach. Bei einer steigenden Flanke, ist der Pin danach auf 1. 
Und das kann man abfragen. Nur weil du die INT0 Funktionalität akriviert 
hast, verliert der Pin ja nicht seine normalen Eigenschaften und kann 
auch weiterhin ganz normal über das PIN Register ausgelesen und 
abgefragt werden.
1
ISR(INT0_vect)
2
{
3
  static uint16_t startTime;
4
5
  if( Pin ist auf 1 )
6
    startTime = TCNT1;
7
8
  else
9
  {
10
    uint16_t Dauer = TCNT1 - startTime;
11
 
12
    if( Dauer <  2343 )
13
    {
14
      grüne LED ein
15
      rote LED aus
16
    }
17
    else
18
    {
19
      grüne LED aus
20
      rote LED ein
21
    }
22
  }
23
}

die 2343 hab ich jetzt nicht nachgerechnet.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

>       PORTA ^= 1 << PINA0;    // GRUENE LED -> LOGISCHE 0
>       PORTA |= ~(1<<PINA1);    // rote led aus

No

Pin auf 1 setzen:
1
  PORTx |= ( 1 << PINnummer );
Pin auf 0 setzen:
1
  PORTx &= ~( 1 << PINnummer );

von c-hater (Gast)


Lesenswert?

Karl Heinz schrieb:

> Das ganze Ansatz ist nicht besonders klug.
[...]
> Du bist an der Pulslänge interessiert. Dein Puls beginnt mit der
> ansteigenden Flanke.

Dieser Ansatz ist allerdings auch nicht gerade clever, denn dieses Axiom 
ist keins, sondern hängt von der Hardwareschaltung ab. OK, man kann die 
"Polarität" des Eingangssignals in einem halbwegs vernünftigen Programm 
natürlich konfigurieren.

Aber der Punkt ist, es ist für einen wirklich sinnvoll programmierten 
Fensterdiskriminator überhaupt nicht nötig, sich auf eine bestimmte 
Polarität des Eingangssignals von vorhnerein festzulegen, so eine 
Konfigurationsmöglichkeit ist deshalb einfach überflüssig. Denn in der 
weiteren Signalverarbeitung interessieren ja überhaupt keine Pegel, 
sondern nur die Abstände der Pegeländerungen.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:
>>       PORTA ^= 1 << PINA0;    // GRUENE LED -> LOGISCHE 0
>>       PORTA |= ~(1<<PINA1);    // rote led aus
>
> No
>
> Pin auf 1 setzen:
>
1
>   PORTx |= ( 1 << PINnummer );
2
>
> Pin auf 0 setzen:
>
1
>   PORTx &= ~( 1 << PINnummer );
2
>

Das dürfte auch der Grund sein, warum du Dauer-Grün bekommst. Deine 
Pin-Setzterein waren einfach nur falsch.

von Karl H. (kbuchegg)


Lesenswert?

c-hater schrieb:

> Dieser Ansatz ist allerdings auch nicht gerade clever,

hast du recht

> weiteren Signalverarbeitung interessieren ja überhaupt keine Pegel,
> sondern nur die Abstände der Pegeländerungen.

Das wird zb dann interessant, wenn es darum geht die Startsekunde zu 
entdecken.

OK. Kommando retour. Den Pegel braucht man nicht abfragen.
Das ändert aber nichts daran, dass man den Timer nicht dauernd startet 
und stoppt bzw. ganz einfach die Differenz der Zählerstände nimmt.

von Max S. (deadline)


Lesenswert?

So Hallo nochmal!
Ich habe mich für mein Funkuhr Projekt jetzt zunächst einmal dem LCD 
zugewendet, damit ich mal vernünftige Ausgaben machen kann.

Die beschriebene Methode von euch hat für mich nicht ganz so reibungslos 
funktioniert, weil ich offenbar kein schönes DCF Signal reinbekomme, der 
Pegel hüpft wohl bei jeder Pegeländerung munter hin und her was dazu 
führt, dass ich jede Menge Pegeländerungen habe die gar nicht im Signal 
eigentlich kontrolliert drin sind. Kann ich das irgendwie umgehen?

Deswegen arbeite ich mit einem Sampling mit 10ms Abstand.
Aktuell stehe ich allerdings wieder vor demselben Problem wie vorher. 
Erst einmal möchte ich den Minutenbeginn (sprich > 1,5s nicht-Modulation 
des Signals) erkennen.
Dazu habe ich mir einen Timer eingestellt, der alle 10ms eine Interrupt 
Routine ausführt. Dabei schaue ich mit einem zweiten Counter nach jeder 
Sekunde ob ich an meinem Pin einen High oder Low Pegel habe. Für beide 
Fälle habe ich eine Zählvariable, die inkrementiert wird. 
Dementsprechend sollte ich für eine 100ms Absenkung einen Counterwert 
für Low Signale von 10 bekommen, für eine 200ms Absenkung einen 
Counterwert für Low Signale von 20. Für die Pause in der 59. Sekunde 
müsste ein Counterwert für High Signale von 100 bekommen, oder nicht?
Um dafür ein Gefühl zu bekommen habe ich mir in einem Beispielprogramm 
immer die Werte der Counter ausgeben lassen (in diesem Beispiel für den 
High-Counter). Leider kommen da nur ganz komische - für mich 
unerklärbare Werte raus wie beispielsweise: 20 20 40 41 20 21

Wenn mir jemand helfen könnte wäre ich extrem dankbar!

Hier mein Code: (die lcd_* routinen sind für die Ausgabe verantwortlich)
1
ISR(TIMER1_COMPA_vect) // alle 1s
2
{
3
  
4
  itoa(highCounter, chighCounter, 10);
5
  lcd_sendString(chighCounter);
6
  lcd_sendString(" ");
7
  debugEnableTimesCounter++;
8
  
9
  if(debugEnableTimesCounter == 3)
10
  {
11
    lcd_goto(0,1);
12
    lcd_sendString("            ");
13
    lcd_goto(0,1);
14
  }
15
  
16
  if(debugEnableTimesCounter == 6)
17
  {
18
    lcd_goto(0,0);
19
    lcd_sendString("            ");
20
    lcd_goto(0,0);
21
    debugEnableTimesCounter = 0;
22
  }
23
  
24
  lowCounter = 0;
25
  highCounter = 0;
26
  TCNT1 = 0;
27
}
28
29
ISR(TIMER2_COMP_vect) // alle 10ms (FEHLER 16%)
30
{
31
  
32
  if(bit_is_set(PIND, 2)) // HIGH Pegel?
33
  {
34
    highPegel = 1;
35
    highCounter++;
36
  }
37
  else if(bit_is_clear(PIND,2)) // LOW Pegel?
38
  {
39
    lowCounter++;
40
    highPegel = 0;
41
  }
42
  TCNT2 = 0;
43
}

: Bearbeitet durch User
von Bernie (Gast)


Lesenswert?

Oha, erst mal einen 40-poligen Mega32 verbauen, wo ein
14-poliger Tiny24 reicht.

Auch ein 244-poliger Controller hilft nicht, wenn man
nicht mal im Kopf durchgespielt hat,
- was die Charakteristika des DCF-Signals sind
- und welche Gegebenheiten eines µCs in Frage kommen,
diese auf einfache Weise zu erfassen.

Deine geschilderten Test-Szenarien sind ein Zeichen dafür,
dass du dich gewaltig anstrengst.
Aber (Waschmittelelwerbung:) Mühe allein genügt nicht!

Woher sollen wir wissen, ob deine LCD-Routinen nicht so
lange dauern, dass sie die DCF-Bearbeitung blockieren?
Oder ihnen per Interrupt die Daten mittendrin ausgetauscht
werden?

Mein Rat:
ERST Hirn einschalten, dann programmieren.

Mit einer DCF-Uhr haben sich hier schon viele Leute
in die µC-Programmierung eingearbeitet!

von Paul (Gast)


Lesenswert?

Wenn alles nicht hilft, besorg dir einfach DCF-Decoderchip, dann kannst 
Du Zeit per RS232 einlesen. macht ohnehin oft Sinn denn sonst muss man 
immer Timer opfern den man oft für anderes braucht.

http://www.shop.robotikhardware.de/shop/catalog/product_info.php?products_id=278

Datenblatt: http://www.robotikhardware.de/download/DCF_RS1.pdf

von Karl H. (kbuchegg)


Lesenswert?

Max S. schrieb:

> Ich habe mich für mein Funkuhr Projekt jetzt zunächst einmal dem LCD
> zugewendet, damit ich mal vernünftige Ausgaben machen kann.

Sehr vernünftig.
Ohne Debug-Möglichkeit ist man auf die Methode "Stochern im Nebel" 
angewiesen. Und die ist nicht sehr effizient.

> Pegel hüpft wohl bei jeder Pegeländerung munter hin und her was dazu
> führt, dass ich jede Menge Pegeländerungen habe die gar nicht im Signal
> eigentlich kontrolliert drin sind. Kann ich das irgendwie umgehen?

Die Frage ist eher:
Hat dein DCF Modul überhaupt vernünftigen Empfang, oder ist das was du 
da an deinen Zahlen siehst nicht eigentlich ein Zeichen dafür, dass der 
Empfänger einfach nur irgendwelches Rauschen durchstellt, weil das genau 
das ist, was aus dem Empfänger rauskommt.

Um sicher zu gehen, dass die Zahlen real sind, würde ich mir mal das 
original Signal vom DCF-Empfänger sichtbar machen. Zb mit einer LED. 
Wenn du nur wild flackert, dann hast du keinen Empfang. Und dagegen 
hilft auch die beste Software nichts.
Wenn die LED aber ruhig vor sich hinblinkt, dann wird es wohl eher ein 
Programmfehler sein, auf die dich deine Zahlen hinweisen.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

> . Leider kommen da nur ganz komische - für mich unerklärbare Werte
> raus wie beispielsweise: 20 20 40 41 20 21

Die Werte sehen von den Verhältnissen her ja nicht so schlecht aus. Da 
würde ich ehrlich gesagt erst mal die 10ms Zeitbasis in Frage stellen, 
auf der diese Zahlen ja letzten Endes basieren.

Nichts desto trotz denke ich nicht, dass dich dieser Ansatz in Summe 
weiter bringt. Denn du sampelst ja unsynchronisiert. D.h. du weisst 
nicht, ob dein Sampleintervall nicht zb so liegt

1
      +-------+                 +--------+
2
      |       |                 |        |
3
      |       |                 |        |
4
   ---+       +-----------------+        +-----------
5
          |                           |
6
          |<------------------------->|
7
          |      1 Sekunde            |

und du daher die Samples 2-er Pulse miteinander vermischt.

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.