Advertisement
The ATMega168 is a great general purpose 8-bit AVR microcontroller from Atmel. It has 23 GPIO pins, but sometimes (as I have found) you can run out of I/O pins as your design grows. This happened to me recently when, of the 23 GPIO pins available, 2 were taken up by an external ceramic resonator, 1 for the reset line, 3 for serial coms, 14 for the LCD, and 3 for RGB LED control. This used all 23 GPIO pins, with none left for the four buttons I needed. What to do? This Design Idea has the solution.
A close look at the ATMega168 data sheet revealed that the I/O pins available on the 28-pin DIP package and on the 32-pin TQFP package are not all the same. On the TQFP package, there are an additional pair of VCC & GND pins and an additional two ADC input pins on top of the advertised 23 GPIOs.
So if I could read my 4 buttons with these extra ADC inputs, all would be OK and the design would be saved.
Now, the user interface was fairly overloaded with functions, and various combination button pushes were used to call up different menus on the LCD. Also, the software was still under development and more combination button pushes might be called for. I wanted to be able to detect each button individually as well as all possible button push combinations, so for four buttons, I needed to detect a total of 24 , or 16 possible button states.
Advertisement
OK I thought, that should not be hard. I just need a resistor network between my four buttons and one of the ADC inputs so each button pulls down a different amount and all 16 combinations are evenly spaced between VCC and GND (Figure 1 ). However when I tried to do this it turned out not to be as easy as it looked at first sight.
Advertisement

Figure 1 Four-button resistor network
I considered using an R2R resistor ladder, but this requires SPDT buttons so the inputs are connected to VCC or GND, not left floating. After thinking about this for a while, I realised I was making things more difficult for myself than I needed to. There are two ADC inputs available, so if I put only two buttons on each input, then each would only need to detect 22 , or 4 possible button state combinations instead of 16 (Figure 2 ). If the states are not evenly distributed between VCC and GND, it would not matter too much.

Figure 2 Simpler two-button resistor network
This resulted in the circuit of Figure 3 , which proved a successful solution and has now been in production in my client’s product for some time. The resistors are all the same value, so a four-resistor network like this Bourns 1206 part can be used for a small, neat solution.

Figure 3 Partial schematic showing two two-button circuits
The spreadsheet in Figure 4 details the operation of each circuit (download the design files at the end of the article). The yellow cells are where you enter the resistor values used, the supply voltage, and the ADC resolution. The green cells are calculated outputs showing the operation of the circuit. The first three columns show a truth table of the button inputs, and the R Buttons column shows the total pull-down resistance resulting from the different button combinations. The Vout and Counts columns show the voltage input to the ADC and the resulting ADC reading. Count Mid Points shows values midway between the expected ADC values – it is these that are used to differentiate the different inputs and decode the button pushes. This ensures that all possible input values are decoded and maximum allowance is made for variation due to resistor tolerance, noise, etc.

Figure 4 Spreadsheet of design calculations for a two-button section
The software decodes which combination of the four buttons is pushed, debounces the button inputs, and handles button repeat operations when certain buttons are held down.
As can be seen, the code distribution is not at all linear. However, even the closest values still maintain a reasonable separation for practical operation. A graph of the results (Figure 5 ) shows the nonlinearity and also highlights that almost half the ADC input range is unused by this circuit.

Figure 5 Button states vs. values
This is a reasonable trade-off for simplicity, but it made me wonder if I could do better, and brought me back to reconsider the initial idea of Figure 1 . It seems logical to expand the two buttons to four by extending the resistor value progression from 10K, 20K to 10K, 20K, 40K, 80K. However, this does not address the nonlinearity problem or the wasted ADC range.
An improved approach would be to replace the pull-up resistor (R1 in Figure 3 ) with a constant current source. Then, the button resistor network will convert the constant current into a nice, evenly stepped output voltage. This could probably be done using a voltage reference (e.g., TL431) and a transistor. But that means additional active components and careful design. This is getting away from the initial vision of a simple resistor network.
Another approach would be to treat the four-button resistor network as if it were a variable-resistance sensor, like a thermistor, and use the same methods used to read those. This lead to the idea of putting the button network in a bridge circuit. This also matches the signal spread to the ADC input range. With careful resistor selection, it is also possible to improve the nonlinearity. After some experimentation, this lead to the circuit in Figure 6 .

Figure 6 Four-button circuit
This circuit uses only one ADC input for the four buttons, leaving the other ADC input for more buttons or other uses. The four buttons and series resistors form one side of the bridge, and R9/R10 forms the other leg. This is used as the reference voltage for the ADC – equal to the ADC’s maximum input voltage. The resistor values are selected so that R10 = R15 and R9 equals the parallel combination of R11 to R14, thus allowing full use of the ADC input range. Keeping R15 and R10 small improves the linearity of the result at the expense of reducing the actual voltage swing.
Note that these resistor values are not easy to get in practice, but they can be made using standard values in combination: series-connected 20K resistors for R13/R14, and two parallel 10K parts in series with three parallel 1K resistors, to make R9. By using these three values, four resistor networks and one resistor will complete the circuit.

Figure 7 Spreadsheet of design calculations for Figure 6

