Thursday, September 12, 2013

pcDuino - Interrupt Tutorial

This is the latest installment of my series of pcDuino tutorials.  This tutorial will show you how to setup and program the pcDuino to respond to an external interrupt.

This assumes that you have a basic working knowledge of the pcDuino and Linux.  It builds upon the pcDuino – Blink Totorial.

For this tutorial you will need the following items:

  1. A pcDuino running the 20130531 Ubuntu image.
  2. A breadboard.
  3. A button such as the Mini Push Button Switch sold by Sparkfun.
  4. Three jumper wires.
  5. A 10K ohm resistor.
  6. The source code below for building interrupt.c.

DSCF4974

Hardware Setup

The setup for this is simple.  You connect one end of the switch to the 3.3V pin on the pcDuino.  You connect the other end of the switch to GPIO2 pin on the pcDuino and to ground through the 10K resistor.  The 10K resistor as it is connected to ground is a pull-down resistor.  It keeps the signal at 0V when the switch is open.  If a pull-up or pull-down resistor was not used the signal would float which could cause intermittent interrupts when one was not signaled.

image

The pins used are shown by the red arrows in the board diagram above.

Interrupt Program

An interrupt is described in Wikipedia as a signal to the processor emitted by hardware or software indicating an event that needs immediate attention.  In this tutorial we are working with a hardware interrupt. 

The pcDuino is setup with two pins that can be used for interrupts: GPIO2 and GPIO3.  The program is written to use SWIRQ_PIN1 and GPIO2.  However, SWIRQ_PIN2 is also define which corresponds to GPIO3.

The way the program works is that when the switch is opened gpio pin 2 is is connected to ground at 0V.  When the switch is pushed it connects 3.3V to gpio pin 2 which is read by the system and results in an interrupt service routine (ISR) being called if one is defined.

There are 5 types of interrupts:

  1. Rising – called when the signal goes from low to high.
  2. Falling – called when the signal goes from high to low.
  3. High – called when the signal is high.
  4. Low – called when the signal is low.
  5. Change – called when the signal goes from low to high and then again when it goes from high to low.

interrupt.c

#include <stdio.h> 
#include <signal.h>   
#include <stdlib.h>    
#include <inttypes.h>    
#include <sys/ioctl.h>    
#include <fcntl.h>
// sw_interrupt must be loaded.  
// if it isn't load using:
// modprobe sw_interrupt

// Data structure for defining an interrupt on the pcDuino   
typedef struct swIRQ {    
    uint8_t channel;    
    int mode;    
    int pid;    
} swIRQt,*irqSWp;

// Pseudo device for controlling interrupts   
static const char *swirq_dev = "/dev/swirq";

#define SWIRQ_START   (0x201)   
#define SWIRQ_STOP    (0x202)    
#define SWIRQ_SETPID  (0x203)    
#define SWIRQ_ENABLE  (0x204)    
#define SWIRQ_DISABLE (0x205)

#define SWIRQ_RISING  (0x00)   
#define SWIRQ_FALLING (0x01)    
#define SWIRQ_HIGH    (0x02)    
#define SWIRQ_LOW     (0x03)    
#define SWIRQ_CHANGE  (0x04)

#define SWIRQ_PIN1    (0x0)   
#define SWIRQ_PIN2    (0x1)

int main(int _argc, char **_argv) {   
  int fd;    
  int ret;    
  uint8_t swIrqNum = 0;    
  swIRQt swIrqCfg;    
 
  // Define ISR    
  int irqPin1Cnt = 0;    
  int irqPin1Func (void) {    
    irqPin1Cnt++;    
  }

  // Attach the ISR to the USR1 signal which is triggered by the OS when the interupt is signaled.   
  signal(SIGUSR1, (void (*) (int))irqPin1Func);

  // Setup to use pin1 / gpio2 when the signal goes from low to high.   
  swIrqCfg.channel = SWIRQ_PIN1;    
  swIrqCfg.mode = SWIRQ_RISING;    
  swIrqCfg.pid = getpid();    
  swIrqNum = SWIRQ_PIN1;    
 
  // Connect a file descriptor to the pseudo device used to control interrupts    
  fd = open(swirq_dev, O_RDONLY);    
  if ( fd < 0 ) {    
    printf("open swirq device fail");    
    exit(0);    
  }    
 
  // Disable interrupts using pin1 / gpio2    
  ret = ioctl(fd, SWIRQ_STOP, &swIrqNum);    
  if (ret < 0) {    
    printf("can't set SWIRQ_STOP");    
    exit(0);    
  }    
 
  // Configure pin1 / gpio2 interrupts    
  ret = ioctl(fd, SWIRQ_SETPID, &swIrqCfg);    
  if (ret < 0) {    
    printf("can't set SWIRQ_SETPID");    
    exit(0);    
  }    
 
  // Enable interrupts on pin1 / gpio2    
  ret = ioctl(fd, SWIRQ_START, &swIrqNum);    
  if (ret < 0) {    
    printf("can't set SWIRQ_START");    
    exit(0);    
  }    
 
  // Disconnect from the pseudo device    
  if (fd) close(fd);    

  // Loop forever. Print out the counter when the ISR has been called.    
  int oldValue = 0;    
  while (2>1) {    
    if (oldValue != irqPin1Cnt) {    
      printf("irqPin1Funct: %d\n", irqPin1Cnt);    
      oldValue = irqPin1Cnt;    
    }    
  }    
}

