Variable resistance measurement method
To measure the resistance of a Joystick or 'angle sense' potentiometer (or the resistance 'set' by any combination of up to 8 switches - or one of up to 100 push buttons) only one PIC pin needs to be used.
The pin starts as an output and is used to charge a capacitor to '1' (Vdd-0.7 = 4v3). A current limit resistor (typ. 180R) prevents 'infinite' current draw when charging up the capacitor up from '0'. The potentiometer/switches to be 'sensed' are connected between the capacitor and ground so as to discharge the capacitor. To ensure the 'discharge' resistance is 'large' compared to 180R (so that the capacitor will charge up to (almost) logic '1', making the calculations easier) a 1k resistor is added to the discharge path. Once the capacitor is charged, the PIC i/o pin is switched to 'input mode' and the PIC uses it's using the counter/timer to time how long it takes for the variable resistance to discharge the capacitor to a logic '0' (0v6 typical, although if high precision is needed a PIC with an internal 'comparator' internal Vref may be used instead).
Multiple buttons can be 'sensed' in the same way, with each button connected to a different value resistor. To detect a combination of multiple switches, each switch needs a resistor that is a binary multiple of the next
To 'detect' a voltage level, the PIC CPU has to 'sample' the pin state and 'loop on not found'. This takes time - as does checking the counter/timer 'count'. To minimse the effect of errors due to the 'loop time', the capacitor discahreg sould be 'long' (comparer to the CPU clk). This means setting the pre-scaler to it's maximium divide (1/256)
At 1MIPS, a 1/256 pre-scaler divide means counter is clocked at at 3.90625 kHz (or 0.256 mS per step). The max. timer count is 255, so (without any mucking around) we can measure times up to 65.28mS.
Typical joystick and axle angle sense potentiometers are 5k and 10k. To discharge the capacitor well within 64mS, we get capacitor values of 4u7 and 2u2 respectively.
When sensing push-buttons and switches, it's a 'good idea' to 'maximise' the resistance 'difference' between switches. Thus, if you have (say) 2 buttons, they can be 10k and 1k, but if you have 10 buttons, you have 10 steps of 1k (so 10k, 9k, 8k (and so on) down to 1k) = whilst 20 would be on 500R steps. When using switches, since more than one can be 'set' at the same time, the switch resistances must be binary weighted. Four switches would be 10k, 4k9, 2k5, 1k4, however any more start to become 'significant' compared to the 180 'charge up' resistor making it harder to detect them. Adding 2 more switches (nominally 680, 330R) is likely to need 'simulation' (using either a formula heavy spreadsheet or SPICE)
The data below was derived from this calculator for capacitor timings
Charge up time for 2u2 is approx 1mS (so 2.1mS for 4u7) from 0v6 to about 4v0 (driven by 4v3 '1'). Discharge time to 0v6, via 11k, for 4u7 is 98mS (for 2u2 it's 46mS), via 6k 54mS/25mS and via 1k it's 4.2mS and 9mS. The maximum pre-scaler divide on the counter/timer is 256, and at 1MIPS, that clocks the counter at 3.90625 kHz (or 0.256 mS per step). The max. timer count is 255, so (without any mucking around) we can measure times up to 65.28mS (so, with 10k variable resistance, we need to stick to the 2u2 capacitor) It's of note that the longer we 'charge up' the capacitor, the higher the 'start' voltage (i.e. the closer it gets to '1' = 4v3) - and the longer the capacitor is left to discharge (via the variable resistor) the closer it will get to 0v (which means it will take longer to reach the logic '1' start voltage). To eliminate this variance, the PIC pin will be set to output a logic 0 (0v6) between measurement cycles. This will hold the capacitor at a 'known start' of 0v6 prior to the charge up / discharge cycle measurement cycle
So (assuming no internal Vref is used) the 'measure' subroutine is :-
Start: Set pin o/p = '1', clear timer counter, loop waiting for count 4. TRIS pin as input, loop waiting for pin = '0' (or timer count = 0 i.e. timed out) Save count, set pin o/p = '0', TRIS pin as output, exit
Note I don't bother to reset the timer/counter after 'wait 4', which saves a bit of time when sensing 'no switches set' (i.e. when capacitor doesn't discharge at all and the sense subroutine waits for the timer/counter to loop back to 0). When sensing potentiometer values, it will take no more than 46mS (179 counter clks) to discharge 2u2 via 11k, so 'no problems' even with 5% resistors and 20% capacitors.
However, when sensing the differing resistance set by 'option' switches (or buttons) we need to be more careful. In theory, we can detect counts up to 250 = 64mS discharge time. This gives us about 15k as the maximum binary resistance = which takes 62mS to discharge, whilst 16k would take 66mS (too long to detect, even before taking into account resistor and capacitor tolerances).
Potentiometer '0' (and max) calibration
Typically, any project using a potentiometer as a 'sensor' won't allow it to run into the 'end stops'. This means it won't ever get to 0R (and never reaches 10k or 5k etc).
So a 'calibration' step is required to find the '0' (and maybe 'max') values. In fact, it's likely calibration will be needed anyway, due to component tolerances and supply voltage variations. Depending on the project, usually the potentiometer can be set to the 'zero' position before power is applied, so '0' calibration can be performed after a 'power-on' delay of 2 or 3mS whilst the capacitor charges from 0v to it's 'normal' low of 0v6 (2u2 charged from 0v via 180R will reach 0v59 after 1.6mS, and 0v599 after 2.6mS) If a 'max' potentiometer position calibration is needed (for example, for Alt-Az telescope mount), the PIC will need to wait for some indication (eg button press**) from the user to indicate when the system has been moved to the 'max' potentiometer position **If the potentiometer won't ever reach 0R, then a button that 'shorts' the pot. (to 0R) can be used as the 'indicator'
An alternative method needing no push button is for the PIC to keep sampling and (1) wait for the 'zero' value to change (at which point we assume the user to be moving to the 'max' position) and then (2) wait for the value to stop changing (at which point we assume the user has reached the 'max' position)
Next page :- 16 keypad 4x4 matrix - (using one pin)