The first version was drawn with a sharpie onto copper by hand. It is ugly (and in my photostream if you want to see). A few months later, I was assigned a project in school that involved getting a board fabbed. I had plenty of room left before reaching the minimum size, so I made a few of these binary clocks. This was my second time doing a layout in Eagle (the first being the school project), and I didn't really know what I was doing. I made a layout, without making a schematic first. Luckily, the layout worked, but I still don't have a schematic drawn up for this project - I will if there is enough demand. There are 2 things about the layout that I would change if i made more - the switch holes are a bit off, and I would add an ICSP header. I finished up 4 of these clocks, and gave two as gifts. The others are currently at my parents' house, though. I will be home and have some pictures in a few months.
Anyway, I think the value of this project is in the code. I learned PIC assembly from John Morton's PIC: Your Personal Introductory Course. I have since moved on to C, but I sometimes miss coding in assembly. The code that follows is very likely not efficient and may have remnants of past versions, but it should be easy to follow, and got the job done.
;********************************************************
;* Binary Clock *
;* Written By: Hal Emmer *
;* Date: July, 2006 *
;* version: 2.0 modified to use interrupts *
;* for PIc: 16f628a *
;* clock: internal oscillator *
;**************************************************************
LIST P=16F628a
ERRORLEVEL -302 ; suppress bank selection messages
__CONFIG 3F18H ; int oscillator
PCL EQU 2 ; bits and registers are defined here to
STATUS EQU 3 ; make this program self contained.
PORTA EQU 5
PORTB EQU 6
INTCON EQU 0BH
TRISA EQU 85H
TRISB EQU 86H
OPTREG EQU 81H
CMCON EQU 1FH
TMR0 EQU 01H
W EQU 0
F EQU 1
C EQU 0 ; bits in STATUS
Z EQU 2
RP0 EQU 5
T0IF EQU 2 ; bit in INTCON
CBLOCK 20H ; define variables required
TICKS
SEGS ; one bit per segment: "-gfedcba"
SEC
MIN
HOUR
FRAME ; used to decide when to display time
HHMM ; one bit per digit displayed
COUNT ; scratch register
DIGIT ; last digit displayed
DELVAR ; delay counter
SPEED
SPEED2 ; used to set time
LIGHT ; hardware compensation
COUNT1
W_SAVE
STATUS_SAVE
ENDC
;*********************************;
; Initialization ;
;*********************************;
ORG 0
goto INIT
ORG 4
goto ISR
INIT CLRF SEC
CLRF MIN
CLRF HOUR
CLRF FRAME
CLRF PORTA
CLRF PORTB
CLRF CMCON
MOVLW 07H
MOVWF CMCON
movlw h'C4'
movwf SPEED
movlw d'59'
movwf SPEED2
BSF STATUS,RP0 ; select register bank 1
CLRF TRISB
BSF TRISB,6 ; RB6 is an input min button
BSF TRISB,7 ; RB7 is an input hr button
CLRF TRISA
bsf TRISA,4 ; RA4 is clockin
MOVLW b'00101000' ; t0cs is RA4/T0CKI
MOVWF OPTREG ; prescalar assigned to WDT (no prescaler)
BCF STATUS,RP0 ; reselect bank 0
BSF INTCON, 5 ; t0 overflow interrupt enable
BSF INTCON, 7 ; global interrupt enable
;*********************************;
; Main Program ;
;*********************************;
MAIN
CALL DISPLAY ; continue to display
GOTO MAIN ; unless interrupted
;*********************************;
; Real-time clock algorithm ;
;*********************************;
CLOCK
INCF SEC,F ; second for next second
MOVF SEC,W
SUBWF SPEED2,W
BTFSC STATUS,C
RETURN
CLRF SEC
INCF MIN,F
MOVF MIN,W
SUBLW d'59'
BTFSC STATUS,C
RETURN
CLRF MIN
INCF HOUR,F
MOVF HOUR,W
SUBLW d'23'
BTFSS STATUS,C
CLRF HOUR
RETURN
;*********************************;
; Displays digit in W ;
;*********************************;
DISPLAY clrf LIGHT
btfsc SEC,0 ; these lines are used to translate
bsf LIGHT,3 ; the stored values to the physical pins
btfsc SEC,1 ; that the LEdS are connected to. Ideally,
bsf LIGHT,2 ; they would not be required, but I wired some
btfsc SEC,2 ; stuff backwards, so I had to fix it in
bsf LIGHT,1 ; firmware - no biggie.
btfsc SEC,3
bsf LIGHT,0
btfsc SEC,4
bsf LIGHT,4
btfsc SEC,5
bsf LIGHT,5
movfw LIGHT
MOVWF PORTB ; and display
BSF PORTA,1
CALL DELAY2 ; short delay
BCF PORTA,1
clrf LIGHT
btfsc MIN,0
bsf LIGHT,3
btfsc MIN,1
bsf LIGHT,2
btfsc MIN,2
bsf LIGHT,1
btfsc MIN,3
bsf LIGHT,0
btfsc MIN,4
bsf LIGHT,4
btfsc MIN,5
bsf LIGHT,5
movfw LIGHT
MOVWF PORTB ; and display
BSF PORTA,0
CALL DELAY2 ; short delay
BCF PORTA,0
clrf LIGHT
btfsc HOUR,0
bsf LIGHT,3
btfsc HOUR,1
bsf LIGHT,2
btfsc HOUR,2
bsf LIGHT,1
btfsc HOUR,3
bsf LIGHT,0
btfsc HOUR,4
bsf LIGHT,4
btfsc HOUR,5
bsf LIGHT,5
movfw LIGHT
MOVWF PORTB ; and display
BSF PORTA,2
CALL DELAY2 ; short delay
BCF PORTA,2
RETURN
;*********************************;
; Checks for a switch press and ;
; updates digit if displayed ;
;*********************************;
CHKSW BTFSC PORTB,6 ; switch closed?
goto CLRCNT1 ; no - return 0
movlw h'FF'
movwf SPEED
movlw d'19'
movwf SPEED2
incf COUNT1,f
movfw COUNT1
SUBLW 5
BTFSC STATUS,C
RETURN
movlw h'FF'
movwf SPEED
RETURN
CHKSW2 BTFSC PORTB,7 ; switch closed?
RETURN ; no
INCF HOUR,F
MOVF HOUR,W
SUBLW 23
BTFSS STATUS,C
CLRF HOUR
RETURN
;*********************************;
; Delay used by switch routine ;
;*********************************;
DELAY MOVLW D'12' ; roughly 100ms delay
MOVWF TICKS
DEL1 CALL DELAY2
DECFSZ TICKS,F
GOTO DEL1
RETURN
DELAY2 MOVLW d'255' ; 255 cycles or so...will test timing.
MOVWF DELVAR
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
DECFSZ DELVAR,F
GOTO DELAY2+2
RETURN
;*********************************;
; Reset speed timers ;
;*********************************;
CLRCNT1 movlw h'C4'
movwf SPEED
movlw d'59'
movwf SPEED2
clrf COUNT1
return
ISR MOVWF W_SAVE ; save W
SWAPF STATUS, W ; save STATUS
MOVWF STATUS_SAVE
BCF STATUS, RP0 ; be sure we are in bank 0
BCF INTCON,2
movfw SPEED
movwf TMR0
movfw SPEED
movwf TMR0
CALL CLOCK ; increment the timer
CALL CHKSW ; and check the buttons
CALL CHKSW2
SWAPF STATUS_SAVE, W
MOVWF STATUS ; restore W and STATUS
SWAPF W_SAVE, F
SWAPF W_SAVE, W
RETFIE ; return from isr
END
Also, a big thanks to Greg Houston for http://formatmysourcecode.blogspot.com/.