Monday, September 23, 2013

Interrupts, the easy way

Many folks starting out in microcontrollers do not know what interrupts are, or are intimidated by them. Today I hope to shed some light on this easy to use, and useful function.

There are two ways to detect if an event has happened, polling, and interrupts. If you are expecting someone to come to your front door, you can get up every few minutes to look out the window (polling), or you can be busy cleaning your house until the doorbell rings (interrupts).

The most common for beginners is polling. In a loop, I check to see if an input is high, or low. Two disadvantages of this method are 1) I might miss an event if I'm busy doing something else, 2) I'm busy checking for the event, and that slows down my other processes.

Interrupts allow me to process a lot of other stuff, and then the interrupt will notify me when the event being monitored happens.

Let's look at a scenario:

I have a push button (doorbell) connected to D2. I have a LED connected to D13. I want to know when someone is at the door, and light the LED when the button is pushed. The polling method would look like this:

// constants won't change. They're used here to
// set pin numbers:
const int buttonPin = 2;     // the number of the pushbutton pin
const int ledPin =  13;      // the number of the LED pin

// variables will change:
int buttonState = 0;         // variable for reading the pushbutton status

void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);    
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT_PULLUP);  
}

void loop(){
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  // check if the pushbutton is pressed.
  // if it is, the buttonState is LOW:
  if (buttonState == LOW) {  
    // turn LED on:  
    digitalWrite(ledPin, HIGH);
  }
  else {
    // turn LED off:
    digitalWrite(ledPin, LOW);
  }
}

Now, let's look at the interrupt version of this sketch, Since the event is short lived, we will toggle the state of the LED each time it's pushed (straying from our doorbell example), this would be ideal for monitoring rpm or other fast moving events, by counting the number of triggers per unit of time:

/*
  Button

 Turns on and off a light emitting diode(LED) connected to digital
 pin 13, when pressing a pushbutton attached to pin 2.


 The circuit:
 * LED attached from pin 13 to ground
 * pushbutton attached to pin 2 from Gnd
we are using pinMode INPUT_PULLUP, so no pull up or pull down resistor needed.
INPUT_PULLUP is the same as adding an external pullup resistor


 * Note: on most Arduinos there is already an LED on the board
 attached to pin 13. We are using the onboard LED for our example.
If you use an external on any other pin, you will need a resistor.


 */

// constants won't change. They're used here to
// set pin numbers:
const int buttonPin = 2;     // the number of the pushbutton pin
const int ledPin =  13;      // the number of the LED pin

// variables will change:
int buttonState = 0;         // variable for reading the pushbutton status

void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);  
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT_PULLUP);
  // set up the interrupt on pin 2, call the routine,
  // and set the action that triggers the call
  attachInterrupt(0, blink, FALLING);
}

void loop()
{
 digitalWrite(ledPin, buttonState);
}

void blink()
{
  buttonState = !buttonState;
}

Now, we notice that effect is erratic. sometimes the button blinks, sometimes it stays steady. This is due to button bounce. The contacts don't make a single state change, they connect / disconnect quite a few times aver a very short time. We need to debounce the button.

change the void blink() function to the following:

void blink()
{
static unsigned long lastMillis = 0;
unsigned long newMillis = millis();
if (newMillis - lastMillis < 50){
}
else{
  buttonState = !buttonState;
  lastMillis = newMillis;
}
}

Follow for more info on Interrupts and INPUT_PULLUP.