PDA

View Full Version : [Question(s)] Interrupt Me please



07-08-2010, 10:45 AM
Hey,
I am trying to get my interrupts going on the Arbotix board so I can tell when the feet on my clunky quad are up or down. I had been polling the digital pins (it works good), but figured interrupts would be better. I need to know when the pins change. I tried the 2nd example in the tutorial but it hates me, so I backed up and tried the first one, and it hates me too. My switches (VEX) are wired to the digital i/o pins and use the pullups and work great when I poll them. Maybe you guys know. Thanks in advance, pm. Here is the code.

// Interrupt-Driver Bumper Example
// A bumper switch on the front of the robot should be tied to digital pin 2 and ground

#include <avr/interrupt.h>

void Stop(void);
void DriveBackward(void);
void DriveForward(void);
void TurnRight(void);

volatile int bumper; // have we hit something

void setup(){

Serial.begin(9600);
Serial.println("Hello TV Land!!");

pinMode(2, INPUT); // Make digital 2 an input
digitalWrite(2, HIGH); // Enable pull up resistor

// attach our interrupt pin to it's ISR
attachInterrupt(0, bumperISR, FALLING);

// we need to call this to enable interrupts
interrupts();

// start moving
bumper = 0;
DriveForward();
}

// The interrupt hardware calls this when we hit our left bumper
void bumperISR(){
Stop();
bumper = 1;
}

void loop(){
// if bumper triggered
if(bumper > 0){
DriveBackward(); // set motors to reverse
delay(1000); // back up for 1 second
TurnRight(); // turn right (away from obstacle)
bumper = 0;
DriveForward(); // drive off again...
}
else {
Serial.print(".");
delay(1000);
}
// we could do lots of other stuff here.
}


void Stop(void)
{
Serial.println("Stop");
}

void DriveBackward(void)
{
Serial.println("DriveBackward");
}

void DriveForward(void)
{
Serial.println("DriveForward");
}

void TurnRight(void)
{
Serial.println("TurnRight");
}

lnxfergy
07-08-2010, 12:58 PM
The issue here is how interrupt pins are labeled. This post is going to seem crazy complicated if I don't quickly cover how many ways you can name a pin. On the AVR, each pin is a member of a port. Ports are 8-bits (8-pins), and each has a letter name. On the ArbotiX, there is actually a port A, B, C, and D. Hardware interrupts (those pins with a dedicated, single-pin interrupt vector) are labeled 0,1,2,... etc. This is all completely independent of the Arduino-style pin labels of Analog 0....7, and Digital 1,2,3... etc. The reason the second example code didn't work for you is that it is ATMEGA168 specific, as it states near the top.

Hardware interrupts 0 and 1 map to Arduino digital pins 2 and 3 on ATMEGA168/328 based Arduinos. Unfortunately, on ATMEGA644P based Arduinos like the ArbotiX, interrupts 0 and 1 are actually the RX and TX pins of serial port 1, which we're using to control AX-12 servos. Thus, we have to use the pin change interrupts.

On the ArbotiX, the Arduino digital pins 0-7, which are brought out to 3-pin headers, are from the AVR PORT B. There are a few registers which have to be configured -- and all of these pins will lead to a single interrupt vector (and thus, a single ISR). We could thus setup to use Digital 1,2,3,4 as our foot sensors, using the following code:



// Quick example of using pin change interrupts on the ArbotiX
// This shows how to use Digital Pin 1-4 (PB1-4) as interrupts for foot sensors

#include <avr/interrupt.h>
int foot_status;

// we need to remember that the Arduino environment uses different pin numbers
// than the ATMEGA644P itself.

void setup(){
// our pins DEFAULT to input... as long as we don't ever set them to output (outside chance of a soft reset).
// but we do want to set them to have pullups:
PORTB |= 0x1E; // this is equivalent to the 4 digitalWrite()s we could do

// this is ATMEGA644P specific, see page ? of datasheet

// Pin change interrupt control register - enables interrupt vectors
// Port B uses pin change interrupt vector 1
PCICR |= (1 << PCIE1);

// Pin change mask registers decide which pins are enabled as triggers
PCMSK1 |= 0x1E;

foot_status = 0;

// enable interrupts
interrupts();
}

void loop(){
// do nothing...
}

// we have to write our own interrupt vector handler..
ISR(PCINT1_vect){
// this code will be called anytime that a foot switches state
// (hi to lo, or lo to hi)
foot_status = (PINB&0x1E)>>1; // much faster than a digitalRead
}
The problem here is: we don't gain much, as we still have to read the pins to find out WHICH foot triggered the interrupt.

