PIC Projects

Division by 10

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
matrix

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.

06445