10 Keys on One Port Pin?

I'm working on a control system that will have several relays, a bunch of LEDs and a number of switches.  I was rapidly running out of port pins on a TAP-28 board to handle all the desired I/O.  Possible solutions included adding an I2C port expander such as the Microchip 23018, but this was an additional complexity to the already-expanding project.


Reading the switches seemed like the easy place to save some port pins.  The control system needs at least 5 switches but maybe more.  These switches are momentary contact to set various options or to initiate actions, and only one will be pressed at a time.  Borrowing from Graham's idea to use a pot (variable resistor) as a multi-position switch and my post on an idea to use an analog input to read switches and a pot, the answer seemed pretty simple.  Use an analog input and a resistor network to read a series of switches.  I made a prototype with 10 switches but the concept could be greatly expanded.

The schematic is shown below.  Ten resistors form a multi-tap voltage divider between +5V and ground.  I used 440Ω resistors for each of these (actually, I used 2  220Ω resistors in series for each because of the format of my prototype board).  Each switch connects a different tap to an analog input, resulting in a different analog voltage for each switch.  I added an additional resistor to pull the analog value up to +5V when no button is pressed.  Its value is about 100x the value of the other resistors.


The resistors add almost nothing to the cost of the switches.  If the switches have leads spaced on 0.1" centers, building a prototype on a piece of perf board is simple.



The Software

The resistors I used are 5% tolerance.  If the resistors were perfectly matched, the expected values would be 0%, 10%, 20% and so on of the full scale reading.  Because the resistors aren't matched, the actual voltage values that will be measured vary around the expected values.  I used a case-select statement to break the readings into 10 zones.  The listing below is in Swordfish Basic - converting to other languages should be straight forward.

Keypad Test (Swordfish PICBasic)
'keypad test
 Device = 18f25k20

Clock = 20
Include "USART.bas"
Include "convert.bas"
Include "ADC.bas"
Include "math.bas"
Dim ADVal As Word
Dim ADVal1 As Word
Dim Button As Integer
Dim ButtonWas As Integer
Function ADInAsmVolt() As Word
result = (ADC.Read(1) + 1) * 5000 / 1024
End Function
ADCON1 = %10000000
ADCON0.7 = 1
ADCON0.6 = 1
ADC.RightJustify = true
While 1=1
ADVal = ADInAsmVolt
ADVal1 = ADInAsmVolt
If abs(ADVal-ADVal1) < 25 Then 'repeatable read
  Select ADVal

Case < 250
Button = 1
Case < 750
Button = 2
Case < 1250
Button = 3
Case < 1750
Button = 4
Case < 2250
Button = 5
Case < 2750
Button = 6
Case < 3250
Button = 7
Case < 3750
Button = 8
Case < 4250
Button = 9
Case < 4700
Button = 10
'no button pressed
  Button = 0

End Select
'invalid read

If Button <> ButtonWas Then
If Button <>0 Then
USART.Write("Button = ", DecToStr(Button),13,10)
'don't print 0

ButtonWas = Button


When I tried the initial version of the software, I would get an erroneous reading occassionally - a higher number button than the button actually pressed was indicated.  What was happening was that the ADC read was only catching part of the button press, resulting in a ADC value greater than it should have been.  I added the statement to require two consistent reads in a 50 µs period to be a valid read.  This completely eliminates erroneous readings and doesn't make reading the keys feel sluggish at all.

The approach will be very stable.  The ADC and the voltage divider are both referenced to supply voltage, so the output is ratio-metric and independent of supply voltage.  The resistors, in close proximity, will be subject to the same thermal drift and the measurement increments are large so drift won't be an issue.

The maximum number of buttons used is limited by the ADC resolution and the tolerance of the resistors.  I'll leave it as an exercise for the reader to determine the range of values for a given voltage divider tap, given that each resistor may be ±5% from its rated value.



Ten keys. 

One port pin. 

Extra cost almost nothing. 


Posted: 3 years 6 months ago by Anonymous #5501
Anonymous's Avatar
There a little something you should think about if you hold down s1 for very long
your sinking 36 ma your 100ohm resistor should be 220 ohm I would use a 440 like the rest.
Posted: 3 years 6 months ago by Anonymous #5502
Anonymous's Avatar
My drawing is a little unclear Burt. The 10 resistors in the voltage divider are 440 ohms. The resistor at the top (100R) should be labeled 100xR. It's actually 47k, not 100 ohms as it might appear. It's function is only to pull the "unpressed" condition high.

Thanks for the comment.
Posted: 3 years 6 months ago by Anonymous #5503
Anonymous's Avatar
I came across an old wired VCR remote control that uses this technology. The picture shows 5 buttons and a slide switch that are connected to the VCR with a single shielded cable on a 1/8" phone plug.

Forum Activity

Member Access