Figure 8 Button states vs. values
The spreadsheet in Figure 7 and the graph of Figure 8 illustrate the circuit results, which show a much more linear operation than before. This circuit could be extended to five or perhaps six buttons by continuing the geometric resistance sequence. However, care must be taken that the resistor values are
accurate enough to ensure reliable operation, and some experimenting with the spreadsheet and min/max resistor values would be needed to check this.
Experience from building and testing this circuit exposed a small problem in that the AREF pin of the microcontroller draws a small current which introduces an error into the AREF voltage. This current is graphed in the microcontroller datasheet and seems to be well defined. To correct for this, a 33O resistor can be placed in series with R10. With this correction, experimental results produced 8-bit ADC readings within one count of the spreadsheet-predicted values. The software for this version is left as an exercise for the reader.

Figure 9 Four-switch prototype
It is also possible to do without R9 and R10 by using the microcontroller’s internal 1.1V reference. In that case, change R15 from 1K to 1.5K. However, the accuracy of VCC will then affect the readings.
So, after a lot of work, it seems the initial goal from Figure 1 was not too hard after all.
Download design files.
Also see:
- Read 10 or more switches using only two I/O pins of a microcontroller
- 3 pins, 3 LEDs, 3 buttons
- Measure two resistive sensors or multiple switches with a single Schmitt
- Read multiple switches and a potentiometer setting with one microcontroller input pin





“I had a garage door opener which used a similar approach in order to allow use one pair of wires to connect between the multiple switches on the wall-mounted control box and the overhead unit that contained the motor. My unit came with a single door-bell button which only provided the open/close door function. Over time, the switch developed some oxidation on the contacts and occasionally would produce a momentary intermediate resistance value upon closure that made the cpu think that a different switch had been pressed. The problem was that the control signal which it erroneously indicated was the one which put the unit into the mode in which the RF remote was ignored. I couldn’t easily get it out of this mode because the switch randomly produced this intermediate resistance and my wall-mounted control was the lower tier model which did not contain the switch which was supposed to legitimately produce this signal. It took a while to debug the problem and figure out what was going wrong. Only by finding the schematic for the controller for the higher tier model was I able to figure out the problem, and temporarily connect an appropriate value resistor to emulate the desired switch to exit the “lockout” mode. So, be fore-warned that this approach makes the device more vulnerable to aging and oxidation of switch contacts.”
“Hi Steve003 Yes that would be a risk. Perhaps the software filtering will be good enough to protect against momentary bad contacts. It depends on how long the momentary intermediate value persists. In a practical application it would be prudent to add 0.1uF filter caps, to the Vref and button signals, to protect against noise pick up. This would also have the effect of generating momentary intermediate values, for a few mS, as the capacitors charge and discharge. However the software debounce filtering will filter these out and prevent them from causing a problem.”
“There is a commercial keypad “shield” for the Arduino platform which works in a similar way to this approach with the mentioned disadvantages: you loose analog input pins and you need to trim in (software or hardware) the thresholds for the individual keys. http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino
I was loosely involved in a student project, where the students wanted to use the keypad without understanding how it works, included the provided library and everything was ok – until they also wanted to use the analog input using the internal bandgap reference (while the undocumented library used Vcc as a reference). It was then the problems started…”
“Hi uwez1 That shield takes a more basic, and less carefully designed, approach to the button inputs. They do not attempt to interpret combination button inputs so their 5 buttons are decoded as only 5 different inputs, whereas the 4 buttons in my design decode as 16 different inputs. The input values in my design are well enough defined that no trimming is required. Once the threshold values are entered into the software the design can be mass produced without trimming individual units. Also the students problem with the voltage reference is a basic one and can easily be overcome in software by selecting the appropriate voltage reference before taking whatever ADC measurements are needed. A different voltage reference can be used for the buttons and other ADC readings if necessary.”
“Hi Les,nnfor your 16-value design, instead of using binary weighted resistors (which might be difficult to get in e.g. E12-series), you could instead easily build up a R-2R network instead with standard values.”
“Hi uwezi I thought about using an R2R network but I couldn’t see a simple way of getting it to work with normally open, momentary action buttons. The R2R network needs its inputs to be high or low, not open circuit, so the normally open circuit buttons are a problem. To overcome this I could have used inverters between the buttons and the R2R network but that is getting away from the idea of just having a simple resistive network.”
“Apparently Rube Goldberg won a Pulitzer Prize. :-)”
“You are “asking for it”! (I see 22 surface-mount parts on the breadboard, but only 7 in the schematic). That is more complex, and more trouble than using a processor with 4 “more” I/O lines. I encountered Rube Goldberg designs such as this when troubleshooting circuits designed by contractors. There are usually better alternatives. Best is to get off the “6 lead processor train” and put in a package with enough leads to do the job! Remember this: some “poor slob engineer” has to troubleshoot this monstrosity when it breaks, and the software engineer (who usually has no hardware experience) who has to write the code for this thing! “Five minutes” after the product is shipped, the switch contacts get dirty and who knows what the software will “think” was pushed! Even “simple” mutiplexed schemes fail when the contact bounce time goes up with age. Just try using an old calculator! Engineers need to consider the degradations that invariably occur with age, and not design schemes marginal at the outset, “which don’t need to be”, “just because I can”. Just look at the comments below to see the fallacy of using an approach like this. “Trimming” belongs in a pH meter, not a 4 button keyboard! May your customers come after you! The IC vendors are largely at fault because they want to offer engineers only a choice between a 6 lead or a 144 lead QFN package that requires an IC fab facility make the board for, and mount. Other ways out involve using existing existing I/O lines already used for other purposes, but only for outputs, and read back through those lines.”
“One could add a pull-up or pull-down to each switch of a significantly lower value than the DAC resistors u2013u00a0accurate enough for 4 bits.”