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 Device = 18f25k20Clock = 20 Include "USART.bas"Include "convert.bas"Include "ADC.bas"Include "math.bas" Dim ADVal As WordDim ADVal1 As Word Dim Button As Integer Dim ButtonWas As Integer     Function ADInAsmVolt() As Word    result = (ADC.Read(1) + 1) * 5000 / 1024End Function ADCON1 = %10000000ADCON0.7 = 1ADCON0.6 = 1ADC.RightJustify   = trueADC.SetConvTime(FOSC_16) USART.SetBaudrate(br9600)  While 1=1     ADVal = ADInAsmVolt    DelayMS(50)    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            Else                'no button pressed                   Button = 0        End Select        Else        'invalid read      EndIf     If Button <> ButtonWas Then            If Button <>0 Then             USART.Write("Button = ", DecToStr(Button),13,10)        Else            'don't print 0          EndIf        ButtonWas = Button    EndIf Wend`

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.

Conclusion

Ten keys.

One port pin.

Extra cost almost nothing.

Win.

Posted: 8 years 7 months ago
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: 8 years 7 months ago
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: 8 years 7 months ago
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

• No posts to display.