Pages: [1]   Go Down
Author Topic: Two Pushbuttons on One Input for More I/O  (Read 965 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 1
Posts: 41
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If you are using pushbuttons and don't quite have enough digital I/O, here's a little trick to free up a pin or two.
   +5V
   +_|
  -|_  PB1
   + |
     \
     / 20K
     \
     /
     \_______________\ To AVR
     |      \        / Input
   +_|      /
  -|_  PB2  \ 150K
   + |      /
     |      \
     |      |
    Gnd    Gnd


If neither button is pressed, the input should read high when its internal pull-up (20K - 50K) is enabled, and low when its pull-up is disabled.  If the top button is pressed, the input will read high with or without the pull-up.  Similarly, if the bottom button is pressed, the input will "stick" low.

A drawback: if both buttons are pressed, the input will read the same as if only the bottom button were pressed.
Logged

Portugal
Offline Offline
God Member
*****
Karma: 6
Posts: 962
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

But how can you see the diference between the high from the pull-up and the high from the button? :-?
Logged

0
Offline Offline
Newbie
*
Karma: 1
Posts: 41
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
But how can you see the diference between the high from the pull-up and the high from the button?

Do digitalWrite(buttons, LOW) to disable the pull-up, read the input, then digitalWrite(buttons, HIGH) to enable the pull-up and read again.  The input will follow the written values only if neither button is pressed.
Logged

Left Coast, CA (USA)
Offline Offline
Brattain Member
*****
Karma: 361
Posts: 17259
Measurement changes behavior
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I would want to measure the actual voltage at the input pin when no switches are pressed to see if a valid logic voltage is present. A high value pull down with the internal pull up enabled may not be valid.

Using a triple voltage divider wired to a analog input pin is a better solution in my opinion.

Lefty
Logged

0
Offline Offline
Newbie
*
Karma: 1
Posts: 41
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I would want to measure the actual voltage at the input pin when no switches are pressed to see if a valid logic voltage is present. A high value pull down with the internal pull up enabled may not be valid.

If the Atmel data sheet can be relied upon, input current (with pull-up disabled) for voltage between 10% to 90% of Vcc should be within +/- 10 uA.  Through 150K to ground, that comes to +/- 1.5 V, well below the approx. 2.6 V threshold between LOW and HIGH readings.  (The input protection diode limits negative voltage to approx. -.6 V.)  The pull-up value is 20K - 50K, yielding a HIGH input voltage of 3.75 - 4.41 V.  Again, this is well above the threshold.  +/- 10 uA would affect this by less than +/- 0.02 V.

Quote
Using a triple voltage divider wired to a analog input pin is a better solution in my opinion.

After posting this, I was thinking of a possible improvement using a binary weighted voltage divider.  I suspect one could unambiguously decode 4 buttons with a single analog input, and maybe 8.  This approach has the advantage of detecting *any* combination of buttons, even if two or more are simultaneously pressed.  Figuring out the necessary tolerances began to make my head hurt, though.
Logged

Greenwood, Indiana
Offline Offline
God Member
*****
Karma: 0
Posts: 508
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

jdoll analog is such fun isn't it? :-D  smiley-razz

http://en.wikipedia.org/wiki/Resistor_ladder

I can't see that being good for more than 4 inputs without time, pain and precision parts but it is an option. For more inputs you may need to calibrate them for various temperatures. The atmega168 and other atmel chips have a built in temperature sensor which may be of use if it's calibrated.

It is a lot of work but it is a cool hack. smiley
Logged

If it was designed by man it can be repaired by man.

0
Offline Offline
Full Member
***
Karma: 0
Posts: 126
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This is very good. smiley Thanks for posting the trick.

Álvaro
Logged

Devon, UK
Offline Offline
Full Member
***
Karma: 4
Posts: 234
Arduino rocks my socks, yes the socks are rocking!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Lady Ada uses 5 buttons on her Monochron in a resistor ladder, I believe. I'll check the schematic.
Logged

Devon, UK
Offline Offline
Full Member
***
Karma: 4
Posts: 234
Arduino rocks my socks, yes the socks are rocking!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Oops, my mistake it's three buttons with five resistors.
Logged

0
Offline Offline
Full Member
***
Karma: 0
Posts: 126
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

What if you could measure N buttons but using a digital input, rather than ADC input ??

Well, it's theoretically possible, and so it is in practice. I just implemented it, and works fine.

But for you to use this approach, you must:
  • Not mind waiting a few milliseconds for buttons to sample.
  • Have either a counter/timer available (or a simple for-loop somewhere)

Theoretically you can even sample multiple buttons, but that is hard to do.

Let's explain first how this works: here's the schematic:



We have a capacitor there. What we will measure is "how long" the capacitor takes to charge until the input pin of arduino reads as 1. We also need to discharge the capacitor before we sample again.

Let's assume no button is pressed, and our capacitor has a value of "VC" at its terminals. If we configure the PIN as OUTPUT LOW, then we're actually feeding only a few microvolts, so we'll sink current from the capacitor using R1.

The basic formula for a RC charge/discharge cycle is "V= V0 * e^(–t/RC)". I'll not write full equations here, you can ask me later for them.

So, after we wait some time (a few milliseconds) we put the PIN back in input mode. If any button is pressed, the capacitor will start charging using the specific resistor attached to that button. After a while, the input PIN will read as '1'. If we count time from when we put PIN as INPUT and PIN reading as '1' we see we have different values depending on which button was pressed.

This requires calibration and some error tolerance. For best results, you should sample 3 times, and if those 3 samples are near a certain known value for a button, you can assume it is pressed.

Here's a sample code I wrote just to demonstrate:

Code:
void setup()
{
      Serial.begin(115200);
      PORTB&=~(1);
}

void loop()
{
      unsigned int c;
      char measure[80];

      // Reset pin.
      DDRB|=1;
    // Let discharge
      delay(1);
      DDRB&=~(1); // Input now.

      for (c=0; c<65535; c++) {
            if (PINB&1) {
                  sprintf(measure,"C: %u\r\n", c);
                  Serial.write(measure);
                  break;
            }
      }
      delay(1000);
}
And here's output for 3 buttons, each pressed at a time and sampled three times:

Code:
C: 1795
C: 1780
C: 1798
C: 7357
C: 7550
C: 7381
C: 2162
C: 2173
C: 2139

Cool, no ? smiley-razz

Best,
Álvaro
Logged

Nowhere
Offline Offline
God Member
*****
Karma: 3
Posts: 852
|-\ |\|\
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

To the OP,
There is already a tutorial on the arduino site for that two switch technique:
http://www.arduino.cc/en/Tutorial/TwoSwitchesOnePin
Logged

Soundcloud page: http://soundcloud.com/beefinator-2
Youtube channel: http://www.youtube.com/user/beefinator14
Old soundcloud page (ran out o

Pages: [1]   Go Up
Jump to: