Maker Pro
Maker Pro

PIC16F84A Digital Clock (It works!)

I designed a digital clock using a PIC16F84A. I have a minor (major with my programming skills) I can't figure out. The 1st hour display (the one in red! XX:XX) is always a 0 when it is not a 1. For instance, if it is 1:30, it is displayed 01:30. I want it to be 1 or blank. This is not a huge problem, but it is annoying and I'd rather not waste power. I have the code, and the circuit on Proteus. Could someone PLEASE help me with this? I'd be glad to email you whatever you need!
 
Code...

Be gentle i'm a rookie. I'm open for any suggestions.

PROCESSOR PIC16F84A
INCLUDE <P16F84A.INC>
RADIX HEX
ORG 0000h
GOTO MAIN
ORG 0004h
GOTO ISR




CBLOCK 0Ch
S1
S10
M1
M10
H1
H10
DEL
DEL0
DEL01
DEL02
WHAT
QSTAT

ENDC
#DEFINE DP PORTB,0 ; PIN NO 06 DECIMA POINT


DELAY01 DECFSZ DEL,1
GOTO $-.1
CLRF PORTB
RETURN
DELAY02 MOVLW .2
MOVWF DEL01
DECFSZ DEL01,1
GOTO $-.1
RETURN

;;;;;;;;;;;;;;;;;;;SEVEN SEGMENT DISPLAY CONNECTING TABLE
TABLE ADDWF PCL,1 ;hgfedcba segments
RETLW B'01111110' ;0
RETLW B'00001100' ;1
RETLW B'10110110' ;2
RETLW B'10011110' ;3
RETLW B'11001100' ;4
RETLW B'11011010' ;5
RETLW B'11111010' ;6
RETLW B'00001110' ;7
RETLW B'11111110' ;8
RETLW B'11011110' ;9

SCAN MOVLW B'00000001' ;SEGMENT 01
MOVWF PORTA
MOVF M1,0
CALL TABLE
MOVWF PORTB
CALL DELAY01

MOVLW B'00000010' ;SEGMENT 02
MOVWF PORTA
MOVF M10,0
CALL TABLE
MOVWF PORTB
CALL DELAY01

MOVLW B'00000100' ;SEGMENT 03
MOVWF PORTA
MOVF H1,0
CALL TABLE
MOVWF PORTB
CALL DELAY01

MOVLW B'00001000' ;SEGMENT 04
MOVWF PORTA
MOVF H10,0
CALL TABLE
MOVWF PORTB
CALL DELAY01
RETURN

INCR INCF S1,1
MOVF S1,0
BCF STATUS,Z
XORLW .10
BTFSS STATUS,Z
RETURN
CLRF S1
INCF S10,1
MOVF S10,0
BCF STATUS,Z
XORLW .6
BTFSS STATUS,Z
RETURN
CLRF S10
INCR_SM INCF M1,1
MOVF M1,0
BCF STATUS,Z
XORLW .10
BTFSS STATUS,Z
RETURN
CLRF M1
INCF M10,1
MOVF M10,0
BCF STATUS,Z
XORLW .6
BTFSS STATUS,Z
RETURN
CLRF M10

INCR_SH INCF H1
SWAPF H10,0
ADDWF H1,0
BCF STATUS,Z
XORLW 13h
BTFSS STATUS,Z
GOTO $+6
CLRF H1
CLRF H10
MOVLW .1
MOVWF H1
RETURN
MOVF H1,0
BCF STATUS,Z
XORLW .10
BTFSS STATUS,Z
RETURN
CLRF H1
INCF H10,1
RETURN
ISR BCF INTCON,GIE
MOVWF WHAT
SWAPF STATUS,0
MOVWF QSTAT
BCF INTCON,T0IF
MOVLW .5
MOVWF TMR0
INCF DEL0,1
MOVF DEL0,0
ANDLW B'01111111'
BCF STATUS,Z
XORLW .125
BTFSS STATUS,Z
GOTO LABLE
BTFSS DEL0,7
GOTO $+.5
CLRF DEL0
CALL INCR
GOTO LABLE
GOTO LABLE
BTFSS DEL0,7
GOTO $+.5
CLRF DEL0
BCF DP ; DECIMAL POINT
CALL INCR
GOTO LABLE
MOVLW .200 ; DECIMAL POINT DELAY SETTING
MOVWF DEL0 ;DECIMAL POINT
BSF DP


LABLE SWAPF QSTAT,0
MOVWF STATUS
SWAPF WHAT,1
SWAPF WHAT,0
BSF INTCON,GIE
RETFIE

