# Multiplexing Inputs on a microcontroller

I have a 4 way DIL switch that I use to provide input to a microcontroller, and I need to free up a port to use for something else.

Is there a way to multiplex 4 inputs into 3 microcontroller ports? I'm thinking that there may be some way to have 2 pins as inputs, and a 3rd output pin that can be driven high to read switch 1 and 2, and low to read switch 3 and 4.

My current simple design is shown below, can I do this with just 3 pins?

Charlieplexing takes advantage of tristate pins of the microcontroller, but it costs extra diodes and somewhat more complex programming.

With N IO-pins you can address N(N-1) switches / LED's.

The Charlieplexing with switches article describes how it works with switches and LEDs

• This is great, exactly what I was after... I've heard of charlieplexing with LED's but didn't think about using the same method for switches as well. Thanks. – BG100 Aug 7 '12 at 20:07

What you describe is a 2-to-1 multiplexer, like the 74HC157, which is a quad 2-input multiplexer, so 2 inputs to 1 output, and 1 select input. You use two multiplexers for the four inputs.

The 74HC153 does a similar thing, but it's a dual 4-to-1 multiplexer, so 4 inputs, 1 output and 2 select lines per multiplexer. Personally I find that a more logical solution for your application.

Connect the enable input to ground, and don't forget to connect unused inputs either to ground or Vcc.

• Thanks for your answer, but I prefer the charlieplexing method, mainly because I already have the diodes and I'd have to order new parts to do this. – BG100 Aug 7 '12 at 20:09

As an alternative to Steven's solution you could use a 2:4 demux like this:

• Aha! Nice picture, but what are those ugly little triangles? ;-) – stevenvh Aug 7 '12 at 18:34
• @stevenvh For reference, those ugly little triangles are the diodes that will avoid burning the demux outputs when more than one switch is on. :-) By the way +1 for your answer. – Bruno Ferreira Aug 7 '12 at 18:37
• I was just teasing. I've heard they're more expensive than pull-down resistors. Did you hear that too? :-) – stevenvh Aug 7 '12 at 18:39
• @stevenvh Yes, I also head that. I always seem to find the harder solution first. Sorry for the typo. – Bruno Ferreira Aug 7 '12 at 18:41
• The way you drew it you'll need a demux with active-high outputs. You could use a 74HC238, which is the active-high version of the more common 74HC138. Apparently there's no active-high version of the 74HC139, which would be a better fit. At least I couldn't find one. – stevenvh Aug 7 '12 at 18:53

Great solution by jippie. I'd like to elaborate a bit on it.

The schematic:

The idea is to make one of the I/Os output and the other two input which allows you to read the state of two buttons. Three times output $\times$ two inputs is good for 6 buttons as the schematic shows.

How do I activate the output? Make it high? Let's do it for pin 1, then diodes B and F are forward biased, so we'd expect to be able to read those buttons. For many microcontrollers this won't work. Pressing button B will make input 2 high, but what if the button isn't pressed? The input would be floating, and then you can't read anything meaningful on it. A pull-down resistor would help, but many microcontrollers only have pull-up resistors, and then you'll never read a low level. I don't know about all of them, but at least a number of AVR and PIC microcontrollers only have pull-ups.

In that case the right way is to activate the internal pull-ups and activate the output by making it low. We're not controlling buttons B and F, but A and E. If button A is not pressed the pull-up will make input 2 high. Press button A and you pull the input low.

The algorithm:

IO2 = input, pull-up enabled
IO3 = input, pull-up enabled
IO1 = output, low
Button_A = IO2 (low = pressed)
Button_E = IO3 (low = pressed)

IO1 = input, pull-up enabled
IO2 = output, low
Button_B = IO1 (low = pressed)
Button_C = IO3 (low = pressed)

IO2 = input, pull-up enabled
IO3 = output, low
Button_F = IO1 (low = pressed)
Button_D = IO3 (low = pressed)


As far as I know all NXP Cortex-M controllers, for instance, have both configurable pull-up/pull-down resistors. For those you can use positive logic (high = pressed) if you use the pull-downs, and an active high output. Note that you will read different buttons for the same output:

IO2 = input, pull-down enabled
IO3 = input, pull-down enabled
IO1 = output, high
Button_B = IO2 (high = pressed)
Button_F = IO3 (high = pressed)

IO1 = input, pull-down enabled
IO2 = output, high
Button_A = IO1 (low = pressed)
Button_D = IO3 (low = pressed)

IO2 = input, pull-down enabled
IO3 = output, high
Button_E = IO1 (low = pressed)
Button_C = IO3 (low = pressed)

• Nice explanation. – jippie Sep 6 '12 at 3:04

If you can rely upon your I/O pins to switch at the same threshold, and you don't mind disabling interrupts for a long time while you read the switches (e.g. because you just read them at startup), you could use two I/O pins, six resistors, and a capacitor. Wire a 1K, 2.2K, 4.7K, and 10K resistor in parallel with each switch, and wire all the switches in series so that the chain will have total resistance from roughly 0K to 17.9K. Put a 1K resistor in series with that chain and tie it one end of the chain to a port pin and the other end to a capacitor. The other end of the capacitor should go to ground. Wire a 10K resistor from the other port pin to that capacitor.

To determine the switch setting, ground both port pins and leave them grounded for awhile. Then float the one connected to the string of resistors and drive the one connected to the 10K resistor. Time how long it takes for the pin connected to the resistor string to go high. Then ground both port pins, leave them grounded for awhile, and float the pin connected to the 10K while driving the resistor-string pin high and time how long it takes for the 10K pin to switch.

If the pins switch at equal voltages, the ratio of the times will be the ratio of the resistances. Since the 10K resistance is known, one can then compute the other.

It may be possible to improve accuracy slightly by using a third pin connected directly to the capacitor to determine when it is suitably charged; using the same pin for both measurements would ensure that both measurements are taken with the same switching threshold.

• Interesting answer, I'd never have though of doing it this way. The programming might be a little tricky though, but thanks for your answer. – BG100 Aug 7 '12 at 20:13
• or, you know, use the built-in a/d and skip all the complication – Kristoffon Aug 8 '12 at 2:14
• Yeah yeah. Though the RC approach does have the advantage of having a time proportional to resistance, whereas the ADC approach will generally yield a non-linear voltage with respect to the switch setting (one could achieve closer to linear voltage if the pull-up resistance is large relative to the pull-down resistance or vice versa, but that would generally squish things into a small portion of the ADC range). – supercat Aug 8 '12 at 15:00

I noticed that you have an analog input there. If you want to use a minimum amount of components connect one side of all switches to the analog input then connect the analog input to +V with a 1K resistor. Connect four different values resistors to the other side of the switches. Choose your resistors carefully to ensure that you get a different voltage regardless of how many switches are on at one time. Generate an interrupt when A/D conversion value is changing or have a routine that reads the value as often as you require.

• I believe that this is the most inexpensive solution to your problem. It only requires five resistors. – Gregory Ion Aug 8 '12 at 6:33
• Given that the 4 switches effectively make up a 4-bit binary number, would this give enough resolution (with 1% resistor tolerance) to allow me to reliably detect 16 different switch combinations? – BG100 Aug 9 '12 at 11:09

My approach would be to use a serial bus I2C/SPI/etc.. You could run that on a 3 pins and have an input register for the switches on that bus. While expensive and more bits that you need currently, the MCP22017 is pretty flexible. Other choices with 8 bits are available if you look around.