Program Setup

Using the editor of your choice or the command “cat > interrupt.c” you will need to get the program on to your pcDuino.  See the Blink Tutorial for screen shots on how to do this.

Next you will need to load the appropriate driver if it isn’t already loaded when you boot your system.  You can check using the lsmod command and then load the driver using “sudo modprobe sw_interrupt” again there are screenshots of this in the Blink Tutorial.

Next compile your program using “gcc interrupt.c –o interrupt” and then run it using “./interrupt”.  When you press the button you should get a number printed on your screen.  You will need to use ctrl-c to exit the program.

Acknowledgements

Thanks to the folks at Sparkfun and their great Getting Started with pcDuino for getting me started with putting this tutorial together.  Also thanks to the folks on the pcDuino website for their support.  Check out the website and forums for more information.

10 comments:

Kevin said...

Thanks!
the best tutorial about the pcDuino interrupts...

how can i get signals from both interrupt pins?

digitalhack said...

Kevin,

Check out this forum post: http://pcduino.com/forum/index.php?topic=4059.msg5421#msg5421

digitalhack

Kevin said...

Wow Quick Reply....

Do you recommend pcDuino interrupts for critical systems?
I just start playing with it

digitalhack said...

Kevin,

I really haven't done enough testing to say one way or the other.

digitalhack

Dark horse said...

I tried the tutorial with pcDuino V3 board and the code doesn't work as expected. What changes needs to be done for the pcDuino V3 board. As far as the A20 documentation, it is pin to pin compatible with A10 processor

digitalhack said...

Dark horse,

I just recently got a pcDuino3 Nano. I may be a day or so but I will run through the tutorial and see what I can figure out.

digitalhack

Anonymous said...

Would you be able to follow up what you found using pcduino3? Thanks

Marek Honek said...

I have a pcDuino V3. I used your code to carry out a test. It is counting of falling edges of square signal. There was only variable incrementing in interrupt service routine:

int irqPin1Func (void) {
irqPinsCnt++;
}

In while loop i have only scanf to read strings as commands from shell and printf function enclosed in if statement to print the value of counted falling edges:

while (2>1) {
printf("type a command:\n");
scanf("%s",&command);
//printf("entered command: %s",command);
if(strcmp(command,"show") == 0)
{
printf("%d\n",irqPinsCnt);
}
if(strcmp(command,"clear") == 0)
{
irqPinsCnt=0;
}

}

It doesn't count accurately for signals with freaquency above approximatelly 1kHz. It counts 380 000 instead of 1 200 000 for 10kHz signal after 120 seconds. I checked CPU time with top command, there was a value of 8%. I also tested your code with real time scheduling, i have added the code:

printf("Init realtime environment\n");
if (geteuid() == 0) {
struct sched_param sp;
memset(&sp, 0, sizeof(sp));
sp.sched_priority = sched_get_priority_max(SCHED_FIFO);
sched_setscheduler(0, SCHED_FIFO, &sp);
// mlockall(MCL_CURRENT | MCL_FUTURE);
fprintf(stderr, "Running with realtime priority!\n");
} else {
fprintf(stderr, "Not running with realtime priority.\n");
}

at the beginning of main function.

It seems the program is running in real-time scheduling, it prints "Running with realtime priority!\n". It doesn't help, the counting is still not correct . What is the root of this kind of problem? Can anybody give advice?

אליסף אריאלי said...

Hi !

I tried "Fast Interrupts" -
I connected signal of 40 Khz to pin 2.

and all interrupts code I tired - were stuck after += 100 "RISING"

I tried the Code from the Arduino-Ide Sample,

I tried the code from this article too, and I got the same results,

anyone Know hot to solve it?


Is there a mode of "Enabled Fast Interrupts"?


Thanl !
Elyasaf

אליסף אריאלי said...

I tried the code from this article too, and I got the same results,

anyone Know hot to solve it?


Is there a mode of "Enabled Fast Interrupts"?


Thanlk !
Elyasaf