KEY BSF OPTION_REG,7
BCF INTCON,GIE
BSF STATUS,RP0
MOVLW B'11101111'
MOVWF TRISB
BCF STATUS,RP0
MOVLW B'00000000'
MOVWF PORTB
CALL DELAY02
;**********************************
SM1 BTFSC PORTB,1 ; SET MINITUS
GOTO SH1
CALL INCR_SM
GOTO KEYX
;***********************************

;***********************************
SH1 CALL DELAY02
BTFSC PORTB,2 ; SET HOUR
GOTO KEYDE
CALL INCR_SH
;***********************************


KEYX BSF STATUS,RP0
CLRF TRISB
BCF STATUS,RP0
MOVLW .100
MOVWF DEL02
CALL SCAN
DECFSZ DEL02,1
GOTO $-.2
BSF INTCON,GIE
BCF OPTION_REG,7
RETURN

KEYDE BSF STATUS,RP0
CLRF TRISB
BCF STATUS,RP0
BSF INTCON,GIE
BCF OPTION_REG,7
RETURN



MAIN CLRF S1
CLRF S10
CLRF M1
CLRF M10
CLRF H1
CLRF H10
CLRF DEL
CLRF DEL0
CLRF DEL01
CLRF DEL02
CLRF WHAT
CLRF QSTAT
BSF STATUS,RP0
CLRF TRISB
CLRF TRISA
MOVLW B'00000011'
MOVWF OPTION_REG
BSF INTCON,T0IE
BSF INTCON,GIE
BCF STATUS,RP0
CALL SCAN
CALL KEY
GOTO $-.2

ORG 2007h
DATA 3FF1h
END
 
It is you that has to be gentle with me. It has been a long time since I have used this kind of code for programming PICs. Usually I stick with C.

I suspect the solution might be around this area:
MOVLW B'00001000' ;SEGMENT 04
MOVWF PORTA
MOVF H10,0
CALL TABLE
MOVWF PORTB
CALL DELAY01
RETURN

I believe this part of the code looks up what needs to be displayed in the hour (tens) position and sends it to the appropriate memory location where it later is pushed to the LED.

You will need to add code in here to check to see if what it looked up returned a "B'01111110' ;0 " from the table. Do this check after it returns from the call to the lookup table. Then if it does return the code (01111110) , you need to change what is in the working register to all zeroes. This can be done by exclusive oring W with itself (I forget the exact command).

With all zeroes loaded, no LEDs will be turned on which is what you want to happen.
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
You need to handle the first digit as a special case and blank it if it is equal to zero.

I suspect that where you have commented "Segment 01" you actually mean Digit 01

Can I suggest the following change:

SCAN MOVLW B'00000001' ;Digit 01
MOVWF PORTA
MOVF M1,0
MOVLW B'00000000' ; set for no segments lit
BTFSC ; skip call to get segments if F is zero
CALL TABLE
MOVWF PORTB
CALL DELAY01


I'd also suggest you read this.

edit: code is untested and based on my limited understanding of your mostly uncommented code.
 
Close!

I'm VERY close with this method.
Now it's doing this:
The zero is gone, but when it should display the "1" it shows a "C". It's like opposite of what I want - everything BUT the "1" shows up!!! I tried different numbers (B'xxxxxxxx') instead of 01111110.
This is what I added to the code (added info in lower case):

MOVLW B'00001000' ;SEGMENT 04
MOVWF PORTA
MOVF H10,0
CALL TABLE
xorlw b'01111110'
MOVWF PORTB
CALL DELAY01
RETURN

There is two xor instructions, I'm lost with them. Here is a link with the instruction set.
http://ww1.microchip.com/downloads/en/devicedoc/35007b.pdf

Now that I'm this close, it's even more frustrating! lol

I'm very greatful for your help.

-Matt

It is you that has to be gentle with me. It has been a long time since I have used this kind of code for programming PICs. Usually I stick with C.

I suspect the solution might be around this area:
MOVLW B'00001000' ;SEGMENT 04
MOVWF PORTA
MOVF H10,0
CALL TABLE
MOVWF PORTB
CALL DELAY01
RETURN

I believe this part of the code looks up what needs to be displayed in the hour (tens) position and sends it to the appropriate memory location where it later is pushed to the LED.

You will need to add code in here to check to see if what it looked up returned a "B'01111110' ;0 " from the table. Do this check after it returns from the call to the lookup table. Then if it does return the code (01111110) , you need to change what is in the working register to all zeroes. This can be done by exclusive oring W with itself (I forget the exact command).

