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 ledState = HIGH;            // the current state of the output pin
int buttonState = 0;              // the current reading from the input pin
int lastButtonState = LOW; // the previous reading from the input pin

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);
  // set initial LED state
  digitalWrite(ledPin, ledState);
}

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(){
if (reading != lastButtonState){
//reset bounce timer
lastDebounceTime = millis();
}
if ((Millis() - lastDebounceTime) > debounceDelay) {
// if the button state has changed
if (reading != buttonState){
buttonState  = reading;
if (buttonState == HIGH){
ledState = !ledState;
}
}
}
}
and change the loop code to:

//set the led
digitalWrite(ledPin, ledState);
//save the reading, 
//next time through the loop,
// it will be the lastButtonState:
lastButtonState = reading;


and add the following above the setup part of the sketch:

long lastDebounceTime = 0; // the last time the output pin was toggled
long debounceDelay = 50; // debounce time, increase if output flickers

Follow for more info on Interrupts and INPUT_PULLUP.