Using a PIC16F84 to read the position of a pot and scale it to 0 < v < max

The routines described here read the position of a potentiometer by a ratio technique. The measured value is scaled to a value between 0 and max, using a successive approximation technique.

The two halves of the pot are measured and the two measured values are added togther to get the range. These values vary due to component tolerences and variation in supply voltage. However these are greatly nullified by the scaling routine.

Starting with the measured value of range, it is itterated down to the measured value of B. The same itteration decisions are applied to Max value, resulting in value so that:

value = MaxValue * B / range

Tests show that the ratio: ( value / position of Pot ) can be much more accurate than the component tolerences would suggest. The values of A and B are measured by the circuit below.

After discharging the capacitor by making RTCC/RA4 a low output, the capacitor is then charged by making RTCC/RA4 an input and either RA2 or RA3 is made an output set to logic 1, ( The other pins are made inputs. ) A counter is started and when RTCC / RA4 gets to logic 1, the counter is sampled.

The RTCC or RA4 is an input with threshold of 0.8Vdd, so gives the longest count for a given Time constant.

The code to read the pot is given below:

It is run on a PIC16F84 clocked at 32K678 using a watch crystal.


;--------------------------------------------------------------------------
; Sec 4.1	RC measurment routine
;--------------------------------------------------------------------------
;				
;
;    GND --| |--[ 200R ]--+---[ R2 ]---- RA2
;	    C 	          |
;		          +---[ R3 ]---- RA3
;		          |
;		          +---RA4/TOCLKI
;
; R2 and R3 form the two sides of a pot.
;	---[ R2    R3 ]---
;                ^
;                |
;                +--------
;
; To measure CR enable either RA2 or RA3  as a high output and RA4 as an input. 
;
; Set RA2 or RA3 to start charging C, use the input to see if it has got to logic 1.
;

	cblock
RCresultR2
RCresultR3
	endc


; start
RCmeasureR2
	BSF	STATUS, RP0
	MOVLW	~7
	MOVWF	TRISB		; Set output high to start Measurement
	BCF	STATUS, RP0
	MOVLW	~1
	MOVWF	PORTB		; Set output high to start Measurement

	; Make RA4 an output and zero it to discharge C
	MOVLW	~( 1 << 4 )
	TRIS	PORTA

	CLRF	PORTA

	CLRF	RCresultR2
	MOVLW	~( 1 << 3 )
	; count how long it takes to charge C
	CALL	RCmeas2
	MOVWF	RCresultR3

	; Make RA4 an output and zero it to discharge C
	MOVLW	~( 1 << 4 )
	TRIS	PORTA
	;	leave RCresultR2 then measure R3
	CLRF	PORTA

	; Make RA3 an output and set it to charge C
	; put the result of A into RCresultR3
	; Keep adding to RCresultR2 to measure R3+R2
	; so do not use:
	;  CLRF	RCresultR2
	MOVLW	~( 1 << 2 )
	CALL	RCmeas2

	MOVLW	~( 1 << 4 )
	TRIS	PORTA

	CLRF	PORTA		

	BSF	STATUS, RP0
	MOVLW	~7
	MOVWF	TRISB		; Set output high to start Measurement
	BCF	STATUS, RP0
	MOVLW	~4
	MOVWF	PORTB		; Set output high to start Measurement

	; RCresultR2 = A+B = range
	; RCresultR3 = B 

	RETURN

RCmeas2
	; W = 0b11110111 or 0b11111011 to make RA3 or RA2 an output.
	TRIS	PORTA
	MOVLW	0xFF
	MOVWF	PORTA		; Set output high to start Measurement

RCmeasR2Loop	
	BTFSC	PORTA,4
	GOTO	RCmeasureR2End
	INCFSZ	RCresultR2
	GOTO 	RCmeasR2Loop
RCmeasureR2End
	DECFSZ	RCresultR2 	; limit max
	MOVFW	RCresultR2
	RETURN

The code above leaves RCresultR2 := A+B := Range and RCresultR3 := B

These values are affected by the component tolerences, so it is unlikely that the values can be used directly. You cannot print a scale on a box, if ( value / position of pot ) is not repeatable. It needs to be scaled to a value between 0 and Max Value. If B > range, then value will limit to outputRange.

So B / Range = Value/Max Value.

The measured values are affected by the component tolerences Kc,Kpot,KVdd which multiply the results by k = Kc*Kpot*KVdd, however, these tolerences should cancel out.

To preserve accuracy, ensure the measured value of ( A + B ) is between 128 and 255.

Note that the code stops RCresultR2 going greater than 255.

kB/ ( kA + kB ) = kA/k(A+B) = A/(A+B)

The next bit of code calculates value, not by doing A/Range * Max count, or A * Max Count/ Range. It uses a successive approximation technique to find the ratio A/(A+B), but at the same time uses the conditional adds of successive halvings of MaxValue to build up value.


;--------------------------------------------------------------------------
; Sec 4.2	Scale output = R3 /( R2 + R3 ) * OutputRange
;--------------------------------------------------------------------------
;				
; JAL code:-
;itteration   = 0
;-- set output range to half of required range, want a value between 0 to 300.
;outputRange  = 150 
;output       = 0
;
;    for 8 loop
;	range   = range / 2
;       nextTry = itteration + range
;	if ( measured >= nextTry ) then
;                -- keep next  try
;		itteration = nextTry
;		output     = output + outputRange
;	end if
;	outputRange = outputRange / 2
;    end loop
;measured = measured + 1	
;range = measured + measured 	
;end loop

	cblock
MMmeasA
MMitteration
MMnextTry
MMoutNextTry
MMoutputhA
MMoutputlA
MMprecision
	endc
		
MRcalcABdivC_A
	MOVFW	RCresultR3	; use R2 as measured vaule
	MOVWF	MMmeasA
	CLRC			; Add R2+R3 to give range
	MOVFW	RCresultR2
	; no need to add R3  to R2 as R2 already if R3+R2
;	ADDWF	RCresultR3,w
	MOVWF	MMnextTry

MRcalcABdivC2
      
	; set to 150, not 300 to save 16 arithmatic
	MOVLW	.150
	MOVWF	MMoutNextTry	; this is divided by 2

	CLRF	MMitteration
	CLRF	MMoutputhA
	CLRF	MMoutputlA
	movlw	8
	movwf	MMprecision
MMcalcLoop2	
	clrc
	rrf	MMnextTry,f
	;	MMrangeT needs to be 300 seconds which is > 255 so divide by 2 now
	; calc  if MMmeas > MMitteration+MMnextTry/2	
	clrc
	movfw	MMnextTry
	ADDWF	MMitteration,w
	SUBWF	MMmeasA,w	; w= f-w
	BNC	MMcalc2 
	BZ	MMcalc2 

	; work out new nextTry for meas
	movfw	MMnextTry
	addwf	MMitteration,f
	; Slave Calc the Timer value
	movfw	MMoutNextTry
	addwf	MMoutputlA,f
	BNC	MMcalc2 
	incfsz	MMoutputhA,f
MMcalc2
	clrc
	rrf	MMoutNextTry,f
	decfsz	MMprecision,f
	goto MMcalcLoop2

	; MMoutputlA and MMoutputhA

	RETURN


The routine has been tried out and works really well.

Test Results... .

first page...

Doug Rice, 13th July 2001.