With all zeroes loaded, no LEDs will be turned on which is what you want to happen.
 
MOVLW B'00001000' ;SEGMENT 04
MOVWF PORTA
MOVF H10,0
CALL TABLE
<Compare W with b'01111110'> Not sure what the code would be for this. You will need to look it up.
BTFSC STATUS,Z ; After compare Z is set if W = b'01111110'
xorlw b'01111110' ; If W = b'01111110', then XOR with same to get all zeroes in W.
MOVWF PORTB
CALL DELAY01
RETURN

This is what I meant. I don't remember what the code is for compare W to a literal. you will need to look this up and plug it in.

In your previous post, your code doesn't check to see if W is b'01111110' first, it just does a XOR operation regardless. You need to check it first, then XOR.
 
That just dawned on me after I wrote the last post. The PIC16 instruction set is limited on comparing (I think). PIC18 instruction sets are huge! I'm working on a way to use a bit test.
 
<Compare W with b'01111110'> Not sure what the code would be for this. You will need to look it up.


I looked over the code and I guess there is no command to compare W with a literal. You will probably need to do it this way:


MOVLW B'00001000' ;SEGMENT 04
MOVWF PORTA
MOVF H10,0 ; Z flag is affected by this. If content is zero, then Z = 1
BTFSC STATUS,Z
goto L1
CALL TABLE
L1: MOVWF PORTB
CALL DELAY01
RETURN

This way, if H10 turns out to be zero, just send that to the PORTB. If H10 is not zero, then do what you would normally do, Call the table and then send that out to Port B.
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
As far as the segment in question, Steve, it's actually "Segment 4". Any help there?

I think you'll find it's "digit"

Segment refers to one of the 7 bars which make up each digit. If you were driving the display this way you would be setting 4 segments and you would have 7 of these little sections of code.

In this case you are setting the appropriate segments for each digit. (the segments are traditionally called a, b, c, d, e, f, and g)
 
The code below is the area where your delays are set up:

DELAY01 DECFSZ DEL,1
GOTO $-.1
CLRF PORTB
RETURN
DELAY02 MOVLW .2
MOVWF DEL01
DECFSZ DEL01,1
GOTO $-.1
RETURN

I believe the DEL variable is set to zero initially, then it is decremented in the timing routine DEL01. You might try setting it initially to a different value and see if that makes the timing go faster. Try "E0" and see what effect it has on timing, then go from there.
 
Ahhh

I've tried your suggestion. What would your proposed code look like. I think I'm doing it wrong. it looses about 2 minutes over a 24hr period. uhg.
 
I am not sure what value the " .2 " equates to in your code in the "Delay02" subroutine. Is it hex or decimal?

DELAY01:.....DECFSZ DEL,1
.....................GOTO $-.1
.....................CLRF PORTB
.....................RETURN
DELAY02:....MOVLW .2 <-- no sure what this value equates to
.....................MOVWF DEL01
.....................DECFSZ DEL01,1
.....................GOTO $-.1
.....................RETURN

In your original code, the DEL variable looks as if it is initially set to zero. Add the first two lines of code in the 'DELAY01' routine below to set the DEL variable to different values and see how it affects the timing.

DELAY01:...MOVLW XX <-- Set 'DEL' variable here
....................MOVWF DEL
....................DECFSZ DEL,1
....................GOTO $-.1
....................CLRF PORTB
....................RETURN

DELAY02:....MOVLW .2 <-- no sure what ' .2 ' value equates to here.
.....................MOVWF DEL01
.....................DECFSZ DEL01,1
.....................GOTO $-.1
.....................RETURN
 
Last edited:
I took another look at your program. I found the below code is a part of the main routine and uses Timer-0 (T0) as part of the call back to the interrupt service routine. Below is where it sets up the prescaler that changes the timing for timer zero. You can try changing the prescaler value and see if that also helps.

MOVLW B'00000011'................<---prescaler value for timer zero.
MOVWF OPTION_REG............<---this is where the value goes in to set the prescaler.
BSF INTCON,T0IE.....................<----and this is where the timer is turned on.
BSF INTCON,GIE

Just a thought.
Good luck.
 
Blanking hour10:
What you could do is to add 10 to the value in W in the H10 routine, and double(copy) the number of entries in the table from 10 - 19, making the '10' = B'00000000'

This way you don't need to test anything. When the H10 value is 1, you get value 11 from the table.

TOK ;)
 
Last edited:
Top