TITLE "night.asm: DCF77 Night LED Display @3.6864MHz" LIST p=16C71 ERRORLEVEL 0, -302 ; Children night display: ; at 20:00 the moon(8) and 11 stars(0..7,9..11) are turned on. ; at 21:00 one star is turned off, at 22:00 the next star etc. ; at 07:00 the last star is switched off and at 08:00 the moon. ; while night display is not active the hour is shown. ; if A1 low everything begins an hour earlier. ; if A2 low the hour is displayed during the day. ; if A3 low the hour is displayed day & night. ; DCF in = A0; if dcfsyn = 0, dcf pulse on led 0 and 6. ; outputs B7/B3 each over R=220 to 6 antiparallel led pairs ; at inputs B6..B4/B2..B0 which are activated to drive leds. ; B7/B3 high for leds 0,2,4,6,8,10, low for leds 1,3,5,7,9,11. RTCC EQU 1 PC EQU 2 STATUS EQU 3 FSR EQU 4 PORTA EQU 5 PORTB EQU 6 ADCON0 EQU H'08' ADRES EQU H'09' PCLATH EQU H'0A' INTCON EQU H'0B' _OPTION EQU H'81' ; OPTION register in page 1 _TRISA EQU H'85' ; TRISA in page 1 _TRISB EQU H'86' ; TRISB in page 1 _ADCON1 EQU H'88' ; ADCON1 in page 1 C EQU 0 DC EQU 1 Z EQU 2 P EQU 5 tmp0 EQU H'0C' ; temporary storage tmp1 EQU H'0D' sav0 EQU H'0E' ; storage for interrupt handler sav1 EQU H'0F' csecs EQU H'10' ; seconds/100 (bcd) secs EQU H'11' ; seconds (bcd) mins EQU H'12' ; minutes (bcd) hours EQU H'13' ; hours (bcd) day EQU H'14' ; dcf day of week (bcd) date EQU H'15' ; dcf day of month (bcd) month EQU H'16' ; dcf month (bcd) year EQU H'17' ; dcf year within century (bcd) pwidth EQU H'18' ; dcf pulse width pdist EQU H'19' ; dcf pulse dist pcount EQU H'1A' ; dcf pulse count secchg EQU H'1B' ; flag from toc1 interrupt prty EQU H'1C' ; flag for parity check dcfsyn EQU H'1D' ; hours since dcf time ok rtovls EQU H'1E' ; rtc overflow counter (9 = 10ms) dcfcod EQU H'20' ; dcf code: last 48 bits ledmux EQU H'26' ; current led mux 0..5 (time multiplexed) ledsel EQU H'27' ; ledselector for mux display ORG H'00' ; reset NOP ; NOP ; GOTO start ; GOTO start ; ORG H'04' ; interrupt GOTO intserv ; dcf77 time GOTO intaltc ; 50Hz AC time (100Hz DC) ORG H'08' start CALL init ; main CALL rswdog ; CALL dsptim ; CALL updtim ; CALL rswdog ; CALL chkpls ; CALL chkdcf ; GOTO main ORG H'20' init MOVLW B'11111111' ; to PORTA MOVWF PORTA MOVLW B'11111111' ; to display MOVWF PORTB BSF STATUS,P ; set page 1 MOVLW B'00001111' ; A3..A0 inputs MOVWF _TRISA MOVLW B'01110111' ; B7,B3 outputs MOVWF _TRISB MOVLW B'11010001' ; rtc prescaler 1:4 MOVWF _OPTION BCF STATUS,P ; set page 0 CLRF RTCC ; reset rtcc CLRF INTCON ; clear all flags and enable-bits BSF INTCON,5 ; enable rtc interrupt BSF INTCON,7 ; enable interrupts MOVLW H'0C' ; reset vars MOVWF FSR initvar CLRF 0 INCF FSR,F MOVLW H'30' XORWF FSR,W BTFSS STATUS,Z GOTO initvar DECF secchg,F ; force first display INCF rtovls,F ; start timer immediately MOVLW H'20' ; init time 20:00 MOVWF hours ; NOP ; BSF STATUS,P ; set page 1 MOVLW B'00000011' ; Port A digital MOVWF _ADCON1 ; BCF STATUS,P ; set page 0 RETLW 0 ; ORG H'50' intserv MOVWF sav0 ; save working register MOVF STATUS,W ; save status register MOVWF sav1 BCF INTCON,2 ; clear rtc overflow flag BCF STATUS,P ; set page 0 DECFSZ rtovls,F ; execute only after 9 counts GOTO intend MOVLW D'9' MOVWF rtovls INCF csecs,F ; count csecs MOVF csecs,W ; new second? XORLW D'100' BTFSC STATUS,Z CLRF csecs ; Y: reset csecs MOVF csecs,F ; csecs zero BTFSC STATUS,Z DECF secchg,F ; Y: flag new second BTFSC PORTA,0 ; DCF Signal low GOTO inthigh ; N: outside a pulse CLRF pdist ; Y: inside a pulse INCF pwidth,F GOTO intend inthigh MOVF pdist,F ; end of a pulse? BTFSC STATUS,Z BSF pwidth,7 ; flag: complete pulse INCF pdist,F ; missing pulse if pdist > 127 intend MOVF sav1,W ; restore status register MOVWF STATUS MOVF sav0,W ; restore working register BCF STATUS,Z BTFSC sav1,Z ; restore zero bit BSF STATUS,Z RETFIE updtim BTFSS secchg,7 ; update time if a new second RETLW 0 CLRF secchg INCF secs,F ; inc seconds MOVF secs,W ; test if 10 seconds ANDLW H'0F' XORLW H'0A' BTFSS STATUS,Z RETLW 0 ; no: done MOVLW H'F0' ; clear seconds ANDWF secs,F SWAPF secs,F ; inc 10-seconds INCF secs,F SWAPF secs,F SWAPF secs,W ; test if 6 10-seconds XORLW H'06' BTFSS STATUS,Z RETLW 0 ; no: done CLRF secs INCF mins,F ; inc minutes MOVF mins,W ; test if 10 minutes ANDLW H'0F' XORLW H'0A' BTFSS STATUS,Z RETLW 0 ; no: done MOVLW H'F0' ; clear minutes ANDWF mins,F SWAPF mins,F ; inc 10-minutes INCF mins,F SWAPF mins,F SWAPF mins,W ; test if 6 10-minutes XORLW H'06' BTFSS STATUS,Z RETLW 0 ; no: done MOVF dcfsyn,F ; dec dcfsyn until zero BTFSS STATUS,Z ; DECF dcfsyn,F ; CLRF mins INCF hours,F ; inc hours MOVF hours,W ; test if 10 hours ANDLW H'0F' XORLW H'0A' BTFSS STATUS,Z GOTO updt24 ; no: test if 24 hours MOVLW H'F0' ; clear hours ANDWF hours,F SWAPF hours,F ; inc 10-hours INCF hours,F SWAPF hours,F RETLW 0 ; done updt24 MOVF hours,W ; test if 24 hours XORLW H'24' BTFSC STATUS,Z CLRF hours RETLW 0 ; done chkpls BTFSS pwidth,7 ; check dcf pulse, save pulse in dcfcod RETLW 0 BCF pwidth,7 MOVF pwidth,W MOVWF tmp0 CLRF pwidth SUBLW D'5' ; error if pulse <= 50ms BTFSC STATUS,C GOTO chkerr MOVF tmp0,W SUBLW D'25' ; error if pulse > 250ms BTFSS STATUS,C GOTO chkerr MOVLW D'15' SUBWF tmp0,W ; carry now contains data bit savpls RRF dcfcod+5,F RRF dcfcod+4,F RRF dcfcod+3,F RRF dcfcod+2,F RRF dcfcod+1,F RRF dcfcod+0,F INCF pcount,F RETLW 0 chkerr CLRF pcount RETLW 0 ;;; check the dcf telegram and transfer to time if ok ; dcfcod+5 P3 Y Y Y Y Y Y Y 58..51 ; dcfcod+4 Y M M M M M D D 50..43 ; dcfcod+3 D d d d d d d P2 42..35 ; dcfcod+2 h h h h h h P1 m 34..27 ; dcfcod+1 m m m m m m 1 ls 26..19 ; dcfcod+0 sz wz ly r x x x x 18..11 chkdcf BTFSS pdist,7 RETLW 0 MOVLW D'44' SUBWF pcount,W ; error if < 44 pulses BTFSS STATUS,C GOTO chkerr CLRF prty BCF STATUS,C ; clear carry CALL savpls ; shift right dcfcod MOVF dcfcod+5,W CALL updprty MOVF dcfcod+4,W CALL updprty MOVF dcfcod+3,W CALL updprty MOVF prty,F ; P3 ok? BTFSS STATUS,Z GOTO chkerr CALL savpls ; shift right dcfcod MOVF dcfcod+2,W ANDLW H'7F' CALL updprty MOVF prty,F ; P2 ok? BTFSS STATUS,Z GOTO chkerr MOVF dcfcod+1,W CALL updprty MOVF prty,F ; P1 ok? BTFSS STATUS,Z GOTO chkerr BTFSS dcfcod+0,7 ; is start bit set? GOTO chkerr MOVF dcfcod+2,W ; dcf telegram ok ANDLW H'3F' MOVWF hours MOVF dcfcod+1,W ANDLW H'7F' MOVWF mins MOVLW D'168' ; dcfsyn ok for 168 hours MOVWF dcfsyn CLRF pcount CLRF secs MOVLW D'182' MOVWF csecs RETLW 0 updprty MOVWF tmp0 ; invert prty for each bit set in acca updprt1 BCF STATUS,C ; clear carry RRF tmp0,f ; lsb to carry BTFSC STATUS,C ; skip if carry set COMF prty,f ; else invert prty MOVF tmp0,f ; repeat until no more bits set BTFSS STATUS,Z GOTO updprt1 RETLW 0 ORG H'100' wdtrap GOTO wdtrap ; trap to force watch dog time out rswdog CLRWDT ; watch dog reset RETLW 0 intaltc MOVWF sav0 ; save working register MOVF STATUS,W ; save status register MOVWF sav1 BCF INTCON,2 ; clear rtc overflow flag BCF STATUS,P ; set page 0 MOVLW D'99' ; dcfsyn always ok MOVWF dcfsyn INCF rtovls,F ; count samples (9 = 10ms) MOVF ADRES,W ; get A/D result from DCF input BSF ADCON0,2 ; start new conversion SUBLW D'101' ; BTFSS STATUS,C ; W > 2V? GOTO xintend ; Y: do nothing MOVF rtovls,W ; N: pulse is low SUBLW D'6' ; BTFSC STATUS,C ; > 6 samples since last csec GOTO xintend ; N: do nothing CLRF rtovls ; Y: count another csec INCF csecs,F ; count csecs MOVF csecs,W ; new second? XORLW D'100' BTFSC STATUS,Z CLRF csecs ; Y: reset csecs MOVF csecs,F ; csecs zero BTFSC STATUS,Z DECF secchg,F ; Y: flag new second xintend MOVF sav1,W ; restore status register MOVWF STATUS MOVF sav0,W ; restore working register BCF STATUS,Z BTFSC sav1,Z ; restore zero bit BSF STATUS,Z RETFIE ORG H'130' dsptim MOVF dcfsyn,F ; if dcf status not ok: BTFSS STATUS,Z ; GOTO dspmux ; MOVLW B'10001000' ; even led MOVWF PORTB ; MOVF pdist,F ; not zero if outside a pulse MOVLW B'00000001' ; led 0 BTFSC STATUS,Z ; MOVLW B'00010000' ; led 6 XORLW B'01110111' ; BSF STATUS,P ; set page 1 MOVWF _TRISB ; BCF STATUS,P ; set page 0 RETLW 0 ; dspmux BTFSS PORTA,3 ; if A3 low display hours only GOTO dsphour ; MOVF hours,W ; hours to tmp0 (bcd to bin) ANDLW H'0F' ; MOVWF tmp0 ; MOVLW D'0' ; BTFSC hours,4 ; MOVLW D'10' ; BTFSC hours,5 ; MOVLW D'20' ; ADDWF tmp0,F ; BTFSS PORTA,1 ;; tmp0++ if A1 low INCF tmp0,F ;; MOVLW B'0000011' ; PCLATH to data range MOVWF PCLATH ; MOVF tmp0,W ; CALL getdat ; get led selection data MOVWF ledsel ; MOVLW H'E' ; no display if data is 0xE XORWF ledsel,W ; BTFSC STATUS,Z ; GOTO dspoff ; MOVLW H'F' ; hour display if data is 0xF XORWF ledsel,W ; BTFSC STATUS,Z ; GOTO dsphour ; INCF ledmux,F ; night display: next led mux MOVF ledmux,W ; XORLW D'6' ; BTFSC STATUS,Z ; CLRF ledmux ; CLRF tmp1 ; MOVF ledmux,W ; tmp0 = (ledmux + 3) mod 12 ADDLW D'3' ; MOVWF tmp0 ; BTFSS PORTA,1 ;; tmp0++ if A1 low INCF tmp0,F ;; MOVLW D'12' ; SUBWF tmp0,F ; BTFSS STATUS,C ; ADDWF tmp0,F ; MOVF ledsel,W ; tmp0 < ledsel? SUBWF tmp0,W ; BTFSC STATUS,C ; BSF tmp1,0 ; no: leds 0..5 on MOVLW D'6' ; tmp0 = (tmp0 + 6) mod 12 ADDWF tmp0,F ; MOVLW D'12' ; SUBWF tmp0,F ; BTFSS STATUS,C ; ADDWF tmp0,F ; MOVF ledsel,W ; tmp0 < ledsel? SUBWF tmp0,W ; BTFSC STATUS,C ; BSF tmp1,4 ; no: leds 6..11 on MOVF ledmux,W ; BCF STATUS,C ; ANDLW B'11111110' ; if ledmux >= 2 BTFSS STATUS,Z ; RLF tmp1,F ; rotate mask for leds 2/3 and 8/9 ANDLW B'11111100' ; if ledmux >= 4 BTFSS STATUS,Z ; RLF tmp1,F ; rotate mask for leds 4/5 and 10/11 MOVLW B'01110111' ; finish tmp1 XORWF tmp1,F ; MOVLW B'01110111' ; turn off leds BSF STATUS,P ; set page 1 MOVWF _TRISB ; BCF STATUS,P ; set page 0 MOVLW B'10001000' ; portb even leds BTFSC ledmux,0 ; skip if ledmux even XORLW B'11111111' ; portb odd leds MOVWF PORTB ; MOVF tmp1,W ; appropriate leds for 1/6 of time BSF STATUS,P ; set page 1 MOVWF _TRISB ; BCF STATUS,P ; set page 0 RETLW 0 ; dsphour BTFSS PORTA,2 ; if A2 low skip hour display GOTO dspoff ; MOVF hours,W ; hours to tmp0 (bcd to bin) ANDLW H'0F' ; MOVWF tmp0 ; MOVLW D'0' ; BTFSC hours,4 ; MOVLW D'10' ; BTFSC hours,5 ; MOVLW D'20' ; ADDWF tmp0,F ; MOVLW D'12' ; tmp0 = hours mod 12 SUBWF tmp0,F ; BTFSS STATUS,C ; ADDWF tmp0,F ; BCF STATUS,C ; W = tmp0/2 RRF tmp0,W ; MOVWF tmp0 ; INCF tmp0,F ; CLRF tmp1 ; BSF STATUS,C ; ledr RLF tmp1,F ; rotate carry in tmp1 DECFSZ tmp0,F ; for tmp0 counts GOTO ledr ; MOVLW B'11111000' ; if bit was shift more than 3: ANDWF tmp1,W ; BTFSS STATUS,Z ; RLF tmp1,F ; shift one more time MOVLW B'01110111' ; finish tmp1 XORWF tmp1,F ; MOVLW B'10001000' ; portb even leds BTFSC hours,0 ; skip if hours even XORLW B'11111111' ; portb odd leds MOVWF PORTB ; MOVF tmp1,W ; appropriate led BSF STATUS,P ; set page 1 MOVWF _TRISB ; BCF STATUS,P ; set page 0 RETLW 0 ; MOVLW B'11111111' ; BTFSS hours,0 ; skip if hours odd XORWF tmp1,F ; update tmp1 MOVF tmp1,W ; turn on appropriate led MOVWF PORTB ; RETLW 0 ; dspoff MOVLW B'11111111' ; all leds off MOVWF PORTB ; MOVLW B'01110111' ; led selects tristated BSF STATUS,P ; set page 1 MOVWF _TRISB ; BCF STATUS,P ; set page 0 RETLW 0 ; ORG H'200' debug NOP ; NOP ; NOP ; NOP ; NOP ; NOP ; NOP ; NOP ; NOP ; NOP ; NOP ; NOP ; NOP ; NOP ; NOP ; NOP ; NOP ; ORG H'300' getdat ADDWF PC,F ; return data from table below RETLW H'4' ; 0 RETLW H'5' ; 1 RETLW H'6' ; 2 RETLW H'7' ; 3 RETLW H'8' ; 4 RETLW H'9' ; 5 RETLW H'A' ; 6 RETLW H'B' ; 7 RETLW H'E' ; 8 RETLW H'F' ; 9 RETLW H'F' ; 10 RETLW H'F' ; 11 RETLW H'F' ; 12 RETLW H'F' ; 13 RETLW H'F' ; 14 RETLW H'F' ; 15 RETLW H'F' ; 16 RETLW H'F' ; 17 RETLW H'F' ; 18 RETLW H'F' ; 19 RETLW H'0' ; 20 RETLW H'1' ; 21 RETLW H'2' ; 22 RETLW H'3' ; 23 RETLW H'4' ; 0 END