Tutorial: Interrupt-Driven Event-Counter on the Raspberry Pi

From dftwiki3
Revision as of 21:20, 23 July 2013 by Thiebaut (talk | contribs) (Increasing the Resolution of the Event Counter)
Jump to: navigation, search

--D. Thiebaut (talk) 19:57, 23 July 2013 (EDT)


The purpose of this tutorial is to illustrate how to implement a user-level interrupt in C on a Raspberry Pi to count events. You may want to start with the very first tutorial of this series, here.






Getting the Environment Ready

Install the WiringPi Library

  • Get the WiringPi library from drogon.net
  • Follow the directions on the Web site to download to the Pi. In my case the Pi is connected to my Mac through an ethernet cable, so I downloaded the tgz archive from https://git.drogon.net/?p=wiringPi;a=summary and sftp it over to the pi. I renamed the actual library to wiring.tgz, which is easier to type than its actual name.

This section is only visible to computers located at Smith College

sftp pi@169.254.0.2
sftp> pwd 
Remote working directory: /home/pi
sftp> put wiring.tgz
Uploading wiring.tgz to /home/pi/wiring.tgz
wiring.tgz                                                                            100%  108KB 107.8KB/s   00:00    
sftp> quit
  • Connect to the RPI and build the library
 tar -xzvf wiring.tgz 
 cd wiringPi-cbf6d64/
 ./build 
wiringPi Build script
=====================


WiringPi Library
make: Warning: File `Makefile' has modification time 1.4e+07 s in the future
[UnInstall]
[Compile] wiringPi.c
[Compile] wiringSerial.c
...
[Compile] drc.c
[Link (Dynamic)]
[Install Headers]
[Install Dynamic Lib] 
make: warning:  Clock skew detected.  Your build may be incomplete.

WiringPi Devices Library
make: Warning: File `Makefile' has modification time 1.4e+07 s in the future
[UnInstall]
[Compile] ds1302.c
...
[Link (Dynamic)]
[Install Headers]
[Install Dynamic Lib] 
make: warning:  Clock skew detected.  Your build may be incomplete.

GPIO Utility
make: Warning: File `Makefile' has modification time 1.4e+07 s in the future
[Compile] gpio.c
[Compile] extensions.c
[Compile] readall.c
[Link] 
make: warning:  Clock skew detected.  Your build may be incomplete.
make: Warning: File `Makefile' has modification time 1.4e+07 s in the future
[Install]
make: warning:  Clock skew detected.  Your build may be incomplete.

All Done.

NOTE: This is wiringPi v2, and if you need to use the lcd, Piface,
  Gertboard, MaxDetext, etc. routines then you must change your
  compile scripts to add -lwiringPiDev

  • Add the new library to the libray path, as explained in the INSTALL file of the wiringPi distribution.
 sudo nano /etc/ld.so.conf
and add the following line to it:
  /usr/local/lib
  • Tell the system to configure the libraries:
  sudo ldconfig

Hardware Setup

  • Our hardware setup is the same as that presented in Introduction to accessing the Raspberry Pi’s GPIO in C++ (Linux Way / SYSFS) on hertaville.com. We have connected the switch only. The printf statements will provide the feedback about whether activating the button triggers the ISR or not.
  • We use PIN 17 of the GPIO, available on the RPI 26-pin connector Pin 11.
  • Connecting the momentary switch is simple:
    • 1 lead connected to GND
    • other lead connected to two places:
      1. to one side of a a 10Kω resistor, the other side of the resistor to 3.3V
      • to Pin 17 of the GPIO



Interrupt Service Routine

Picking the Right Constant for GPIO Pin 17

  • The wiringPi library labels GPIO Pin 17 as Pin 0 (see drogon.net), as illustrated in the table below taken from their Web site:

RaspberryPi GIP WiringPi pinout.jpg

  • Our switch is connected to Pin 17 of the GPIO, so we'll use 0 to refer to this pin when using the wiringPi library.




ISR Code



The code for the Interrupt Service Routine is given below. Its operation is simple:

  1. it defines Pin 0 (GPIO Pin 17) as the pin which will receive the events
  2. it defines a function that will be called by the interrupt triggered by Pin 0.
  3. it initializes the wiringPi library
  4. it attaches



/*
isr4pi.c
D. Thiebaut
based on isr.c from the WiringPi library, authored by Gordon Henderson
https://github.com/WiringPi/WiringPi/blob/master/examples/isr.c

Compile as follows:

    gcc -o isr4pi isr4pi.c -lwiringPi

Run as follows:

    sudo ./isr4pi

 */
#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;


/*
 * myInterrupt:
 */

void myInterrupt (void) {
   eventCounter++;
}


/*
* main
*/

int main (void) {

  if (wiringPiSetup () < 0) {
      fprintf (stderr, "Unable to setup wiringPi: %s\n", strerror (errno));
      return 1;
  }

  if ( wiringPiISR (BUTTON_PIN, INT_EDGE_FALLING, &myInterrupt) < 0 ) {
      fprintf (stderr, "Unable to setup ISR: %s\n", strerror (errno));
      return 1;
  }

  while ( 1 ) {
    // display # of events in past second
    printf( "%d\n", eventCounter );

    // reset counter to 0 
    eventCounter = 0;

    // wait 1 second doing nothing...
    delay( 1000 ); // wait 1 second
  }

  return 0;
}







Compilation

  • Compile the code above against the WiringPi library as follows:
 gcc -o isr4pi isr4pi.c -lwiringPi



Test

Now comes the time to test the setup. We launch the program on the RPI and press the button several times. Note that because there is no debouncing on the button, spurious spikes are generated when we activate it and the number printed on the screen is quite a bit larger than how often we press the button. For example, pressing the button once generates counts of 2, 3, and even 6. This is not a flaw. Just a property of mechanical switches. There are several good solutions on the Web ways to debounce switches.

pi@raspberrypi ~ $ sudo ./isr4pi 
0
0
0
14
13
10
2
3
2
6
2
^C
pi@raspberrypi ~ $ 


Note that you have to Control-C out of the program to stop it...

Increasing the Resolution of the Event Counter

  • Raspberry Pi users are reported the important latency with which user-level interrupts are serviced. One user reports as much as 75 µs latency on some of the interrupts, and an other indicates that dynamic refresh of the RAM may also create longer delays.
  • One solution is to increase the priority of the interrupt, which requires making it a kernel-level interrupt and recompile the kernel, which is not for the faint-of-heart.
  • Another solution that is inexpensive and requires one external chip can save the day and still work with user-level interrupts while providing better accuracy. The figure below illustrates the concept:

300px