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.