In the last two projects we used special variables for each displayed digit. This is, however, not always possible, so a procedure is needed to figure out the digits of a given number n. If we concentrate on the rightmost decimal digit n0 then it is obvious that n0=n%10, the remainder of division of n by 10. As soon as the rightmost digit is figured out, to compute the remaining digits we can apply a similar procedure to n/10 (integer part). However, PIC16F84A does not have division in its instruction set, so we need to implement it by using addition and subtraction only.
The demonstration program is just a counter from 0 to 255 in about 360msec intervals. This sequence of 256 numbers is repeated periodically. The LED display is LTC-4724JR, all whose segments for each digit have a common cathode. We use the same technique of time multiplexing for displaying multiple digits. This time, however, displaying of a digit is provided by outputting a 1 to the corresponding pin of PORTA. Furthermore, there is no need to invert the digit codes in array segments within the displayDigit() procedure.
| Schematic | Layout | Segm. codes | ||
![]() |
Each digit on the LED display is active for 8 milliseconds resulting in the display update period of 24 msec. This exceeds the safe amount of 20msec for which the human eye can remember the images and results in a slight flickering of the display. To get rid of this each digit should be displayed for 7 or even better 6 msec.
In the main loop we display 3 digits of the counter variable count running between 0 and 255. Since PIC16F84A supports only one byte operations, the counter updates itself upon reaching the maximum value of 255. The digits of count are displayed from the rightmost (LSB) towards the leftmost (MSB) corresponding to d=1,2,4, respectively. To display a digit with index d this value must be output to PORTA. Procedure div10() takes n as a (global) parameter and returns n=n/10 and r=r%10. The numbers r and d are passed to displayDigit() function that takes care on fetching the digit's code from the memory and organizing necessary delays.
To implement the division we apply the "High-school" method. Since the dividend n is a 1-byte number and the divisor 10 takes 4 bits in the binary representation, the division is performed in 8-4+1=5 iterations of the main loop. At each iteration of this loop we get one of 5 bits of the quotient. The entire procedure runs in about 45 CPU cycles, which is comparable with the one in Intel Pentium CPU family. The rest of the code is similar to the one in the previous experiment.
TITLE "7seg3.asm" ; displaying 0,1,...,255 on a triple digit LED
List P=16F84a, R=DEC
INCLUDE "p16F84a.inc"
; data segment
CBLOCK 0x00C
del, n, r ; variables for passing the parameters
count ; number to display
temp, cnt ; local variable
d ; currently processing digit (1, 2, or 4)
s ; counter update speed (360 ms)
segments:10 ; 7-seg codes array
ENDC
; code segment
PAGE
__CONFIG _CP_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC
org 0 ; start program at the beginning of mem
bsf STATUS, RP0 ; change to BANK 1
clrf PORTA ; turn off all digits
clrf TRISB ^ 0x80 ; enable RB1-RB7 for output
movlw 0x18
movwf TRISA ^ 0x80 ; enable RA0-RA2 for output
movlw 0x7F ; enable internal Pull-Ups
movwf OPTION_REG ^ 0x80
bcf STATUS, RP0 ; back to BANK 0
; initialization
call initSegments
clrf count ; start counting with 0
movlw 15 ; set up counter update speed 360ms
movwf s ; 3*8*15 = 360ms
; main loop
loop
movf count, w
movwf n ; copy count to n for processing
clrf d ; start displaying the rightmost digit
incf d, f ; d = 1
call div10 ; computing n = n/10 and r = n%10
call displayDigit
incf d, f ; do the same with the middle digit
call div10
call displayDigit
incf d, f ; ... and the leftmost one
incf d, f ; (its d-index is 4)
call div10
call displayDigit
decfsz s, f ; time to update the counter?
goto loop ; NO - display same digits n1,n0 again
; update the counters
movlw 15 ; YES - update
movwf s ; restore s
incf count, f ; count++
goto loop
; procedures
displayDigit ; display digit r at position d
movf d, w
movwf PORTA ;start displaying the digit #d
movf r, w ; get parameter to w
addlw segments
movwf FSR ; FSR is a pointer to the digit code
movf INDF, w ; w = segments[digit]
movwf PORTB ; light on the digit
movlw 8 ; a 8ms delay
movwf del
call delay
return
div10 ; computing n = n/10 and r = n%10
movlw 5
movwf cnt ; cnt = 5
movf n, w
movwf r ; r = n
clrf n ; n = 0
movlw 0xA0
bsf STATUS, C
subwf r, f ; loop that executes 5 times
btfsc STATUS, C
goto $+3
addwf r, f
bcf STATUS, C
rlf n, f ; C = bit of n/10
movwf temp
rrf temp, f
movf temp, w ; shift right W
decfsz cnt, f ; div is done in 5 loop iterations
goto $-10 ; end of loop
return
initSegments ; set up 7-seg digit codes
movlw b'11011110' ; gidit 0
movwf segments+0
movlw b'00011000' ; digit 1
movwf segments+1
movlw b'11101100' ; digit 2
movwf segments+2
movlw b'01111100' ; digit 3
movwf segments+3
movlw b'00111010' ; digit 4
movwf segments+4
movlw b'01110110' ; digit 5
movwf segments+5
movlw b'11110110' ; digit 6
movwf segments+6
movlw b'00011100' ; digit 7
movwf segments+7
movlw b'11111110' ; digit 8
movwf segments+8
movlw b'01111110' ; digit 9
movwf segments+9
return
delay ; a delay for del milliseconds
movlw 200
sublw 1 ; this loop takes 5us*200 = 1ms
sublw 0 ; for PIC16F84A @ 4 Mhz
btfss STATUS, Z
goto $-3
decfsz del
goto delay
return
end
|
Download 7seg3.asm
Last modified:Mon, Jan 23, 2023.