Program #5 - A Clock that Displays Time in Binary Numbers

;-----------------------------------------------------------------------;
; BINCLOCK.ASM A clock that displays in bcd numbers ;
;-----------------------------------------------------------------------;
 LIST P=16F84 ; tells which processor is used
 INCLUDE "p16f84.inc" ; defines various registers etc. Look it over.
 ERRORLEVEL -224 ; supress annoying message because of tris
 __CONFIG _PWRTE_ON & _LP_OSC & _WDT_OFF ; configuration switches
 CBLOCK 0CH
 sec ; seconds digit
 sec10 ; 10's of second digit
 mins ; minutes digit
 min10 ; 10's of minutes digit
 hr ; hours digit
 hr10 ; 10's of hours digit
 w_temp ; holds W during interrupt
 status_temp ; holds STATUS during interrupt
 fsr_temp ; holds FSR during interrupt
 button ; holds mask for pushbuttons
 ENDC
 
;-----------------------------------------------------------------------;
; Here are some DEFINEs which give 'names' to pushbutton port bits ;
;-----------------------------------------------------------------------;
 #DEFINE SETPB PORTB, 4
 #DEFINE SELECTPB PORTB, 5
 #DEFINE SHOWPB PORTB, 6
 ORG 0 ; start at location 0
 goto main ; jump over to main routine 
 ORG 4
 
 goto isr ; jump to interrupt routine
;-----------------------------------------------------------------------;
; High limit + 1 of digits at position W ;
;-----------------------------------------------------------------------;
sethi:
 addwf PCL, f
 dt H'A',H'6',H'A',H'6',H'A',H'3'
;-----------------------------------------------------------------------;
; Delay routines ;
;-----------------------------------------------------------------------;
msec250: ; enter here to delay for 250 milliseconds
 movlw D'250' 
nmsec: ; delay for # msec in W on entry
 nop ; each nop is 0.122 milliseconds
 nop
 nop ; each total loop is 8 X 0.122 = 0.976 msec
 nop
 addlw H'FF' ; same as subtracting 1 from W
 btfss STATUS, Z ; skip if result is zero
 goto nmsec ; this is 2 X 0.122 msec 
 return ; back to calling point
;-----------------------------------------------------------------------;
; Delay for one second ;
;-----------------------------------------------------------------------;
onesecond: ; a subroutine that delays for 1 seconds
 call msec250
 call msec250
 call msec250
 call msec250
 return
;-----------------------------------------------------------------------;
; Put value in W on LEDs for 1 second ;
;-----------------------------------------------------------------------;
sendnbr:
 movwf PORTB ; light LEDs
 call onesecond ; wait 1 second
 clrf PORTB ; clear the LEDs
 movlw D'100' ; pause for 0.1 sec
 call nmsec
 return 
;-----------------------------------------------------------------------;
; Send the current time out LEDs ;
;-----------------------------------------------------------------------;
disptime: 
 movf hr10, W
 call sendnbr
 movf hr, W
 call sendnbr
 movf min10, W
 call sendnbr
 movf mins, W
 call sendnbr
 return
;-----------------------------------------------------------------------;
; Wait until selected button is released ;
;-----------------------------------------------------------------------;
waitup6: ; wait for show pushbutton up
 movlw B'01000000' ; RB6 mask
 movwf button
 goto wait
waitup5: ; wait for select pushbutton up
 movlw B'00100000' ; RB5 mask
 movwf button
 goto wait
waitup4: ; wait for set pushbutton up
 movlw B'00010000' ; RB4 mask
 movwf button
wait:
 movf button, W ; mask into W
 andwf PORTB, W
 btfsc STATUS, Z ; skip if not zero (released)
 goto wait
 movlw D'10'
 call nmsec ; wait 10 msec for debounce
 movf button, W ; check for release again
 andwf PORTB, W
 btfsc STATUS, Z ; skip if selected button released
 goto wait
 return ; yes, finished
;-----------------------------------------------------------------------;
; Initilization Subroutine ;
;-----------------------------------------------------------------------;
init:
 movlw B'0000000' ; all outputs port A
 tris PORTA 
 movlw B'01110000' ; RB4 - RB6 inputs, others outputs
 tris PORTB ; on port B
 movlw H'0' ; all low (off)
 movlw PORTB
 movlw B'00000100' ; pull-ups enabled 
 ; prescaler assigned to TMR0
 ; prescaler set to 1:32
 ; rolls over each second
 option 
 movlw 0
 movwf hr10
 movlw H'9' ; initialize hrs, mins and secs
 movwf hr ; Do this before interrupts are
 movlw H'5' ; turned on because isr also acts
 movwf min10 ; on these registers
 movlw H'0'
 movwf mins
 movwf sec10
 movwf sec
 movlw B'10100000' ; GIE & T0IE set, T0IF cleared
 movwf INTCON
 return
