;******************************************************************** ;* * ;* Title : DCF * ;* Version : 1.4nix 12. Mai 2007 * ;* Author : Uwe Nagel, Parkstr. 46, D-68766 Hockenheim * ;* Assembler : MPASM (c) Microchip * ;* * ;******************************************************************** ; 0.1 21.05.02 erster Entwurf ; 0.2 30.05.02 erste lauffähige Version mit LCD ; 0.3 29.09.02 Kosmetik am Quellcode und an der Homepage, keine ; Änderung der Funktion ; 0.4 01.04.03 Antennen Symbol auf Wunsch von Christof Rueß ; 0.5 24.07.03 Zeitimpule auf Wunsch von CIOVALLADOLID ; wahlweise zwei feste Schaltausgänge auf Wunsch von Herrn Eichler ; 0.6 30.08.03 Ports löschen beim Einschalten ; PTT-Signal auf PORTA,4 (CIOVALLADOLID) ; 0.7 26.09.03 Filter gegen unsichere Flanken ; 0.8 01.10.03 diverse Optimierungen ; 0.9 15.08.04 anderes Ausgabeformat auf Wunsch von Peter Hilkmann (PE1DCD) ; 0.9a 02.11.04 Fehler bei Monatsausgabe beseitigt, neue Sprachen ; 0.a 06.12.04 Spezialversion mit 16F874 und 4MHz (hier nicht integriert) ; 0.b 03.04.05 serielle Ausgabe der Zeit, Sprachen ausgelagert ; 0.ba 23.10.05 neue Sprache 'swedish', Dank an Volker Lange-Janson ; 0.bb 09.02.06 fehlendes 'RETURN' eingefügt, Dank an Peter Engels ; 0.bc 10.02.06 Antennensymbol auch bei PE1DCD ermöglichen ; Programm adaptiert auf 16F628 (Nachfolger 16F84) ; 0.bd 21.02.06 useTimer1 eingeführt ; 0.c 18.03.06 erster Versuch mit Nebenuhr ; 1.0 08.04.06 Hauptuhr mit Sommer-Winterzeit-Umstellung ; 1.1 19.06.06 andere Impulsausgabe für Mutteruhr (PULS500) ; 1.2 02.09.06 blinkendes Antennensymbol ; 1.3 28.09.06 Plausibilitätskontrolle von Puls- und Pausenzeiten ; 1.3a 09.11.06 Fehlerkorrektur MOVLF bei 16f628 raus ; 1.4 25.03.07 Ausgabe von Tag im Jahr und Kalenderwoche ; Anzeige der Zeit in UTC ; 1.4a 02.05.07 Fehlerkorrektur: Tabellen für Jahrestag ; 1.4nix 13.05.07 Version für 6 Nixies mit BCD-Dekoder ;******************************************************************** #define TITEL "-DCF77-Uhr 1.4nix-" ;--- useTimer1 definieren, um Timer1 in 16F628 oder anderen neuen PICs ; zu verwenden. Dann muss ein 4MHz Quarz angeschlossen werden, nicht 3,2768MHz ; geht nicht mit PIC16F84! #define useTimer1 ;--- MUTTERUHR definieren um auf PORTA,3 einen Minutentakt zum Ansteuern ; von Nebenuhren auszugeben. PORTA,2 muss dazu auf 1 liegen. Zieht man PORTA,2 ; auf Masse, dann wird jede Sekunde ein Takt zum Stellen ausgegeben. #define MUTTERUHR ; wenn PULS500 definiert ist, wird jede Minute ein 500ms-Puls ausgegeben ; sonst wechselt PORTA,3 jede Minute den Pegel ;#define PULS500 ;--- KALENDERWOCHE definieren, um Tag im Jahr und Kalenderwoche zu ermitteln ; und während der Sekunden x8 und x9 in der zweiten Zeile anzuzeigen ; funktioniert wegen Speichermangel nur mit 16F628 ! ; nicht im Anzeigeformat PE1DCD implementiert ;#define KALENDERWOCHE title TITEL #ifdef __16F877 ; configs für 16F877 __config _XT_OSC & _WDT_ON & _PWRTE_ON & _CP_OFF & _LVP_OFF include RAM_START equ 0x20 #endif errorlevel -302 ; keine bank select message radix dec ;****** Variablen *************************************************** cblock RAM_START save_W ; w und save_STATUS ; STATUS während interrupt save_FSR ; FSR während Interrupt flag ; diverse Bits temp tmp1 tmp2 zweihund ; 200 Interupts sind 1 Sek puls_zeit ; misst Pulsdauer pause_zeit ; misst Pausendauer ;*** diese Zeit wird angezeigt dispSec, dispMin, dispStd, dispTag, dispMon, dispJahr, dispWtag, dispStat ;*** hier kommt die gerade empfangene Zeit rein dcfMin, dcfStd, dcfTag, dcfMon, dcfJahr, dcfWtag, dcfStat ;*** zum Vergleich, die letzte Minute dcf1Min, dcf1Std, dcf1Tag, dcf1Mon, dcf1Jahr, dcf1Wtag, dcf1Stat filter, altsec, flag2 endc #ifdef MUTTERUHR cblock pulshi, pulslo, pulsdauer endc #endif cblock utcDay, utcMonth, utcYear, tmp3 endc #ifdef KALENDERWOCHE cblock a0,a1,a2,b0,b1,b2,c0,c1 year,month,day tag0,tag1,calenderweek,weekday endc cnt equ tmp2 #endif ;****** bits in flag ************************************************ #define neusync flag,0 #define alt flag,1 #define sekunde flag,2 #define syncd flag,3 #define dcfsign flag,4 #define ms5 flag,5 #define comm flag,7 #define SommerWinter flag2,0 #define WinterSommer flag2,1 #define Pulsstart flag2,2 #define Pulsende flag2,3 lc_data equ tmp1 ; local in lcd_write lcd_init_cnt equ tmp2 ; only used in lcd_init temp_lcd equ tmp2 ; temporary in write_lcd_char, clear_display temp_byte equ tmp2 ; temporary in write_byte ;****** Konstanten ************************************************** #define TICKS 200 ; für 4MHz bei Timer1 ;******************************************************************** ;* Programmcode ... ;******************************************************************** org 0 goto main ;******************************************************************** ;* interrupt : ;******************************************************************** org 4 goto interrupt ;******************************************************************** ;* Tabellen in Page 0: ;******************************************************************** #ifdef KALENDERWOCHE getLo: movf temp,w addwf PCL,f retlw low 58 retlw low 89 retlw low 119 retlw low 150 retlw low 180 retlw low 211 retlw low 242 retlw low 272 retlw low 303 retlw low 333 retlw low 364 retlw low 395 getHi: movf temp,w addwf PCL,f retlw high 58 retlw high 89 retlw high 119 retlw high 150 retlw high 180 retlw high 211 retlw high 242 retlw high 272 retlw high 303 retlw high 333 retlw high 364 retlw high 395 #endif ;******************************************************************** ;* interrupt service Routine, ;* hinter Tabellen, damit die alle in Page 0 liegen können: ;******************************************************************** interrupt movwf save_W ; W und STATUS sichern swapf STATUS,w movwf save_STATUS movf FSR,w movwf save_FSR bcf STATUS,RP0 ; Wähle register page 0 btfss PIR1,CCP1IF goto no_timer_isr ; ist es timer interrupt ? #ifdef PULS500 movf pulsdauer,w btfsc STATUS,Z bcf PORTE,0 btfss STATUS,Z decf pulsdauer,f #endif ; increment filter if port=1 and filter if less than 8 ; decrement filter if port=0 and filter if greater than 0 btfss PORTA,0 goto port_is_0 port_is_1 incf filter,f btfsc filter,3 ; <8 ? port_is_0 decf filter,f btfsc filter,7 clrf filter ; set dcfsign if filter becomes 6 ; clear dcfsign if filter becomes 1 ; 0 1 2 3 4 5 6 7 6 5 4 3 2 1 0 ; 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 movf filter,w xorlw 6 btfsc STATUS,Z bsf dcfsign xorlw 6^1 btfsc STATUS,Z bcf dcfsign ;** teste auf steigende Flanke btfss alt btfss dcfsign goto NoPos ;** Pause kürzer als 750ms ist ungültig movf pause_zeit,w addlw -150 ; >150*5ms=750ms btfss STATUS,C goto NoPos ;** positive Flanke... bsf alt bsf Pulsstart clrf puls_zeit btfss neusync ; erster Impuls nach 59. Sekunde? goto NoNeg clrf dispSec movf dcfMin,w movwf dispMin movf dcfStd,w movwf dispStd movf dcfTag,w movwf dispTag movf dcfWtag,w movwf dispWtag movf dcfMon,w movwf dispMon movf dcfJahr,w movwf dispJahr movf dcfStat,w movwf dispStat bcf neusync movlw TICKS ; 200 * 5ms movwf zweihund goto end_timer_isr NoPos: ;** teste auf fallende Flanke btfsc alt btfsc dcfsign goto NoNeg ;** Puls kürzer als 80ms ist ungültig movf puls_zeit,w addlw -16 ; >16*5ms=80ms btfss STATUS,C goto NoNeg ;** fallende Flanke bcf alt clrf pause_zeit bsf Pulsende ;** teste ob 0 (<150ms) oder lang (>150ms) movf puls_zeit,w addlw -30 ; >30*5ms call shiftDCF NoNeg: incfsz pause_zeit,w ; bis max 255 zählen damit incf pause_zeit,f ; bei längeren Pausen nicht wieder ; bei 0 angefangen wird decfsz zweihund,f goto TimeOut ;** Sekunde abgelaufen movlw TICKS movwf zweihund call tick TimeOut: incfsz puls_zeit,f ; 1,28 s keine steigende Flanke ? goto end_timer_isr call sec59 ; überprüfe empfangene Zeit end_timer_isr bcf sekunde movf dispSec,w xorwf altsec,w ; hat sich die Sekunde geändert ? skpz bsf sekunde xorwf altsec,f ; aktuelle Sekunde merken bcf PIR1,CCP1IF ; lösche timer interrupt flag no_timer_isr movf save_FSR,w movwf FSR swapf save_STATUS,w movwf STATUS swapf save_W,f swapf save_W,w retfie ;******************************************************************** ;* Hauptprogramm : ;******************************************************************** main ; goto test clrf TMR1L clrf TMR1H movlw 0x31 movwf T1CON ; prescaler 8, timer 1 enabled movlw 0x0b movwf CCP1CON ; compare mode, trigger special event movlw low 624 movwf CCPR1L ; Fosz/4/(prescaler8)/(200Hz)-1 movlw high 624 movwf CCPR1H bsf STATUS,RP0 ; select Register-Page 1 movlw 0x04 movwf PIE1 ; enable Compare Interrupt movlw 0x03 ; TMR0 internal clock/16 = 200Hz @ 3,2768MHz Quarz movwf OPTION_REG movlw 0x07 movwf ADCON1 ; Port A digital movlw 0x0f ; PORTA0 DCF-Input; 1=Mutteruhr stellen movwf TRISA ; clrf TRISB ; Stunden BCD clrf TRISC ; Minuten-Einer und Sekunden-Einer clrf TRISD ; Minuten-Zehner und Sekunden-Zehner clrf TRISE ; Mutteruhr bcf STATUS,RP0 ; select Register-Page 0 clrf PORTE clrf PCLATH clrf flag clrf puls_zeit movlw TICKS movwf zweihund clrf dispSec clrf dispMin clrf dispStd clrf dispWtag clrf dispJahr movlw 1 movwf dispMon movwf dispTag clrf flag2 #ifdef MUTTERUHR clrf pulslo clrf pulshi #endif #ifdef UTC movlw 1 movwf utcDay movwf utcMonth movwf utcWday clrf utcYear #endif movlw 0xc0 ; Enable Peripherial Int movwf INTCON ;------ Display initialisieren und Einschaltmeldung ------ call display_init ; init LC-Display movlw 3 ; etwa 3 Sekunden anzeigen movwf temp hallo: clrwdt btfss sekunde goto hallo bcf sekunde decfsz temp,f goto hallo mainloop: clrwdt btfsc Pulsstart call AntenneEin btfsc Pulsende call AntenneAus btfss sekunde goto mainloop ;--- hier kommt das Programm jede Sekunde genau einmal hin bcf sekunde call showtime call special #ifdef KALENDERWOCHE call calcWeek #endif goto mainloop ;******************************************************************** ;******************************************************************** ;* Spezialprogramme für verschiedene Zwecke ;******************************************************************** special #ifdef MUTTERUHR btfss WinterSommer goto Mutter1 bcf WinterSommer movlw low 62 movwf pulslo movlw high 62 movwf pulshi Mutter1 btfss SommerWinter goto Mutter2 bcf SommerWinter movlw low 672 movwf pulslo movlw high 672 movwf pulshi Mutter2 movf pulslo,w iorwf pulshi,w skpnz goto Mutter3 decf pulslo,f incf pulslo,w skpnz decf pulshi,f goto stellen Mutter3 btfss PORTA,1 goto stellen movf dispSec,w skpz return stellen movlw 0x01 xorwf PORTE,f ;PORTE0 umdrehen #ifdef PULS500 movlw 100 movwf pulsdauer #endif return #else return #endif ;******************************************************************** ; solange nicht synchronisiert ist geht PORTE,1 aus, wenn ein DCF-Impuls ; anliegt. Ist synchronisiert geht er an! AntenneEin: bcf Pulsstart btfss syncd bcf PORTE,1 btfsc syncd bsf PORTE,1 return AntenneAus: bcf Pulsende btfss syncd bsf PORTE,1 btfsc syncd bcf PORTE,1 return ;******************************************************************** ;****** neue Sekunde anzeigen *************************************** ;******************************************************************** showtime: movlw 0x99 btfsc dispStat,4 movlw 0x98 movwf tmp1 movf dispStd,w movwf temp call bcd_add btfss temp,7 ; 98 oder 99? goto Utc1 movlw 0x24 ; einen Tag zurück movwf tmp1 call bcd_add goto Utc2 Utc1 movf dispJahr,w movwf utcYear movf dispMon,w movwf utcMonth movf dispTag,w movwf utcDay Utc2 movf temp,w btfsc PORTA,2 ; PORTA,2=0 : UTC 1:Lokalzeit movf dispStd,w btfss PORTA,3 ; PORTA,3=0 : Datum 1:Zeit movf dispTag,w movwf tmp1 ; tmp1 enthält Tag movf dispMon,w btfss PORTA,2 ; PORTA,2=0 : UTC 1:Lokalzeit movf utcMonth,w btfsc PORTA,3 ; PORTA,3=0 : Datum 1:Zeit movf dispMin,w movwf tmp2 movf dispJahr,w btfss PORTA,2 ; PORTA,2=0 : UTC 1:Lokalzeit movf utcYear,w btfsc PORTA,3 ; PORTA,3=0 : Datum 1:Zeit movf dispSec,w movwf tmp3 ; PORTE,2 wird gesetzt, wenn Tag/Stunden-Zehner>0 ; ermöglicht die Unterdrückung dieser Stelle movf tmp1,w andlw 0xf0 btfss STATUS,Z bsf PORTE,2 btfsc STATUS,Z bcf PORTE,2 movf tmp3,w ; Nibble werden getauscht wegen andlw 0x0f ; Pinanordnung am Prozessor movwf temp swapf tmp2,w andlw 0xf0 iorwf temp,w movwf PORTC swapf tmp3,w andlw 0x0f movwf temp movf tmp2,w andlw 0xf0 iorwf temp,w movwf PORTD movf tmp1,w movwf PORTB return ;******************************************************************** display_init: clrf PORTA movlw 0x01 movwf PORTB movlw 0x35 movwf PORTC movlw 0x24 movwf PORTD return ;******************************************************************** shiftDCF: rrf dcfJahr,f rrf dcfMon,f rrf dcfTag,f rrf dcfStd,f rrf dcfMin,f rrf dcfStat,f return ;******************************************************************** ;* in der 59. Sekunde aufgerufen, sortiert erst mal den Empfangspuffer, ;* um die Zeitwerte in einzelnen Bytes zu erhalten ;******************************************************************** ;55555555 54444444 44433333 33333222 22222221 11111111 ;87654321 09876543 21098765 43210987 65432109 87654321 Sekunde ;PJJJJJJJ JMMMMMWW WTTTTTTP SSSSSSPm mmmmmm1a ooaR0000 DCF-Bits ; ;JJJJJJJJ 000MMMMM 00TTTTTT 00SSSSSS 0mmmmmmm 1aooaR00 00000WWW ; Jahr Monat Tag Stunde Minute Status Wtag ;******************************************************************** sec59: rlf dcfMon,w rlf dcfJahr,f ; =Jahr rlf dcfTag,w rlf dcfMon,w andlw 0x07 movwf dcfWtag ; =Wochentag Mo..So = 1..7 rrf dcfMon,f rrf dcfMon,f movlw 0x1f andwf dcfMon,f ; =Monat rrf dcfTag,f movlw 0x3f andwf dcfTag,f ; =Tag rrf dcfStd,w rrf dcfMin,f rrf dcfStat,f rrf dcfMin,f rrf dcfStat,f ; =Status movlw 0x7f andwf dcfMin,f ; =Minute rrf dcfStd,f rrf dcfStd,f movlw 0x3f andwf dcfStd,f ; =Stunde movlw dcf1Min movwf FSR call tickMin ; letzte Zeit weiterrechnen movf dcfMin,w ; und mit gerade empfangener xorwf dcf1Min,w ; Zeit vergleichen skpz goto ungleich movf dcfStd,w xorwf dcf1Std,w skpz goto ungleich movf dcfTag,w xorwf dcf1Tag,w skpz goto ungleich movf dcfMon,w xorwf dcf1Mon,w skpz goto ungleich movf dcfJahr,w xorwf dcf1Jahr,w skpz goto ungleich ;*** zwei aufeinanderfolgende Zeiten haben zueinander gepasst ;*** die aktuelle Zeit bei nächster Sekunde ins Display kopieren bsf syncd bsf neusync return ungleich: ;*** zwei aufeinanderfolgende Zeiten haben nicht eine Minute unterschied ;*** merken wir die gerade empfangene Zeit für die nächste Minute movf dcfMin,w movwf dcf1Min movf dcfStd,w movwf dcf1Std movf dcfTag,w movwf dcf1Tag movf dcfMon,w movwf dcf1Mon movf dcfJahr,w movwf dcf1Jahr movf dcfStat,w movwf dcf1Stat bcf syncd return ;******************************************************************** ;* hier wird eine Zeit um eine Sekunde weitergezählt. ;******************************************************************** tick: movlw dispSec movwf FSR call IncDec ; Sekunde movlw 0x60 xorwf INDF,w skpz return clrf INDF ; Sekunde=0 incf FSR,f ; ->Minute ;* hier wird eine Zeit um eine Minute weitergezählt. tickMin: call IncDec ; Minute movlw 0x60 xorwf INDF,w skpz return clrf INDF ; Minute=0 incf FSR,f ; -> Stunde call IncDec ; Stunde movf FSR,w ; Zeiger auf Stunde sichern movwf tmp2 movlw 4 addwf FSR,f ; -> WTag movf INDF,w xorlw 7 ; Sonntag? skpz goto keinWechsel ; es ist ein Sonntag decf FSR,f ; ->Jahr decf FSR,f ; ->Monat decf FSR,f ; ->Tag movf INDF,w addlw -0x25 btfss STATUS,C goto keinWechsel ; Tag ist kleiner 25 ; es ist mindestens der 25. incf FSR,f ; -> Monat movf INDF,w xorlw 0x03 ; März? skpz goto keinMaerz ; kein März ; es ist ein Sonntag >=25. im März decf FSR,f ; -> Tag decf FSR,f ; -> Stunde movf INDF,w xorlw 0x02 skpz goto keinWechsel ; es ist nicht 2Uhr ;es ist 2 Uhr --> es wird Sommer incf INDF,f ; Uhr eine Stunde vorstellen bsf WinterSommer movlw 5 addwf FSR,f ; -> Status movlw 0x30 xorwf INDF,f ; Status umstellen goto fertig keinMaerz: xorlw 0x03^0x10 ; Oktober? skpz goto keinWechsel ; es ist ein Sonntag >=25. im Oktober decf FSR,f ; -> Tag decf FSR,f ; -> Stunde movf INDF,w xorlw 0x03 skpz goto keinWechsel ; es ist nicht 3 Uhr ; es ist ein Sonntag >=25. im Oktober, 3 Uhr movlw 5 addwf FSR,f ; -> Status movf INDF,w andlw 0x10 ; ist noch Sommerzeit? skpnz goto keinWechsel movlw 0x30 xorwf INDF,f ; Status umstellen movf tmp2,w movwf FSR ; -> Stunde decf INDF,f ; Uhr eine Stunde zurück bsf SommerWinter goto fertig keinWechsel: bcf SommerWinter bcf WinterSommer fertig: movf tmp2,w movwf FSR ; Zeiger wieder auf Stunde movlw 0x24 ; Stunde==24? xorwf INDF,w skpz return clrf INDF ; Stunde=0 incf FSR,f tickTag: call IncDec ; Tag incf FSR,f ; ->Monat incf FSR,f ; ->Jahr incf FSR,f ; ->WTag incf INDF,f movlw 1 btfsc INDF,3 ; WTag==8? movwf INDF decf FSR,f ; ->Jahr decf FSR,f ; ->Monat movlw 8 subwf INDF,w movf INDF,w skpnc addlw -7 andlw 0x01 addlw 0x31 movwf temp movf INDF,w xorlw 2 ; Februar ? skpz goto nofeb movlw 0x30 movwf temp incf FSR,f ; ->Jahr movf INDF,w ; Jahr decf FSR,f ; ->Monat decf FSR,f ; -> Tag ; 00 04 08 12 16 sind Schaltjahre, danach wiederholt es sich andlw 0x13 skpnz goto leapyear xorlw 0x12 skpnz goto leapyear movlw 0x29 movwf temp leapyear: nofeb: movf temp,w xorwf INDF,w skpz return movlw 1 movwf INDF ; Tag=1 incf FSR,f ; -> Monat call IncDec movf INDF,w xorlw 0x13 skpz return movlw 1 movwf INDF ; Monat=1 incf FSR,f ; -> Jahr ; call IncDec ; return IncDec: incf INDF,f ; Zähler erhöhen movlw 6 addwf INDF,w ; +6 (Dez.-korrektur) --> Ergebnis vorerst in w btfsc STATUS,DC ; Überlauf im Low-Nibble? movwf INDF ; ja, Korr. Ergebn. zurück return ;******************************************************************** #ifdef KALENDERWOCHE ;******************************************************************** ; addiert 24 Bit a2..a0 zu 24 bit b2..b1 ;******************************************************************** add24: movf a0,w addwf b0,f movf a1,w btfsc STATUS,C addlw 1 addwf b1,f movf a2,w btfsc STATUS,C addlw 1 addwf b2,f return ;******************************************************************** ; multipliziert 24 Bit a2..a0 mit 16 Bit c1.c0 ; Produkt in b2..b0 ;******************************************************************** mul: clrf b0 clrf b1 clrf b2 movlw 16 movwf cnt mulloop: btfsc c0,0 call add24 bcf STATUS,C rlf a0,f rlf a1,f rlf a2,f rrf c0,w rrf c1,f rrf c0,f decfsz cnt,f goto mulloop return ;******************************************************************** ; teilt 24 Bit b2..b0 durch 16 Bit a1.a0 ; Quotient in b2..b0, Rest in c1..c0 ;******************************************************************** div2416 clrf c1 clrf c0 movlw 25 movwf cnt divloop movf a0,w subwf c0,w movwf tmp1 movf a1,w btfss STATUS,C incf a1,w subwf c1,w btfss STATUS,C goto nosub movwf c1 movf tmp1,w movwf c0 nosub rlf b0,f rlf b1,f rlf b2,f rlf c0,f rlf c1,f decfsz cnt,f goto divloop rrf c1,f rrf c0,f return ;******************************************************************** ; wandelt ein BCD-Byte nach binär W->W ;******************************************************************** bcd2bin8: movwf tmp1 swapf tmp1,w andlw 0x0f movwf temp addwf temp,f ;*2 no Carry! rlf temp,f ;*4 addwf temp,f ;*5 rlf temp,f ;*10 movf tmp1,w andlw 0x0f addwf temp,w ;w=binärwert der BCD-Zahl a return ;******************************************************************** ; Binary To BCD Conversion Routine ; Converts ACCb to decimal BCD. Does not change ACCb. ; Modified version. The original was found in ; MICROCHIP 'Embedded Controll Handbook' 1993 ;******************************************************************** bin2bcd: bcf STATUS,C ; clear the carry bit movlw 16 movwf cnt ; maximal 16 Bit clrf a0 ; in 6 BCD-Stellen wandeln clrf a1 clrf a2 loopBCD rlf b1,w ; MSB -> Carry rlf a0,f rlf a1,f rlf a2,f rlf b1,w ; rotate ACCb rlf b0,f rlf b1,f decfsz cnt,f goto adjDEC return adjDEC movlw a0 movwf FSR call adjBCD incf FSR,f call adjBCD incf FSR,f call adjBCD goto loopBCD adjBCD movlw 3 addwf INDF,W movwf temp btfsc temp,3 ; test if result > 7 movwf INDF movlw 0x30 addwf INDF,W movwf temp btfsc temp,7 ; test if result > 7 movwf INDF ; save as MSD return ;******************************************************************** ; teilt b2..b0 durch 2 ;******************************************************************** div2: bcf STATUS,C rrf b2,f rrf b1,f rrf b0,f return ;******************************************************************** ; berechnet aus year, month, day ; die Tage seit dem 1.1.1970 ; für das julianische Datum muss man noch 2440588=0x253D8C addieren ;******************************************************************** days: movf year,w addlw 30 movwf a0 clrf a1 clrf a2 ; a=year+30 movf month,w ; 0x01..0x0c addlw -3 movwf temp btfss temp,7 ; negativ? goto goon movlw 12 addwf temp,f decf a0,f ; year-1 goon: movlw low 1461 ;0xb5 movwf c0 movlw high 1461 ;0x05 movwf c1 ; c=1461 call mul ; b=year*1461 movlw 2 movwf a0 clrf a1 clrf a2 call add24 ; b=year*1461+2 call div2 call div2 ; b=(year*1461+2)/4 call getLo movwf a0 call getHi movwf a1 clrf a2 call add24 ; b+=(month*153+2)/5+58 movf day,w movwf a0 clrf a1 clrf a2 call add24 ; b+=day return ;******************************************************************** ;test: ; movlw 0x07 ; movwf dispJahr ; movlw 0x12 ; movwf dispMon ; movlw 0x31 ; movwf dispTag calcWeek: #ifdef UTC movf utcYear,w call bcd2bin8 movwf year movf utcMonth,w call bcd2bin8 movwf month movf utcDay,w call bcd2bin8 movwf day #else movf dispJahr,w call bcd2bin8 movwf year movf dispMon,w call bcd2bin8 movwf month movf dispTag,w call bcd2bin8 movwf day #endif call days movlw 3 movwf a0 clrf a1 clrf a2 call add24 ; b=days+3 movf b0,w movwf tag0 movf b1,w movwf tag1 movlw 7 movwf a0 call div2416 ; b=(days+3)/7; c=(days+3)%7; incf c0,w ; w=Wochentag 1=Montag movwf weekday movf b0,w movwf calenderweek ; movlw 1 movwf month movlw 7 movwf day call days ; 1.Woche im Jahr movf b0,w subwf tag0,f movf b1,w btfss STATUS,C incf b1,w subwf tag1,f ; movf tag0,w addlw 4 movwf tag0 btfsc STATUS,C incf tag1,f ; Tag im Jahr movlw 7 movwf a0 clrf a1 clrf a2 call div2416 movf b0,w subwf calenderweek,f incf calenderweek,f ; Kalenderwoche roh movf calenderweek,w xorlw 53 btfss STATUS,Z goto not53 #ifdef UTC movlw utcDay #else movlw dispTag #endif call bcd2bin8 movwf temp movf weekday,w subwf temp,w addlw -28 ; >=28 btfss STATUS,C goto not53 movlw 1 movwf calenderweek not53: return #endif ;****************************************** ;bcd_add ; ; Computes z = x + y ; where x,y,z are all 8-bit packed BCD numbers ; Exits with C=1 if x+y > 0x99 and with z=1 if ; x+y = 0x100. ; Note that z can be aliased to x or y so that ; it's possible to calculate x = x+y or y = x+y ; This routine forms the BCD two's complement of ; y and then uses the bcd_subtract routine to ; find the sum. e.g. z = x - (-y) = x + y ; ; 10 cycles x equ temp y equ tmp1 z equ temp bcd_add comf y, w ;W = ~y addlw 0x9a+1 ;W = ~y + 0x9a +1 ;W = (~y + 1) + 0x9a ;W = 0x9a - y subwf x, w ;W = x - (0x9a - y) ;W = x + y - 0x9a ;W = x + y + 0x66 rlf z, f ;Get the carry skpdc ;if lsn of x + lsn of y < 10 (dec) addlw -0x06 ; then remove the extra 6 added above btfss z,0 ;Similarly for the msn addlw -0x60 rrf z, f ;restore the carry movwf z return ;******************************************************************** end