Friday, January 23, 2015

GPIO Interrupts using WiringPi

My weather station has two devices that send simple pulses to the Raspberry Pi. The rain gauge pulses each time its "bucket" fills and the wind speed gauge pulses for each rotation. The best way to handle this is by using interrupts.

An interrupt is a signal to the computer to stop what it is doing and do something else.  These are used extensively by the operating system, but an application can use them as well.  If you have a GPIO input that needs to be responded to, you could poll the GPIO pin in a loop waiting for it to change, but this can be unreliable for very brief state changes. It is also wasteful of the CPU.  The better way is to assign an interrupt that will be activated when the GPIO pin changes state.

I code all my projects in C and use Gordon's WiringPi API.  This library makes it much easier to use GPIO.  Interrupts are now supported using the wiringPiISR function.  (The waitForInterrupt function is deprecated and should not be used.) Using interrupts takes only three steps:

  • Create a function to be called when the GPIO pin changes state. This must be defined like this: void myInterrupt(void)
  • Initialize the WiringPi library by calling wiringPiSetup
  • Call wiringPiISR to configure the interrupt

You can choose to have your interrupt called when the pin goes low, goes high, or both (INT_EDGE_FALLING, INT_EDGE_RISING, INT_EDGE_BOTH)

My interrupts simply increment a global variable. The value of this variable is then checked periodically (typically once per second) and the count is converted into the appropriate units for the device.

An example of how to use an interrupt is shown below.

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <wiringPi.h>

// Use GPIO Pin 17, which is Pin 0 for wiringPi library

#define BUTTON_PIN 0

// the event counter 
volatile int eventCounter = 0;

// -------------------------------------------------------------------------

void myInterrupt(void) {
   eventCounter++;
}

// -------------------------------------------------------------------------

int main(void) {
  // sets up the wiringPi library
  if (wiringPiSetup () < 0) {
      fprintf (stderr, "Unable to setup wiringPi: %s\n", strerror (errno));
      return 1;
  }

  // set Pin 17/0 generate an interrupt on high-to-low transitions
  // and attach myInterrupt() to the interrupt
  if ( wiringPiISR (BUTTON_PIN, INT_EDGE_FALLING, &myInterrupt) < 0 ) {
      fprintf (stderr, "Unable to setup ISR: %s\n", strerror (errno));
      return 1;
  }

  // display counter value every second.
  while ( 1 ) {
    printf( "%d\n", eventCounter );
    eventCounter = 0;
    delay( 1000 ); // wait 1 second
  }

  return 0;
}

All of the code from my weather station can be found at
https://github.com/tedhale/raspberry-pi-hobbyist

You can also get it using git:
git clone https://github.com/tedhale/raspberry-pi-hobbyist.git

3 comments:

  1. Professor D. Thiebaut wrote this code in 2013
    http://cs.smith.edu/dftwiki/index.php/Tutorial:_Interrupt-Driven_Event-Counter_on_the_Raspberry_Pi

    ReplyDelete
  2. Not only is the code stolen, it's also incorrect. volatile is NOT a synchronization primitive across threads.

    ReplyDelete
  3. Never said the code was mine, just put it out there as an example.
    Correct, "volatile" is not a synchronization primitive. It is a flag to the compiler that the variable may change out of scope and no cached version should be used.

    Be a good troll and explain to me how it is incorrect.

    ReplyDelete