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