;-----------------------------------------------------------------------;
; Interrupt routine, increments time by one second (BCD) ;
;-----------------------------------------------------------------------;
isr:
 movwf w_temp ; save W
 swapf STATUS,W ; save status
 movwf status_temp ; without changing flags
 swapf FSR,W ; save FSR
 movwf fsr_temp ; without changing flags
 movlw sec ; point at sec register
 movwf FSR
newdigit: incf INDF, f ; current digit up one
 movlw sec ; get difference between sec and FSR
 subwf FSR, W
 call sethi ; use to get high limit + 1
 subwf INDF, W ; reached that number yet?
 btfss STATUS, Z ; skip over if yes
 goto restore ; else exit isr
 clrf INDF ; set current digit to 0
 incf FSR, f ; point at next digit
 btfss hr10, 1 ; has hr10 reached 2?
 goto newdigit ; no, increment the next digit
 btfss hr, 2 ; has hr reached 4?
 goto newdigit ; no
 clrf hr ; yes, set hour to 00
 clrf hr10 ; and hour 10
restore: 
 swapf status_temp,W ; get original status back
 movwf STATUS ; into status register
 swapf fsr_temp,W ; get original fsr back
 movwf FSR ; into status register
 swapf w_temp,f ; old no flags trick again
 swapf w_temp,W ; to restore W
 bcf INTCON,T0IF ; clear the TMR0 interrupt flag
 retfie ; finished reset GIE
;-----------------------------------------------------------------------;
; Increment and display digit pointed to by FSR ;
;-----------------------------------------------------------------------;
updigit:
 incf INDF, f ; selected digit up one
 movlw mins ; set up to subtract mins address
 subwf FSR, W ; from address of current digit
 call sethi ; get maximum of digit + 1 into W
 subwf INDF, W ; is it = to current digit value?
 btfsc STATUS, Z ; gives zero if yes, skip if no
 clrf INDF ; reset value of digit to zero
 movf INDF, W ; get current value and ..
 movwf PORTB ; display it
 call onesecond ; pause for 1 second
 return
;-----------------------------------------------------------------------;
; increment selected digit until select pressed ;
;-----------------------------------------------------------------------;
setdigit:
 movwf PORTB
 btfss SETPB ; set pressed?
 call updigit ; yes
 btfsc SELECTPB ; check if select pressed
 goto $ -3 ; repeat till select pressed again
 call waitup5 ; make sure select released
 incf FSR, f
 return
;-----------------------------------------------------------------------;
; Select and increment digits ;
;-----------------------------------------------------------------------;
select:
 bcf INTCON, GIE ; no interrupts while setting time
 movlw mins ; point at minutes register
 movwf FSR
 call waitup5 ; wait on select pushbutton up
 movlw B'00000001' ; light right LED (mins)
 call setdigit
 movlw B'00000010' ; light min10 LED
 call setdigit
 movlw B'00000100' ; light hr LED
 call setdigit
 movlw B'00001000' ; hr10 LED on
 call setdigit
 clrf PORTB ; clear LEDs
 bsf INTCON, GIE ; enable interrupts again
 return
;-----------------------------------------------------------------------;
; The main routine ;
;-----------------------------------------------------------------------;
main: 
 call init ; set up initial conditions
loop: 
 btfss SHOWPB ; check for show pushbutton
 call disptime ; display the time
 btfss SELECTPB ; check for select
 call select
 goto loop ; do forever
 end

A Binary Clock

This program is a clock. The time will be displayed when the 'show' pushbutton is pressed. The digits of the time are displayed in BCD on the four LEDs. Tens of hours is displayed followed by hours, tens of minutes and then minutes.

If the 'select' pushbutton is pressed the program goes into a mode where the digits can be set. The rightmost LED will come on indicating that the minutes digit is to be set. The minutes digit will be incremented and displayed at one second intervals while the 'set' pushbutton is pressed. Pressing 'select' again will transfer to the next higher digit. After tens of hours is set, the next press of the 'select' button returns you to the time display mode.

Defines

There are three pushbuttons in this program. Each uses a bit in Port B. To make things easier, names have been assigned to these bits. The compiler directive '#DEFINE' is simply a word replacement. Everywhere 'SETPB' is found the the code it will be replaced with 'PORTB, 4'.

A comparison with 'eggtimer.asm'

This program is similar the eggtimer.asm but instead of counting down, it counts up. There are also six registers holding digits, (only 4 displayed). These are updated in an interrupt routine, ('isr'), that is reached by a 'goto' at location 4.

If you look at the interrupt routine in 'eggtimer' you find each of the three digit registers treated in turn. This time there are six registers but you don't see the name of any but 'sec' in 'isr'. The code between saving and restoring of registers is even smaller in the case of 'binclock'. How can this be, more registers but fewer instructions?