We could try mapping each foot to a separate interrupt vector (which means a separate port). For instance, we could use Analog 1 (on port A), Digital 1 (on port B), one of the motor or I2C lines (on port C), and one of the ServoA/B lines (on port D), then each foot would have it's own interrupt handler -- but we still have the issue that we get an interrupt on both the rising AND falling edge of the pin change.

I know I'm not actually using interrupts -- but I'm actually not using the slower Arduino digitalRead functions either. I have my foot sensors plugged into D1-5, they are pulled high with the internal AVR pullups, and when the foot is down, they go low. Then I just use the following bitmaps:



// couple bitmaps, LF = D1 ... RR = D4
#define LF_UP (PINB&0x02)
#define LR_UP (PINB&0x04)
#define RF_UP (PINB&0x08)
#define RR_UP (PINB&0x10)

// below I might do something like:
while(LF_UP)
// lower LF leg
Thus, I can check if a foot is up with what compiles to about 5 instructions -- rather than ~160 on average for a digitalRead() call.

I've been saying I'd write a more thorough tutorial on this for going on months... as this isn't the first time I've answered this question...

-Fergs

07-08-2010, 02:41 PM
Hey, I tried it out and it works. I made a little test program, pretty much your program with some debug. I think I know whats going here, but on this line:

// Pin change interrupt control register - enables interrupt vectors
// Port B uses pin change interrupt vector 1
PCICR |= (1 << PCIE1);

I am not sure quite what it does. Looking in iomxx4.h, I can find all of these defines, and your comments help me sort of understand what you are doing for the other lines, but on this one, I understand PCICR enables the vectors for port A, B, C, and D???. I understand the left shift, but cannot find PCIE1 in the includes. Can you tell me what its doing. Thanks, pm. PS here is the test code for any one wants to try it.


// Quick example of using pin change interrupts on the ArbotiX
// This shows how to use Digital Pin 1-4 (PB1-4) as interrupts for foot sensors

#include <avr/interrupt.h>

// couple bitmaps, LF = D1 ... RR = D4
#define LF_UP (PINB&0x02)
#define LR_UP (PINB&0x04)
#define RF_UP (PINB&0x08)
#define RR_UP (PINB&0x10)

int foot_status;

// we need to remember that the Arduino environment uses different pin numbers
// than the ATMEGA644P itself.

void setup(){

Serial.begin(9600);
Serial.println("Hello TV Land!!");

// our pins DEFAULT to input... as long as we don't ever set them to output (outside chance of a soft reset).
// but we do want to set them to have pullups:
PORTB |= 0x1E; // this is equivalent to the 4 digitalWrite()s we could do

// this is ATMEGA644P specific, see page ? of datasheet

// Pin change interrupt control register - enables interrupt vectors
// Port B uses pin change interrupt vector 1
PCICR |= (1 << PCIE1);

// Pin change mask registers decide which pins are enabled as triggers
PCMSK1 |= 0x1E;

foot_status = 0;

// enable interrupts
interrupts();
}

void loop(){
// do nothing...
if (LF_UP == 0) {
Serial.println("Left front foot up!");
}
else if (LR_UP == 0) {
Serial.println("Left rear foot up!");
}
else if (RF_UP == 0) {
Serial.println("Right front foot up!");
}
else if (RR_UP == 0) {
Serial.println("Right Rear foot up!");
}

delay(10);

}


// we have to write our own interrupt vector handler..
ISR(PCINT1_vect){
// this code will be called anytime that a foot switches state
// (hi to lo, or lo to hi)
foot_status = (PINB&0x1E)>>1; // much faster than a digitalRead
}

lnxfergy
07-08-2010, 02:46 PM
Hey, I tried it out and it works. I made a little test program, pretty much your program with some debug. I think I know whats going here, but on this line:

// Pin change interrupt control register - enables interrupt vectors
// Port B uses pin change interrupt vector 1
PCICR |= (1 << PCIE1);

I am not sure quite what it does. Looking in iomxx4.h, I can find all of these defines, and your comments help me sort of understand what you are doing for the other lines, but on this one, I understand PCICR enables the vectors for port A, B, C, and D???. I understand the left shift, but cannot find PCIE1 in the includes. Can you tell me what its doing. Thanks, pm.

PCIE1 is a bit definition -- it's probably in an atmega164/324/644p-specific file (I'm not sure which one). Basically, we need to set a particular bit to high. PCIE1 is going to have a value of 0 to 7, denoting which bit in the byte-long register PCICR corresponds to that vector.

-Fergs

P.S. General AVR tidbit of knowledge: you'll often see (1<<PCIE1) written as _BV(PCIE1) (bv = bit value) which is a commonly defined macro -- it's not as prevelant in Arduino land -- but very common amongst AVR programmers in AVR-GCC.

07-08-2010, 03:17 PM
Thanks very much!