Forum: Mikrocontroller und Digitale Elektronik Atmega16 Timer1 DCF77-Empfänger


von Jochen R. (josch90)


Lesenswert?

Hallo,

ich habe versucht mir einen DCF77-Empfänger zu programmieren. sobald das 
L-Signal vom Empfänger kommt wird ein interrupt an PD2 ausgelöst, das 
programm zählt ca 100ms und prüft ob das Signal immer noch L ist, um 
eine 1 oder eine 0  im Signal festzustellen, dies wird dann im T-Bit 
gespeichert und je nach sekundenzahl ins passende Register geschoben. 
Die Pause in der 59. sekunde soll von einem Compare interrupt am Timer1 
erkannt werden und den sekundenzähler wieder auf 0 setzen, dass Problem 
liegt allerdings darin, dass das ganze Programm nicht mehr funktioniert, 
sobald ich den Timer im Programm einschalte, er scheint viel zu schnell 
zu Zählen, kann mir jemand sagen was ich falsch mache?

Im Programm habe ich einige Ausgabebefehle integriert, in der hoffnung, 
dass ich den Fehler finde, allerdings vergebens...

Quelltext:
    .include "m16def.inc"
.org 0000
    rjmp init
.org int0addr
    rjmp int0routine
.org OC1Aaddr
    rjmp secpause

.equ xtal = 8000000
.def minute = r24
.def stunde = r23

  init:
    ldi r16, low(ramend)
    out spl, r16
    ldi r16, high(RAMEND)
    out sph, r16

    ldi r16, 0xff      ;Portb->Ausgang
    out ddrb, r16
    out ddrc, r16      ;auch
    out ddra, r16      ;nochmal

    ldi r16, 0x00      ;Portd->Eingang
    out ddrd, r16

    ldi r16, 0b00000010    ;INT0 konfigurieren
        out MCUCR, r16

        ldi r16, 0b01000000    ;INT0 aktivieren
        out GICR, r16

    ldi r16, 0b00000101    ;Timer1 aktivieren, teiler 1024
    out tccr1b, r16

    ldi r16, 0b00010000    ;Interrupt bei Compare A Match
    out timsk, r16

    ldi r16, 0xff      ;Compare A Match wert festlegen
    out ocr1al, r16
    ldi r16, 0x20
    out ocr1ah, r16

    ldi r25, 0xff

    ldi r16, 0b01010101
    out porta, r16

    sei



  loop:
    ;bld r16, 5
    ;com r16
    in r20, porta
    mov r16, r25
    com r16
    out portb, r16
    mov r16, minute
    com r16
    out portc, r16
    rjmp loop



;***************************Entscheidung über 1 oder 
0*******************
;100ms 0V = Logisch 0      200ms 0V = Logisch 1

  int0routine:
    cli
    ;push r16
    ldi r16, 0x00
    out tcnt1h, r16
    out tcnt1l, r16
    rcall count
    ;pop r16
    inc r25  ;sekundenzähler erhöhen
    clt      ;T-Flag wird gelöscht
    sbis portd, 2
    set      ;T-Flag wird gesetzt
    rjmp savedcf


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Hier wird anhand des Sekundenzählers der richtige speicherort
;für jedes bit bestimmt.

  savedcf:
    cpi r25, 0
    breq ausgabe

    cpi r25, 21
    brlo end
    breq min1

    cpi r25, 22
    breq min2

    cpi r25, 23
    breq min4

    cpi r25, 24
    breq min8

    cpi r25, 25
    breq min10

    cpi r25, 26
    breq min20

    cpi r25, 27
    breq min40

    cpi r25, 28
    breq end

    cpi r25, 29
    breq std1

    cpi r25, 30
    breq std2

    cpi r25, 31
    breq std4

    cpi r25, 32
    breq std8

    cpi r25, 33
    breq std10

    cpi r25, 34
    breq std20

    rjmp end  ;35 aufwärts un 1-20 wird ausgeblendet


  min1:
    bld minute, 0
    rjmp end

  min2:
    bld minute, 1
    rjmp end

  min4:
    bld minute, 2
    rjmp end

  min8:
    bld minute, 3
    rjmp end

  min10:
    bld minute, 4
    rjmp end

  min20:
    bld minute, 5
    rjmp end

  min40:
    bld minute, 6
    rjmp end

  std1:
    bld stunde, 0
    rjmp end

  std2:
    bld stunde, 1
    rjmp end

  std4:
    bld stunde, 2
    rjmp end

  std8:
    bld stunde, 3
    rjmp end

  std10:
    bld stunde, 4
    rjmp end

  std20:
    bld stunde, 5
    rjmp end




  end:
    sei
    reti

  ausgabe:
    ;out portb, stunde
    ldi stunde, 0x00
    ldi minute, 0x00
    sei
    reti

