' --------------------------------------------------------------------------
' Dekodierung des DCF77-Signals auf der Open-Micro, Open-Mini und Open-Midi
' In OCBASIC 1.05 auf der Open-Midi von Dietmar Harlos am 12. Dezember 2006
' erweitert um Datum und Fehlerabfrage Feb. 2007 von Eckhard Raetz
' --------------------------------------------------------------------------
' Es wird der invertierte Ausgang des preiswerten DCF77-Empfangsmoduls mit
' der Conrad-Bestellnummer 641138 verwendet. Sicherheitshalber sollte ein
' Widerstand 10 kOhm zwischen Ausgang und +5 Volt geschaltet werden.
' Die Befehle BEEP, GET, INPUT und SLOWMODE deaktivieren standardmaessig
' die Interrupts und fuehren deshalb zu Empfangsstoerungen.
'Das Zeitprotokoll des DCF Senders der Physikalisch-Technische Bundesanstalt (PTB) in Braunschweig :
'Sekunde: Bedeutung:
'0. Minutenbeginn (immer LOW)
'1. - 14. Reserviert, keine Bedeutung
'15. Reserveantenne aktiv
'16. Umstellung von Sommer- auf Winterzeit, oder umgekehrt
'17. Sommerzeit aktiv
'18. Winterzeit aktiv
'19. Ankündigung Schaltsekunde
'20. Zeitbeginn, (immer High)
'21. - 27. Minute plus 1, 2, 4, 8, 10, 20, 40
'28. Prüfbit Minute, Parität gerade 0 oder ungerade 1
'29. - 34. Stunde plus 1, 2, 4, 8, 10, 20
'35. Prüfbit Stunde
'36. - 41. Tag plus 1, 2, 4, 8, 10, 20
'42. - 44. Wochentag plus1, 2, 4
'45. - 49. Monat plus 1, 2, 4, 8, 10
'50. - 57. Jahr plus 1, 2, 4, 8, 10, 20, 40, 80
'58. Prüfbit Datum, Parität 36 bis 57 Sekunde
'59. fehlt zur Erkennung des Minutenanfangs
' --------------------------------------------------------------------------
DEFINE dcfport bPTA3 'DCF77-Empfaenger-AusgangDEFINE dcfimpuls BYTE
DEFINE dcfzaehler BYTE '8 Byte Zwischenspeicher fuer DCF77-Routine.
DEFINE dcfmin BYTE
DEFINE dcfhour BYTE
DEFINE dcfday BYTE
DEFINE dcfmonth BYTE
DEFINE dcfyear BYTE
Define dcfmerk Byte 'zur Ganggenauigkeit
DEFINE dcfstat BYTE
define dcf_ok Bit [7] of dcfstat 'wird im Basicprogramm verwendet
DEFINE dcfparity 0 'Paritaet muss auf Bit Nummer 0 liegen!
DEFINE dcfparmsk &b00000001 'Bitmaske fuer Paritaetsbit
DEFINE dcftog 1 'letzter Portzustand (zur Flankendetektion)
DEFINE dcfok 6 '"DCF-Signal okay"
DEFINE dcfset 7 '"Interne Uhr der OM wurde gestellt"
' --------------------------------------------------------------------------
' User-Assemblerroutine im Timer Overflow Interrupt.
' Sie wird in den 20-ms-TIMER-Interrupt der OM eingebunden und deshalb
' 50 mal in der Sekunde aufgerufen. In der folgenden Interruptroutine
' wird der DCF77-Pin, an dem der invertierte Ausgang des DCF77-Empfaengers
' angeschlossen ist, abgefragt.
' Das Schluesselwort INLASM kennzeichnet die folgende PROCEDURE als
' Assemblerroutine ("IAR"). Dadurch, und dass sie unmittelbar am Anfang
' des Programms steht, wird sie zu einer IIAR, also zu einer Assembler-
' Interruptroutine, die bei einem aktivierten User-Interrupt gestartet wird.
PROCEDURE iiar_tofl INLASM
! cbeqa #iTOFL,iiar_tofl_istofl 'falls kein Timer Overflow Interrupt,
! jmp iiar_tofl_ende+2 ' dann zur naechsten IIAR in der Kette
#iiar_tofl_istofl
! lda dcfimpuls 'Variable zur Messung der DCF-Impulslaenge
! inc dcfimpuls 'Dcfimpuls = dcfimpuls + 1
! bmi ende1 'Signal nicht mehr vorhanden (timeout)
' Je nachdem, ob der DCF-Port high oder low ist, werden verschiedene Code-
' teile (Subroutinen) aufgerufen. Es werden LOW- und HIGH-Flanken erkannt.
! brset dcfport,PTA,iiar_tofl_portishigh
' Subroutine fuer LOW-Flanke. Der DCF-Impuls beginnt. Das geschieht am
' Beginn einer Sekunde oder einer Minute.
#iiar_tofl_portislow
! brclr dcftog,dcfstat,ende
! bclr dcftog,dcfstat
! cmp #75 'seit mehr als 1,5 Sekunden kein Impuls?
! bcs iiar_tofl_keinminsync 'dann wurde der Minutensync empfangen
! brclr dcfok,dcfstat,iiar_tofl_keindcfok
! lda dcfzaehler
! cmp #59 'die dekodierte DCF-Uhrzeit in die System-
! bne iiar_tofl_keindcfok ' variablen fuer die RTC der OM uebernehmen,
! jmp uebernahme
#iiar_tofl_keindcfok 'nach dem Minutensync auf jeden Fall den
! jmp kein_ok ' "DCF-Signal ist okay" setzen.
#iiar_tofl_keinminsync
! clr dcfimpuls 'und in jedem Fall den Zaehler fuer die Laenge
#ende
! jmp iiar_tofl_exit ' des DCF-Impuls auf Null setzen und beenden
#ende1
! jmp iiar_tofl_cleardcfok
' Subroutine fuer HIGH-Flanke. Der DCF-Impuls endete und wird ausgewertet.
#iiar_tofl_portishigh
! brset dcftog,dcfstat,ende
! bset dcftog,dcfstat
! cmp #3 'falls der DCF-Impuls weniger als 0,06 Sekunden
! bcs iiar_tofl_cleardcfok ' dauerte ist das Signal nicht okay
! ldx dcfzaehler 'Zaehler fuer die DCF-Impulse ins Indexregister laden
! inc dcfzaehler 'und um 1 erhöhen.
! cmpx #20 'Wenn 20te Sekunde ...
! bcs ende ' kleiner 20te Sekunde, interessiert nicht.
! beq iiar_20te_sekunde ' ....dann beginnt mit dem nächsten Impuls das Minutensignal.
! cbeqx #28,iiar_tofl_checkparity 'Nach Minuten Empfang
! cbeqx #35,iiar_tofl_checkparity 'Nach Stunden Empfang
! cmpx #58 'nach Datums Empfang
' Paritaet kontrollieren und wieder loeschen.
! beq iiar_tofl_checkparity ' Wenn 58 Sekunde empfangen? -> Paritaet kontrollieren
! bcc ende 'Beenden, Wenn ueber Datum hinaus.Sollte nicht sein.
! cmp #7 'dcfimpuls (im Accu) wird mit 7 verglichen
'typische Werte: 4 & 5 bei 0-Bit, 9 & 10 bei 1
! bcs ende 'bei >= 0,14 s: 1-Bit, sonst: 0-Bit -> beenden
! lda dcfstat 'Paritaet berechnen/aktualisieren
! xor #dcfparmsk
! sta dcfstat 'und wieder speichen
! cmpx #28
! bhi iiar_tofl_addhour 'beim Empfang eines 1-Bits das Bit von BCD nach
! lda dcftable-21,x ' binaer konvertieren und temporaere Minute und
! add dcfmin ' zusammenaddieren
! sta dcfmin
! bra iiar_tofl_exit
#iiar_tofl_addhour
! cmpx #35 'desgleichen bei Stunde
! bhi iiar_tofl_addday
! lda dcftable-29,x
! add dcfhour
! sta dcfhour
! bra iiar_tofl_exit 'und beenden
#iiar_tofl_addday
! cmpx #41 'bei Tag
! bhi wtag
! lda dcftable-36,x
! add dcfday
! sta dcfday
! bra iiar_tofl_exit 'und beenden
#wtag:
!cmpx #44 'Wochentag, wird nicht ausgewertet
!bhi iiar_tofl_monat
!bra iiar_tofl_exit
#iiar_tofl_monat:
!cmpx #49 'Monat
!bhi iiar_tofl_jahr
!lda dcftable-45,x
!add dcfmonth
!sta dcfmonth
!bra iiar_tofl_exit
#iiar_tofl_jahr:
!cmpx #58 'Jahr
!bhi iiar_tofl_exit
!lda dcftable-50,x
!add dcfyear
!sta dcfyear
!bra iiar_tofl_exit
#iiar_20te_sekunde
!cmp #7 'die 20te Sekunde muß HIGH sein
!bcs kein_ok 'wenn nicht von vorn
!bra iiar_tofl_clearparity 'wenn ja Parityflag löschen.
#iiar_tofl_checkparity 'empfangene Paritaet mit der berechneten
! cmp #7 ' Paritaet vergleichen und bei einem Fehler
! clra ' das Okay-Flag loeschen
! adc dcfstat
! lsra
! bcs iiar_tofl_clearparity
#iiar_tofl_cleardcfok 'Okay-Flag loeschen
! bclr dcfok,dcfstat
#iiar_tofl_clearparity 'Bit zur Berechnung der Paritaet loeschen
! lda #dcfparmsk
! coma
! and dcfstat
! sta dcfstat
#iiar_tofl_exit 'Ruecksprung in das Betriebssystem
! clc 'CLC bedeutet: Betriebssystemroutinen fuer
! rts ' Tofl-Interrupt werden ausgefuehrt
#uebernahme
! ldx dcfmin
! cmpx #59 'wenn Minute größer als 59
! bhi kein_ok
! ldx dcfhour
! cmpx #23 'wenn Stunde größer als 23
! bhi kein_ok
! ldx dcfday
! cmpx #31 'wenn Tag größer als 31
! bhi kein_ok
! ldx dcfmonth
! cmpx #12 'wenn Monat größer als 12
! bhi kein_ok
! ldx dcfyear
! cmpx #10 ' 2011 muß ein Update gemacht werden
! bhi kein_ok
! cmpx #7 'kann jedes Jahr um 1 erhöht werden
! blo kein_ok
! mov dcfmin,MINUTE ' Übernahme Funkzeit -- Interne Uhr
! mov dcfmin,dcfmerk ' Zur Genauigkeit, Minute merken, wann letztmals Funkzeit empfangen wurde
! mov dcfhour,HOUR '
! mov dcfday,DAY
! mov dcfmonth,MONTH
! mov dcfyear,YEAR
! clr SECOND
! clr TIMER
! bset dcfset,dcfstat
#kein_ok
! clr dcfzaehler ' Zaehler fuer die DCF-Impulse und den
! clr dcfmin ' temporaeren Speicher zur Berechnung der
! clr dcfhour
! clr dcfday ' DCF-Uhrzeit loeschen. Ausserdem das Flag
! clr dcfmonth
! clr dcfyear
! clr dcfimpuls
! bset dcfok,dcfstat 'dcfok muß ja irgendwann gesetzt werden
! bra iiar_tofl_exit
#dcftable 'Tabelle fuer die Konvertierung BCD -> binaer
! db 1,2,4,8,10,20,40,80
END PROCEDURE
#iiar_tofl_ende 'hier hinter beginnt die naechste IIAR
'(sofern vorhanden)
' ---------------------------------------------------------------------------