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.
Doug Rice, 13th July 2001.