Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

Reading Multiple Buttons with one IO Pin

  1. Jan 14, 2010 #1
    Hello All,

    I have been working on this problem for a few hours now and it's really beginning to bug me. I did a search on Google for methods of monitoring multiple push-buttons (tact switches in my case) using a single analog IO pin from a microcontroller. So far, I have come across a few useful examples of how it can be done.

    This first link seems to show the simplest way to go about it (with the fewest components):

    I would just go with this here except for the fact that I need to monitor 10 buttons rather than 7. I have no idea how the guy calculated the resistor values. According to the page, they were selected so that the the microcontroller can also tell if multiple buttons are pressed simultaneously.

    The second site I found on this was here:

    The third post on this page shows a text representation of an R2R ladder setup to do the same thing. I'm assuming that this will give unique voltages for any combination of switches just like the first example, however, I definitely would prefer to go with the first setup if I can to keep the component count to a minimum. Would anyone here happen to know anything about this and/or how determine the resistor values if you want to do a single series string like the example in link 1?

    Jason O
  2. jcsd
  3. Jan 14, 2010 #2


    User Avatar

    Staff: Mentor

    (puts on uC customer support hat)

    Is there a reason that you want to use a single analog input as an IO expander? Do you instead have a 2 digital pin I2C port on your uC that you could do traditional IO expansion with?

    (hat off)
  4. Jan 14, 2010 #3
    Hi Jason,

    One of my mentors taught me this trick for keeping track of board configuration. You essentially pull the analog line high with a 1% to reference and then you leave the line open, pull down with a resistor, or short to ground on the board you're tracking.
    This way, your software can do a quick check to ensure that it's compatible and perhaps, evoke different behaviors to coincide with the various releases of boards.

    Generally, I never used the technique for over eight configurations because of the concerns with voltage drops on the ground, resistor value errors, and noise. With a dedicated ground trace back to the analog ground and a bypass cap between the line and analog near the processor (note 1), you should be able to push it one more bit.

    To start, your processor will get in a value for the A/D. Typically 8 or 10 bits.
    Let's say you have an 8 bit converter. For each configuration, you'll expect to get back one of sixteen values that are in the given ranges:

    Configuration => range of returned values:
    0 => 00h - 0Fh
    1 => 10h - 1Fh
    2 => 20h - 2Fh
    14 => E0h - EFh
    15 => F0h - FFh

    Due to noise, grounding errors, and resistor value errors, you can expect to get a range of values back, but you design for the value in the center.

    For example for configuration 3, I expect to get 30h - 3Fh as my range of values from the A/D. My nominal value is half way between, or 37h. Thus I can accommodate +/- 7 counts and still get the correct result.

    To determine your resistor values, begin by assuming pull up resistor, R_pu. I typically use 1% 10K, but you may wish to consider a 10K, .1% since your going for 16 values and you don't want stack up to be an issue.

    Next, for each of the "nominal values," such as 47h, use this equation:
    R_pd = R_pu nom_count / (2^n - 1 - nom_count).

    To test the nominal count, use your actual resistor value, R_d into this equation:
    nom_count = (2^n - 1) R_d / Ru+Rd

    For Config = 4, the nominal A/D value is 47h (71 decimal). I'll use 10k for Ru, which gives R_d = 3.86k. 3.83k is the closest stock value, so I'll pop it into the test equation to get 70.6 decimal or about 47h.

    Anyway, this gives you the resistor values. During actual operation, shift the lower four bits into the bit bucket to tell which switch was pressed.

    - Mike

    Note 1 - Running your analog trace out to the front panel and then tying it to a ground at the processor won't win any awards for CE marking the product. Instead, you may wish to consider a 4.7k - 10k resistor between node where Pu and Pd tie together and the A/D. Then, tie a 4.7nF to 10 nF cap to analog ground at the A/D side.
    Likewise, remote routing of the analog ground to prevent load-based drops may present an EMC issue. A 100 - 200 ohm VHF ferrite bead with a parallel resistor of about it's own rating can help.
  5. Jan 14, 2010 #4


    User Avatar
    Science Advisor

    You could try something like this:

    switch detection.PNG


    There are many constant current source circuits.
    If you got one that would produce 1 mA, the top switch shown would produce 0.2 volts to the processor if closed.
    The next would be 0.4 volts, then 0.6 volts 0.8 volts etc up to 1.4 volts. With no switches closed you wouuld get 1.6 volts. Nice regular steps.

    If you used a current of 0.909 mA you could use 220 ohm resistors which would be easier to get, but they should still be 1 % tolerance if possible.

    You can add extra switches and resistors if you like.
  6. Jan 14, 2010 #5
    Hi Everyone,


    I never thought about using an io expander (never even thought to look into that), but I wanted to conserve IO pins on the PIC I'm using which is why I thought about going for the analog input method. Though, as a possible alternative, I was thinking I could go with a parallel to serial shift register with a latch on it to read the button values. I am already using the SPI bus to communicate with other peripherals so I could just use one pin for the CS line to latch and clock the bits from it. The only problem there is I'm thinking that I would have to periodically check the latch in order to monitor the buttons (unless there is some way to check it with an interrupt, not sure there). Whereas in the analog case, I can monitor the pin for a change in voltage to trigger an interrupt to handle the button press.


    Thank you for your very detailed analysis there. I'm still trying to digest and make sense of everything that you said though. Now, in the case that you are describing, would that configuration permit you to tell if more than one button is pressed? In my case, I'm not necessarily wanting to read different combination button presses, but at least recognize when it happens and ignore it rather than the device doing something unpredictable.

    Using that method for board tracking is a cool idea, I'll definitely have to keep it in mind :-).

    As for your switch example, It sounds very similar to what Microchip is suggesting in their Application note AN234: http://ww1.microchip.com/downloads/en/AppNotes/00234a.pdf.

    Only in that case, they are using the switches to pull up the value to V+ rather than the way you mentioned.

    But even in that case, I'm not sure what resistor values to pick such that the controller doesn't get confused if more than one button is pressed... Is there a standard way to deal with this kind of situation?


    Thanks for the circuit. This one looks very similar to the one shown in the link that I posted, except for the use of the constant current source.

    I think my main issue here is determining how to handle the multiple button press issue... What do you all think? Again, I don't care what button combination was pressed, but I just don't want it to cause the system to think something was pressed that wasn't correct.

    Jason O
    Last edited: Jan 14, 2010
  7. Jan 14, 2010 #6


    User Avatar
    Science Advisor

    I don't care what button combination was pressed,

    So, if you just have a pull-up resistor at the single analog IO pin of the microcontroller and all the buttons in parallel to ground, if any switch was pressed the input would be pulled low.

    pull down.PNG

    Is that what you mean?
  8. Jan 14, 2010 #7

    This method cannot get past the multiple switch problem. Then again, the scan line technique never could get past multiple switches and it became very popular. If you can't risk an accidental misreading, then the best way I can think of is to use a PCF8575 from NXP/Ti or use a cheap processor via UART. Then run your stolen pins from the port expander.
  9. Jan 15, 2010 #8
    Hey Everyone!

    I think I figured out how to solve this problem in the simplest way :-). First of all, I realized that for my application, I don't actually need to detect various combinations of buttons so much, but simply detect ignore them when they occur so as to avoid erroneous input to the microcontroller (more on this in a minute).

    The second thing I realized is that I don't need to actually calculate the vales of each resistor explicitally such that I get an even voltage division (like the one in the first example does). I actually figured out that if I use a 10k resistor at the top of the string followed by 9 1k resistors in series (see attached image). This gives me a good string where the smallest difference between votlage values is at least 130mV, even though the votlage steps are not linear. This approach saves on parts cost since I wouldn't have to order 10 different resistor part numbers to make one like the first example.

    Edit: After making the original image, I found that using a 9k resistor gives nicer values and a greater voltage margin between each switch

    Now as for the multi button push situation, here's my solution, and it's entirely a software issue. To detect the button presses, the analog voltage signal is fed into an Analog input pin which also has a voltage change interrupt assigned to it also. When the user presses a single switch, the voltage will first read the value of the button that was pressed. If a second button is then pressed while the first button is pressed, the voltage will change to some value between 0 and 2.37V. If the voltage does not go back to 5V (as in the user let up on the single button), then the processor simply ignores the voltage change signal and waits for a 5V reading to come back. Once the user lets up on the button, then the interrupt is fired and the state is reset so it can receive another button press :smile:

    That's my proposed solution. What do you all think?

    - Jason O

    Attached Files:

  10. Jan 15, 2010 #9


    User Avatar
    Science Advisor

    Missed one. If you push the second button from the top down, you get 0.454 volts. 1/11 of 5 V.

    If you hold a button down and then push any button below it, it won't make any difference to the voltage fed into the CPU.
  11. Jan 15, 2010 #10
    Haha thanks, I did forget to add that one to the list for some reason :smile:
  12. Jan 15, 2010 #11
    I think you should consider each switch in series with a unique resistor, with the resistors in a binary sequence (10 ohms, 20, 40, 80, 160 etc) and all resistors attached in parallel to a bus which is tied to a series resistor to Vcc. Then any voltage you read off the bus is a unique combination of each and every combination of push buttons. If the voltage on the bus = Vcc, then no pushbuttons are pushed. A series resistor string will not give a unique output for every combination, as already noted.
    Bob S
Share this great discussion with others via Reddit, Google+, Twitter, or Facebook