;Verzögerung
  count:
    ldi r16, 0x00
    ldi r17, 0x00
  count_1:
    inc r16
    cpi r16, 0xff
    brne count_1
    inc r17
    cpi r17, 90
    brne count_1
    ret



;*********************secPause******************************

secpause:
    ;push r16
    ldi r16, 0x00
    out tcnt1h, r16
    out tcnt1l, r16
    ldi r25, 0xff
    ;pop r16
    com r20
    out porta, r20
    reti


Gruß

Jochen

von spess53 (Gast)


Lesenswert?

Hi

>    ldi r16, 0xff      ;Compare A Match wert festlegen
>    out ocr1al, r16
>    ldi r16, 0x20
>    out ocr1ah, r16

Datenblatt:
To do a 16-bit write, the high byte must be written before the low byte. 
For a 16-bit read, the low byte must be read before the high byte.

In fast allen Datenblättern gibt es einen Punkt:'Accessing 16-bit 
Registers'

MfG Spess

von Jochen R. (josch90)


Lesenswert?

Danke, das hat mir schon um einiges weitergeholfen, allerdings zählt 
mein sekundenzähler relativ warlos, an was kann das liegen, evtl. an 
empfangsstörungen?

Gruß

Jochen

von spess53 (Gast)


Lesenswert?

Hi

Ich nehme an, das deine 100ms Abfrage kritisch ist. Also evtl. zu knapp.

Ich habe das damals anders gelöst:

Bei der Startflanke wird der Zähler auf Null gesetzt. Dann wird das Ende 
des Impulses abgewartet und danach der Zählerstand abgefragt. Zwischen 
50 und 150 ms eine Null, bei >150 und <250 eine Eins. >250 
Schaltsekunde. Nur so vom Prinzip her.

MfG Spess

von Martin S. (smartinick)


Lesenswert?

Ich mach es auch so ähnlich wie Spess, allerdings pin-change interrupt 
da int0/1 schon belegt sind.
damit ist auch die zustands/flanken sache egal und es ist auch egal ob 
der high- oder low-aktive ausgang des dcf-modules verwendet wird.

dann einen timer der alle 10ms um 1 hochzählt und im pinchange-int den 
zähler wert testen.

hat den vorteil das ich auch einfach empfangsstörungen ausbügeln kann, 
z.b. alles kleiner als 80ms kann nur eine störung sein, alles über etwa 
240ms bis 780ms kann auhc nur eine störung sein.
erreicht der timer 255 ist es ein time-out und somit kein signalempfang.
den timer resete ich nicht am ende des 100/200ms puls sondern beim 
beginn der des nächsten 100/200ms puls, damit lässt sich der 
minuten-puls über 1800 bzw. 1900ms auch detektieren.

die bits shifte ich einfach aus dem carry in den sram und beim 
minuten-puls dann von dort wieder per shift um x stellen bis zur 
gewünschten info, konvertieren, weitershiften bis zur nächsten. parity 
lässt sich damit auch recht einfach prüfen.

funktioniert in wien mit schlechtem empfang nicht mal so schlecht, 
einzig in den abendstunden wenn rundherum die tv-geräte laufen klappt es 
öfter mal nicht.

aja und das ganze in asm auf einem mega168 mit 20mhz. wobei die hohe 
taktfrequenz freilich nicht notwendig ist für dcf, klar.

von spess53 (Gast)


Lesenswert?

Hi

Viele Wege führen nach Rom. Mein Verfahren ist auch nicht unbedingt das 
Nonplusultra. Ich hatte nur im Rahmen dieser 'DCF... 
Wetter...'-Diskussion  mal eigene Aufzeichnungen gemacht und dafür 
schnell was 'zusammengeschossen'. Hat aber erstaunlich gut funktioniert.

MfG Spess

von Martin S. (smartinick)


Lesenswert?

Guten Morgen,

Ist im Prinzip eh sehr einfach... außer man hat schlechten empfang und 
immer wieder störungen im signal. Und da find ich das verfahren das du 
ja auch anwendest (timer und vergangene Zeit auswerten) am einfachsten, 
auswertung im hintergrund via isr-routinen ist möglich und es lässt sich 
einfach erweitern um eben die störsignale mithilfe von zeitfenstern zu 
ignorieren.

kommt natürlich ein störsignal innerhalb eines "gültigen" zeitfensters 
und das 2x dann kanns mit der parity auch schon schwierig werden. -> 
plausibilitätstests wie 0<=sek/min=<59 usw. helfen dann auch noch.

aber bei gutem empfang ist die sache ja eigentlich sehr einfach, bzw. in 
der praxis welche der theorie noch am ehesten entspricht ;)

schönen tag, mfg, Martin.

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.