Indirect Addressing

The answer lies in the FSR register and what is called 'indirect addressing'. When we have a number of registers in sequence like sec - hr10, we can use another register, FSR, to point to the first, (sec). If we increment FSR, it points to 'sec10'. Increment it again and it points to 'mins' etc. The register INDF, (indirect register), takes the place of whatever register FSR is pointing to. Operations on INDF are actually performed on the register FSR points to. Since the same actions are repeated on each of the six registers in turn, one piece of code containing INDF can operate on each by incrementing FSR.

We use the register 'FSR' in the isr and must save it on entry and restore it on exit of the interrupt subroutine. This is because we also use FSR in the display routines outside the isr.

Setting the time

Indirect addressing and the FSR register are also used in the code that sets the time. This is another place where each register has the same thing done to it in turn. When you push the 'select' button, the program jumps to a routine that allows you to increment a selected register. The selected registers are 'mins' - 'hr10' and a LED lights showing the position of the digit to be incremented.

Setting time involves three subroutines. 'select' calls 'setdigit' which in turn calls 'updigit' Subroutine calls can be listed in any order and the call will find the code. But, I like to list subroutines with those called preceding those doing the calling. This leaves the main routine last.

Notice that interrupts are disabled at the beginning of the time setting routine and enabled on leaving. This is necessary because the interrupt routine works with the same registers as the time setting routine. We can't have both changing the registers 'at the same time'.

Masks and ANDing

Some code space is saved by combining wait routines for three separate pushbuttons. It would be nice to write an instruction like btfsc PORTB, (variable), but that isn't allowed. The bit number has to be a constant. Instead we work with the whole port and what is called a 'mask'.

A mask is like a piece of cardboard with holes in it that you place over the binary representation of a register. Where holes are, the bits come through unchanged; in other positions they will be changed to zeros. The mask has 1's where the holes are, zeros in the other bit positions. We 'AND' the number with a mask to zero certain bits.

Suppose we 'AND' the number B'10110101' with the mask B'00001111'. The result is B'00000101'. We have removed the high four bits, (upper nibble), of the original number. By using a mask with only 1 bit set, we can look at a single bit in a number. This is what is done in the code starting at waitup6.

For more information on 'AND' and other logical bit operations try: http://www.cscene.org/cs9/cs9-02.html

Using a table

A table is used in both the time setting routine and the interrupt subroutine. The table holds 'overflow' numbers for various time digit registers. These are used to reset the digits back to zero. Notice that the table is placed first in program memory. If any part of the table extends beyond the 256th instruction, special instructions are needed to access it.

Now it's your turn

The time setting routine is sufficient but there are a couple of problems. First, the digit zero doesn't appear; instead there is a blank display just the same as between time displays. It should be easy to make a certain pattern appear in place of the zero, say, '1111' or all digits on. Can you make the changes? Two places are necessary; just before 'movwf PORTB' in 'sendnbr' and in 'updigit'.

Secondly, the display indicating which digit is to be set is the same as the display for the digits 1,2,4 and 8. Maybe when indicating which digit is to be set the digit could flash on and off. Can you change the subroutine 'setdigit' to make this happen?

The clock could be turned into an alarm clock. Give some thought to how you would have the current time checked against alarm time to set off the alarm. You will need more registers to hold the alarm time digits. The same time setting routine can be used because of FSR.

You might want the clock to be able to 'memorize' the alarm times, even if the battery failed or was removed. That will be covered in program 6.

This clock program could be combined with routines from 'morsenbr' for an audible clock.

You may want to have the clock display every ten seconds rather than have to press the 'show pushbutton'. Can you make the necessary changes? I hope you are using low current LEDs if you do this.

Comments:

Questions:


file: /Techref/piclist/cheapic/binclock.htm, 21KB, , updated: 2008年3月3日 10:58, local time: 2025年9月2日 10:27,
40.74.122.252:LOG IN

©2025 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions?
Please DO link to this page! Digg it! / MAKE!

<A HREF="http://techref.massmind.org/techref/piclist/cheapic/binclock.htm"> Binary Clock </A>

After you find an appropriate page, you are invited to your to this massmind site! (posts will be visible only to you before review) Just type a nice message (short messages are blocked as spam) in the box and press the Post button. (HTML welcomed, but not the <A tag: Instead, use the link box to link to another page. A tutorial is available Members can login to post directly, become page editors, and be credited for their posts.


Link? Put it here:
if you want a response, please enter your email address:
Attn spammers: All posts are reviewed before being made visible to anyone other than the poster.
Did you find what you needed?

Welcome to massmind.org!

Welcome to techref.massmind.org!

.

AltStyle によって変換されたページ (->オリジナル) /