title "3x5dcf.asm" ; I've never seen a clock with 3x5 displays. This should put that right. HH:MM:SS format only. ; ; 190917 v0.1 Start project ; 190918 v0.2 Rewritten for STP16CP05 chip and three extra data lines to save another device ; 190918 v0.3 Actual implementation of STP16CP05 drive ; 190918 v0.4 Lookup table numbers ; 190922 v0.5 DCF input and decode ; 190922 v0.6 Parity and other error tests ; 190925 v1.0 Release ; ; GPL Copyleft 2019 pic@polonai.se LIST P=16F628A, F=INHX8M #include <p16f628a.inc> __CONFIG _INTRC_OSC_NOCLKOUT & _WDT_OFF & _LVP_OFF & _CP_OFF & _BODEN_OFF ; Equates RESET_V EQU 0x00 ; Address of RESET Vector OSC_FREQ EQU D'4000000' ; Oscillator Frequency is 4 MHz ; Registers TEMP EQU 0x20 ; TEMP register THOURS EQU 0x21 ; 10 Hours value HOURS EQU 0x22 ; Hours value TMINS EQU 0x23 ; 10 Minutes value MINS EQU 0x24 ; Minutes value TSECS EQU 0x25 ; 10 Seconds value HSECS EQU 0x26 ; Half Seconds value (because of TIMER1) DIGIT1 EQU 0x31 ; 10 hr digit DIGIT2 EQU 0x32 ; hour digit DIGIT3 EQU 0x33 ; 10 min digit DIGIT4 EQU 0x34 ; minute digit DIGIT5 EQU 0x35 ; 10 sec digit DIGIT6 EQU 0x36 ; seconds digit DCFHIGH EQU 0x37 ; Counter for the length of received DCF high pulses, used for decoding DCFLOW EQU 0x38 ; Counter for the length of received DCF space between pulses, used for decoding and syncing MINBUF EQU 0x39 ; Received DCF Data Minutes Buffer HRBUF EQU 0x3A ; Received DCF Data Hours Buffer DCFSEC EQU 0x3D ; Counter for DCF data FLAGS EQU 0x7F ; Various flags #define ROW0 PORTB,0 ; Row 0 #define ROW1 PORTB,1 ; Row 1 #define ROW2 PORTB,2 ; Row 2 #define ROW3 PORTB,3 ; Row 3 #define ROW4 PORTB,4 ; Row 4 #define COL10 PORTB,5 ; Column 0x10 #define COL11 PORTB,6 ; Column 0x11 #define COL12 PORTB,7 ; Colon (0x12) #define DCFIN PORTA,4 ; Input DCF signal, positive (mark = HIGH) #define SDI PORTA,6 ; Data line of STP16CP05 #define CLK PORTA,7 ; Clock for STP16CP05 #define LE PORTA,0 ; Latch Enable for STP16CP05 #define OE PORTA,1 ; Output Enable for STP16CP05 #define LED PORTA,2 ; Blue LED for DCF input activity #define TETS PORTA,3 ; Yellow LED for debugging #define SOLID FLAGS,0 ; colon pattern solid when DCF OK #define DCFOK FLAGS,1 ; when both hour and minute even parities are OK, needs good signal #define RXD FLAGS,2 ; Received DCF data bit org 0x00 GOTO START org 0x04 ; Interrupt handler ; NOTE: Everything of this program runs inside an interrupt handler so it's not necesssary ; to back up any critical registers (WREG, STATUS) when entering or exiting the handler. BTFSC INTCON,TMR0IF ; TMR0 interrupt GOTO DCFHandler BTFSC PIR1,TMR1IF ; Check to see if the interrupt was caused by a TIMER1 rollover Overflow) GOTO Timehandler ; half second timer BTFSC PIR1,TMR2IF ; Check to see if the interrupt was caused by a TIMER2 rollover (overflow) GOTO Multiplex ; 6 ms multiplex time for LED display RETFIE ; If none of the above is true, then just leave the interrupt handler DCFHandler BCF INTCON,TMR0IF ; Clear interrupt source MOVLW 0x44 ; Check DCFIN every 50 ms-ish (too many interrupts and too low internal clock of 4 MHz) MOVWF TMR0 BTFSC DCFIN ; Input high? GOTO DCF1 ; Yes BTFSC LED ; LED on? GOTO FIRSTLOW ; Yes INCF DCFLOW,F MOVLW 0x2A ; Test for 42 pulses SUBWF DCFLOW,W ; if true, C=1, loss of signal BTFSC STATUS,C GOTO DCFLOSS RETFIE DCFLOSS MOVLW 0x29 MOVWF DCFLOW BCF DCFOK BCF SOLID RETFIE FIRSTLOW ; Test for one or zero DCF bit BCF LED ; Switch off LED BCF RXD CLRF DCFLOW ; Destroy value in counter MOVLW 0x00 ; Decode received bit SUBWF DCFHIGH,W BTFSC STATUS,Z ; One sample? GOTO SETZERO ; Yes MOVLW 0x01 SUBWF DCFHIGH,W BTFSC STATUS,Z ; Two samples? GOTO SETZERO ; Yes MOVLW 0x02 SUBWF DCFHIGH,W BTFSC STATUS,Z ; Three samples? GOTO SETONE ; Yes MOVLW 0x03 SUBWF DCFHIGH,W BTFSC STATUS,Z ; Four samples? GOTO SETONE ; Yes MOVLW 0x04 SUBWF DCFHIGH,W BTFSC STATUS,Z ; FIVE samples? (just in case) GOTO SETONE ; Yes BCF DCFOK ; HIGH pulse too long BCF SOLID RETFIE SETONE BSF RXD SETZERO ; Rather remain zero INCF DCFSEC MOVLW 0x16 SUBWF DCFSEC,W BTFSS STATUS,C ; At minutes yet? RETFIE ; No MOVLW 0x1E SUBWF DCFSEC,W BTFSC STATUS,C ; At hours yet? GOTO SHIFTHOUR ; Yes BCF STATUS,C BTFSC RXD BSF STATUS,C RRF MINBUF,F ; Move received bit into minutes buffer RETFIE SHIFTHOUR MOVLW 0x25 SUBWF DCFSEC,W BTFSC STATUS,C ; Done hours? RETFIE ; Yes BCF STATUS,C BTFSC RXD BSF STATUS,C RRF HRBUF,F ; Move received bit into hours buffer RETFIE DCF1 BTFSS LED ; LED on? GOTO FIRSTHIGH ; No INCF DCFHIGH,F RETFIE ; Not testing anything else, already done at FIRSTLOW FIRSTHIGH BSF LED BCF DCFOK ; DCF check at next 59th second CLRF DCFHIGH ; Clear HIGH pulse duration counter MOVLW 0x0D SUBWF DCFLOW,W ; Test for pulse > 0x0D*50 ms (valid LOW pulse) BTFSS STATUS,C ; Pause too short? RETFIE ; Yes MOVLW 0x21 SUBWF DCFLOW,W ; Test for pulse > 0x21*50 ms (59th second) BTFSS STATUS,C ; Pause too short for 59th second? RETFIE ; Yes MOVLW 0x28 SUBWF DCFLOW,W ; Test for pulse >= 0x28*50 ms (59th second) BTFSC STATUS,C ; Pause too long? RETFIE MOVLW 0x0B ; Preload TIMER1 (0.5 second)... MOVWF TMR1H MOVLW 0xD0 MOVWF TMR1L ; ...to start the second half second after half a second CLRF DCFSEC ; This is the zeroth second (M) CLRF TSECS CLRF HSECS MOVLW 0x7F ; Blank parity bit ANDWF MINBUF,W ; and store in W CALL PARCHECK ; Call parity routine btfsc TEMP,0 ; Test parity bit 0 goto MINODD ; if bit=1 odd # of bits set BTFSS MINBUF,7 ; Parity even? GOTO HRCHECK ; Yes, check hour parity GOTO DCFNOK ; No, no need to test hours MINODD BTFSC MINBUF,7 ; Parity odd? GOTO HRCHECK ; Yes, check hour parity GOTO DCFNOK ; No, no need to test hours HRCHECK ; Check parity of hour MOVLW 0x7E ; Blank parity bit (and bit 0) ANDWF HRBUF,W ; and store in W CALL PARCHECK ; Call parity routine btfsc TEMP,0 ; Test parity bit 0 goto HRODD ; if bit=1 odd # of bits set BTFSS HRBUF,7 ; Parity even? GOTO DCFWOK ; Yes, make DCF OK GOTO DCFNOK ; No HRODD BTFSC HRBUF,7 ; Parity odd? GOTO DCFWOK ; Yes, make DCF OK GOTO DCFNOK ; No ; Parity Subroutine ; https://picprojects.org/projects/pictips.htm#Parity PARCHECK movwf TEMP swapf TEMP,W xorwf TEMP,W movwf TEMP rrf TEMP,F rrf TEMP,F xorwf TEMP,W movwf TEMP rrf TEMP,F xorwf TEMP,F return DCFWOK BSF DCFOK BSF SOLID ; Make colon solid BCF MINS,0 ; Decode minutes from buffer BTFSC MINBUF,0 BSF MINS,0 BCF MINS,1 BTFSC MINBUF,1 BSF MINS,1 BCF MINS,2 BTFSC MINBUF,2 BSF MINS,2 BCF MINS,3 BTFSC MINBUF,3 BSF MINS,3 BCF TMINS,0 BTFSC MINBUF,4 BSF TMINS,0 BCF TMINS,1 BTFSC MINBUF,5 BSF TMINS,1 BCF TMINS,2 BTFSC MINBUF,6 BSF TMINS,2 BCF HOURS,0 ; Decode hours from buffer BTFSC HRBUF,1 ; Only seven "hour" bits so offset by one BSF HOURS,0 BCF HOURS,1 BTFSC HRBUF,2 BSF HOURS,1 BCF HOURS,2 BTFSC HRBUF,3 BSF HOURS,2 BCF HOURS,3 BTFSC HRBUF,4 BSF HOURS,3 BCF THOURS,0 BTFSC HRBUF,5 BSF THOURS,0 BCF THOURS,1 BTFSC HRBUF,6 BSF THOURS,1 CLRF MINBUF CLRF HRBUF RETFIE DCFNOK BCF DCFOK BCF SOLID ; Make colon flash CLRF MINBUF CLRF HRBUF RETFIE Timehandler BCF PIR1,TMR1IF ; Clear Interrupt Source MOVLW 0x0B ; Preload TIMER1 (0.5 second) MOVWF TMR1H MOVLW 0xDB ; Tweak for excess interrupts (ugly) MOVWF TMR1L ; Increment halfseconds INCF HSECS,F MOVLW 0x14 ; Test for overflow SUBWF HSECS,W ; if true, C=1, increment 10 seconds BTFSS STATUS,C RETFIE CLRF HSECS ; Increment 10 seconds INCF TSECS,F MOVLW 0x06 ; Test for overflow SUBWF TSECS,W ; if true, C=1, increment minutes BTFSS STATUS,C RETFIE CLRF TSECS BTFSC SOLID ; DCF OK? If so skip further time processing RETFIE ; Yes ; Increment minutes INCF MINS,F MOVLW 0x0A ; Test for overflow SUBWF MINS,W ; if true, C=1, increment 10 minutes BTFSS STATUS,C RETFIE CLRF MINS ; Increment 10 minutes INCF TMINS,F MOVLW 0x06 ; Test for overflow SUBWF TMINS,W ; if true, C=1, increment hours BTFSS STATUS,C RETFIE CLRF TMINS ; Check for midnight MOVLW 0x02 SUBWF THOURS,0 ; if true, C=1, check for midnight BTFSC STATUS,C GOTO MIDNIGHT ; Increment hours INCF HOURS,F MOVLW 0x0A ; Test for overflow SUBWF HOURS,W ; if true, C=1, increment 10 hours BTFSS STATUS,C RETFIE CLRF HOURS ; Increment 10 hours INCF THOURS,F RETFIE MIDNIGHT INCF HOURS,F MOVLW 0x04 ; Test for midnight SUBWF HOURS,W ; if true, C=1, new day BTFSS STATUS,C RETFIE CLRF HOURS CLRF THOURS RETFIE Multiplex BCF PIR1,TMR2IF ; Clear interrupt source (4.114ms) BSF OE ; Disable outputs STP chip BCF COL12 ; Switch off colon BTFSS ROW0 GOTO ONROW1 BTFSS ROW1 GOTO ONROW2 BTFSS ROW2 GOTO ONROW3 BTFSS ROW3 GOTO ONROW4 BSF ROW4 MOVLW 0x02 MOVWF PCLATH ; Adjust for page boundary computed goto BCF STATUS,C ; Clear carry RRF HSECS,W ; Divide half seconds by two to get whole seconds and place in W reg CALL row0show MOVWF DIGIT6 MOVFW TSECS CALL row0show MOVWF DIGIT5 MOVFW MINS CALL row0show MOVWF DIGIT4 MOVFW TMINS CALL row0show MOVWF DIGIT3 MOVFW HOURS CALL row0show MOVWF DIGIT2 MOVFW THOURS CALL row0show MOVWF DIGIT1 BCF ROW0 GOTO Show ONROW1 BSF ROW0 MOVLW 0x02 MOVWF PCLATH ; Adjust for page boundary computed goto BCF STATUS,C ; Clear carry RRF HSECS,W ; Divide half seconds by two to get whole seconds and place in W reg CALL row1show MOVWF DIGIT6 MOVFW TSECS CALL row1show MOVWF DIGIT5 MOVFW MINS CALL row1show MOVWF DIGIT4 MOVFW TMINS CALL row1show MOVWF DIGIT3 MOVFW HOURS CALL row1show MOVWF DIGIT2 MOVFW THOURS CALL row1show MOVWF DIGIT1 BCF ROW1 GOTO Show ONROW2 BSF ROW1 MOVLW 0x02 MOVWF PCLATH ; Adjust for page boundary computed goto BCF STATUS,C ; Clear carry RRF HSECS,W ; Divide half seconds by two to get whole seconds and place in W reg CALL row2show MOVWF DIGIT6 MOVFW TSECS CALL row2show MOVWF DIGIT5 MOVFW MINS CALL row2show MOVWF DIGIT4 MOVFW TMINS CALL row2show MOVWF DIGIT3 MOVFW HOURS CALL row2show MOVWF DIGIT2 MOVFW THOURS CALL row2show MOVWF DIGIT1 BCF ROW2 GOTO Show ONROW3 BSF ROW2 MOVLW 0x02 MOVWF PCLATH ; Adjust for page boundary computed goto BCF STATUS,C ; Clear carry RRF HSECS,W ; Divide half seconds by two to get whole seconds and place in W reg CALL row3show MOVWF DIGIT6 MOVFW TSECS CALL row3show MOVWF DIGIT5 MOVFW MINS CALL row3show MOVWF DIGIT4 MOVFW TMINS CALL row3show MOVWF DIGIT3 MOVFW HOURS CALL row3show MOVWF DIGIT2 MOVFW THOURS CALL row3show MOVWF DIGIT1 BCF ROW3 GOTO Show ONROW4 BSF ROW3 MOVLW 0x02 MOVWF PCLATH ; Adjust for page boundary computed goto BCF STATUS,C ; Clear carry RRF HSECS,W ; Divide half seconds by two to get whole seconds and place in W reg CALL row4show MOVWF DIGIT6 MOVFW TSECS CALL row4show MOVWF DIGIT5 MOVFW MINS CALL row4show MOVWF DIGIT4 MOVFW TMINS CALL row4show MOVWF DIGIT3 MOVFW HOURS CALL row4show MOVWF DIGIT2 MOVFW THOURS CALL row4show MOVWF DIGIT1 BCF ROW4 Show BCF SDI BCF COL11 BTFSC DIGIT6,5 ; Get Col11 (rightmost column) BSF COL11 BCF COL10 BTFSC DIGIT6,6 ; Get Col10 BSF COL10 BCF SDI ; Start loading shift register: Data -> 0 BTFSC DIGIT6,7 ; Get Col0F BSF SDI BSF CLK BCF CLK ; Output bit BCF SDI ; Data -> 0 BTFSC DIGIT5,5 ; Get Col0E BSF SDI BSF CLK BCF CLK ; Output bit BCF SDI ; Data -> 0 BTFSC DIGIT5,6 ; Get Col0D BSF SDI BSF CLK BCF CLK ; Output bit BCF SDI ; Data -> 0 BTFSC DIGIT5,7 ; Get Col0C BSF SDI BSF CLK BCF CLK ; Output bit BCF SDI ; Data -> 0 BTFSC DIGIT4,5 ; Get Col0B BSF SDI BSF CLK BCF CLK ; Output bit BCF SDI ; Data -> 0 BTFSC DIGIT4,6 ; Get Col0A BSF SDI BSF CLK BCF CLK ; Output bit BCF SDI ; Data -> 0 BTFSC DIGIT4,7 ; Get Col09 BSF SDI BSF CLK BCF CLK ; Output bit BCF SDI ; Data -> 0 BTFSC DIGIT3,5 ; Get Col08 BSF SDI BSF CLK BCF CLK ; Output bit BCF SDI ; Data -> 0 BTFSC DIGIT3,6 ; Get Col07 BSF SDI BSF CLK BCF CLK ; Output bit BCF SDI ; Data -> 0 BTFSC DIGIT3,7 ; Get Col06 BSF SDI BSF CLK BCF CLK ; Output bit BCF SDI ; Data -> 0 BTFSC DIGIT2,5 ; Get Col05 BSF SDI BSF CLK BCF CLK ; Output bit BCF SDI ; Data -> 0 BTFSC DIGIT2,6 ; Get Col04 BSF SDI BSF CLK BCF CLK ; Output bit BCF SDI ; Data -> 0 BTFSC DIGIT2,7 ; Get Col03 BSF SDI BSF CLK BCF CLK ; Output bit BCF SDI ; Data -> 0 BTFSC DIGIT1,5 ; Get Col02 BSF SDI BSF CLK BCF CLK ; Output bit BCF SDI ; Data -> 0 BTFSC DIGIT1,6 ; Get Col01 BSF SDI BSF CLK BCF CLK ; Output bit BCF SDI ; Data -> 0 BTFSC DIGIT1,7 ; Get Col00 BSF SDI BSF CLK BCF CLK ; Output bit BSF LE BCF LE ; Drive latch, done shift register BTFSS SOLID ; Solid colon when OK BTFSC HSECS,1 ; Get colon pattern (0.5 Hz flash) if not OK BSF COL12 BCF OE RETFIE ; END Interrupthandlers START ;Init stuff BCF STATUS,RP0 ; Go to bank 0 BCF STATUS,RP1 ; Go to bank 0 MOVLW 0x07 ; Turn comparators off MOVWF CMCON BSF STATUS,RP0 ; Go to bank 1 CLRF TRISA ; Set port A as all outputs BSF TRISA,4 ; except port A (DCFIN) CLRF TRISB ; Set port B as all outputs MOVLW B'00000111' BANKSEL OPTION_REG MOVWF OPTION_REG ; Weak pullups, prescaler Timer0, 1:256 (0.26ms per count for Timer0) BCF STATUS,RP0 ; Go back to bank 0 BSF T1CON,T1CKPS1 ; These set the TIMER1 prescaler. Here are the possible values: BSF T1CON,T1CKPS0 ; 00=1:1 01=1:2 10=1:4 11=1:8 BCF T1CON,T1OSCEN ; Turn off the TIMER1 oscillator to save power (we don't need it because we're using the internal oscillator) BCF T1CON,TMR1CS ; Select the internal oscillator for TIMER1 BSF T1CON,TMR1ON ; Enable TIMER1 BSF T2CON,T2CKPS1 ; Prescale 1:16 for TIMER2 BCF T2CON,T2CKPS0 ; BCF T2CON,TOUTPS3 ; Postscale 1:1 for TIMER2 BCF T2CON,TOUTPS2 ; BCF T2CON,TOUTPS1 ; BCF T2CON,TOUTPS0 ; BSF T2CON,TMR2ON ; Enable TIMER2 ; Enable interrupts BCF INTCON,INTF ; Clear INTF flag before enabling interrupts BCF INTCON,T0IF ; Clear T0IF (TIMER0 Interrupt Flag) before enabling interrupts BCF PIR1,TMR1IF ; Clear TMR1IF (TIMER1 Interrupt Flag) before enabling interrupts BCF PIR1,TMR2IF ; Clear TMR2IF (TIMER1 Interrupt Flag) before enabling interrupts BCF INTCON,INTE ; Disable interrupt on RB0/INT pin (an External interrupt) BSF INTCON,PEIE ; Enable PEIE (PEripheral Interrupt Enable - for TIMER1, the 16 bit timer) BSF STATUS,RP0 ; Go to bank 1 BSF PIE1,TMR1IE ; Enable interrupt on TIMER1 overflow (when the TMR1 register pair wraps around from 0xFFFF to 0x0000) BSF PIE1,TMR2IE ; Enable interrupt on TIMER2 overflow BCF PIE1,CMIE ; Disable interrupt on comparator output change BCF STATUS,RP0 ; Go to bank 0 BSF INTCON,GIE ; Enable global interrupts ; INIT Registers CLRF TMR1H CLRF TMR1L CLRF THOURS CLRF HOURS CLRF TMINS CLRF MINS CLRF TSECS CLRF HSECS CLRF FLAGS ; INIT Ports MOVLW B'00011110' ; Switch off high side MOSFETs except top row MOVWF PORTB CLRF PORTA WaitForInterrupt GOTO WaitForInterrupt ; Do Nothing ; Lookup tables ORG 0x222 row0show ADDWF PCL,F ; Jump into the lookup table RETLW B'11100000' ; 0 RETLW B'11000000' ; 1 fancy serif RETLW B'11100000' ; 2 RETLW B'11100000' ; 3 RETLW B'10000000' ; 4 RETLW B'11100000' ; 5 RETLW B'11000000' ; 6 with short top bar RETLW B'11100000' ; 7 RETLW B'11100000' ; 8 RETLW B'11100000' ; 9 row1show ADDWF PCL,F ; Jump into the lookup table RETLW B'10100000' ; 0 RETLW B'01000000' ; 1 RETLW B'00100000' ; 2 RETLW B'00100000' ; 3 RETLW B'10100000' ; 4 RETLW B'10000000' ; 5 RETLW B'10000000' ; 6 RETLW B'10100000' ; 7 with a hook RETLW B'10100000' ; 8 RETLW B'10100000' ; 9 row2show ADDWF PCL,F ; Jump into the lookup table RETLW B'10100000' ; 0 RETLW B'01000000' ; 1 RETLW B'11100000' ; 2 RETLW B'11100000' ; 3 RETLW B'11100000' ; 4 RETLW B'11100000' ; 5 RETLW B'11100000' ; 6 RETLW B'00100000' ; 7 RETLW B'11100000' ; 8 RETLW B'11100000' ; 9 row3show ADDWF PCL,F ; Jump into the lookup table RETLW B'10100000' ; 0 RETLW B'01000000' ; 1 RETLW B'10000000' ; 2 RETLW B'00100000' ; 3 RETLW B'00100000' ; 4 RETLW B'00100000' ; 5 RETLW B'10100000' ; 6 RETLW B'00100000' ; 7 RETLW B'10100000' ; 8 RETLW B'00100000' ; 9 row4show ADDWF PCL,F ; Jump into the lookup table RETLW B'11100000' ; 0 RETLW B'11100000' ; 1 fancy bottom bar RETLW B'11100000' ; 2 RETLW B'11100000' ; 3 RETLW B'00100000' ; 4 RETLW B'11100000' ; 5 RETLW B'11100000' ; 6 RETLW B'00100000' ; 7 RETLW B'11100000' ; 8 RETLW B'11100000' ; 9 with long tail NOP END