[Home] [Downloads] [Search] [Help/forum]

Gammon Forum

See www.mushclient.com/spam for dealing with forum spam. Please read the MUSHclient FAQ!

[Folder]  Entire forum
-> [Folder]  Electronics
. -> [Folder]  Microprocessors
. . -> [Subject]  Multiple interrupts on one pin - wired "or" configuration
Home  |  Users  |  Search  |  FAQ
Username:
Register forum user name
Password:
Forgotten password?

Multiple interrupts on one pin - wired "or" configuration

Postings by administrators only.

[Refresh] Refresh page


Posted by Nick Gammon   Australia  (21,379 posts)  [Biography] bio   Forum Administrator
Date Tue 10 May 2011 02:23 AM (UTC)
Message
A question that comes up a few times is, how can we detect an interrupt (eg. a switch press) from multiple sources when the Atmega328 processor only has two interrupt inputs?

This is a bit fiddly to get perfect, so I am documenting it here...

The trick is to wire each input to a separate pin (in my example, pins 3, 4, 5, 6) but to also connect them together to the interrupt input pin (pin 2) using diodes, like this:



All pins are enabled with internal pull-ups, so the default state for them all is on (high). Then if any switch is closed its corresponding pin will be low, and also the interrupt-detection pin (pin 2) will be low as well. However the diodes prevent the other pins from being brought low.

So now an interrupt service routine can fire, detecting a falling pulse. To work out which one caused it, it polls (reads) each of the data pins to see which one is low. The results are saved in an array, ready for testing by the main loop.


// Multiple interrupts on one pin example.
// Author: Nick Gammon
// Date:   10th May 2011

volatile byte pins [4];  //pins 3, 4, 5, 6
volatile boolean fired;

#define FIRST_PIN 3

#define DEBOUNCE_TIME 100  // ms

unsigned long last_time;

void isr ()
{
  // they haven't processed the last one yet
  if (fired)
    return;
    
  for (byte i = 0; i < sizeof pins; i++)
    {
    byte val = pins [i] = digitalRead (i + FIRST_PIN);
    if (val == LOW)
      fired = true;  
    }  // end of for
}  // end of isr

void setup ()
{
  // enable pullup on interrupt-detect line
  digitalWrite (2, HIGH); 
  
  for (byte i = 0; i < sizeof pins; i++)
    digitalWrite (i + FIRST_PIN, HIGH);  // enable pullups on data pins

  attachInterrupt (0, isr, FALLING);

  Serial.begin (115200);
}  // end of setup

void loop ()
{

  if (fired)
  {
    unsigned long now = millis ();

    if (now - last_time > DEBOUNCE_TIME)
    {
      last_time = now;
      Serial.print ("Fired! Switch ");

      for (byte i = 0; i < sizeof pins; i++)
        if (pins [i] == LOW)
          Serial.print (i + 1, DEC);

      Serial.println (" was pressed.");
    }  // end of debounce time elapsed

    fired = false;
  }  // end if fired

}  // end of loop



Testing showed a couple of "race conditions" hence the way it is structured.

For one thing, because of switch bounce the interrupt might fire, but by the time the pins were tested to see which one caused it, none of them registered low. Thus the "fired" flag is only set if a low pin is found.

Also, in the main loop, the "fired" flag is cleared last thing, so that the interrupt routine knows to not change the array of pins if they are still being processed. That is why the interrupt routine does nothing if the "fired" flag is already set.

This general technique should be able to be extended to any number of switches (that you have pins to connect them to), but only require a single interrupt input.

The other interesting thing about the above code is that the debouncing is done by checking elapsed time, not by building in a delay. This way the main loop does not have any "artificial" delays in it which might stop other important processing.

Milliseconds wrap-around


Also of note is the way that the debounce code is written with a subtraction rather than an addition. This caters for the wrap-around that occurs every 4294967296 (0xFFFFFFFF) milliseconds, or roughly 49.7 days.

As an example, say the last keypress was at 0xFFFFFFF0 milliseconds (so there were 16 milliseconds to go before wrap-around), and then the next keypress is at 0x0000000A milliseconds (after the wrap-around). Doing the subtraction:


0x0000000A - 0xFFFFFFF0 --> 0x0000001A


This calculation correctly returns that 26 seconds have elapsed.

Test rig




The blue wire on the right is connected to the common pins of the diodes, and goes to pin 2 of the Arduino (the interrupt input). The other four wires each go to the other end of each diode. The orange wire is connected to ground, and by touching it against the diodes I can "press" the switch to test the circuit.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


7,312 views.

Postings by administrators only.

[Refresh] Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]


Written by Nick Gammon - 5K   profile for Nick Gammon on Stack Exchange, a network of free, community-driven Q&A sites   Marriage equality

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( https://gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Hosted at FutureQuest]