Pages: [1]   Go Down
Author Topic: [FIXED]Due Interrupt Bug  (Read 1490 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I believe I have found a bug with the external interrupts of the due. I was trying to use 8 interrupts simultaneously for 4 encoders but I could never get all of them to work. Eventually I got them to work by trying different pins for each channel until they all worked.
Perhaps there is an error in the compiler?

Using pins 23,24,45,43,31,33,37,35 works. But I couldn't get any simpler sequential sets of pins to work together. Sometime only one channel would be picked up by the interrupt and sometimes only half of the encoder resolution would be returned.

I can attach my code if needed.
« Last Edit: February 04, 2013, 06:18:03 pm by Phillip_James » Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Please do.

I haven't used the Due yet, but judging by the datasheet interrupts can get complex (see page 57).

There are interrupt levels and priorities. If your code to handle an interrupt takes too long, then it is possible another interrupt would be missed.

It is extremely unlikely there is an error in the compiler. Possibly one in the libraries, which would still be in their early stages. Possibly one in your code, or not so much an error as a design problem.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
#define VELOCITY_SAMPLE_TIME 10 //sample time (ms) between velocity checks
#define ENCODER_COUNTS 250

#include "Encoder.h"

unsigned long VelocityTimeStamp=0;

Encoder encoderFL(27, 29);
Encoder encoderFR(45, 43);
Encoder encoderRL(31, 33);
Encoder encoderRR(41, 39);

void setup()
{
  Serial.begin(9600);
 
  encoderFL.initilise();
  encoderFR.initilise();
  encoderRL.initilise();
  encoderRR.initilise();

  //Attach Interupts to both pins for each encoder(for full resolution)
  attachInterrupt(encoderFL.pin_a,doEncoderFL,CHANGE);
  attachInterrupt(encoderFL.pin_b,doEncoderFL,CHANGE);
  attachInterrupt(encoderFR.pin_a,doEncoderFR,CHANGE);
  attachInterrupt(encoderFR.pin_b,doEncoderFR,CHANGE);
  attachInterrupt(encoderRL.pin_a,doEncoderRL,CHANGE);
  attachInterrupt(encoderRL.pin_b,doEncoderRL,CHANGE);
  attachInterrupt(encoderRR.pin_a,doEncoderRR,CHANGE);
  attachInterrupt(encoderRR.pin_b,doEncoderRR,CHANGE);

}

void loop()
{
   updateVelocity();
   //ENCODER DEBUG
   Serial.print(encoderFL.getPosition(),DEC);
   Serial.print(" ");
   Serial.print(encoderFR.getPosition(),DEC);
   Serial.print(" ");
   Serial.print(encoderRL.getPosition(),DEC);
   Serial.print(" ");
   Serial.print(encoderRR.getPosition(),DEC);
   Serial.print(" ---- ");
   Serial.print(encoderFL.getVelocity(),DEC);
   Serial.print(" ");
   Serial.print(encoderFR.getVelocity(),DEC);
   Serial.print(" ");
   Serial.print(encoderRL.getVelocity(),DEC);
   Serial.print(" ");
   Serial.print(encoderRR.getVelocity(),DEC);
   Serial.println();

}

//Wrapper Functions to call correct encoder object method on interupt
void doEncoderFL(){
  encoderFL.doCount();
}
void doEncoderFR(){
  encoderFR.doCount();
}
void doEncoderRL(){
  encoderRL.doCount();
}
void doEncoderRR(){
  encoderRR.doCount();
}


//Function to calculate velocity of encoders once every VELOCITY_SAMPLE_TIME
void updateVelocity(){
  if(VelocityTimeStamp+VELOCITY_SAMPLE_TIME<millis()){
    encoderFL.calculateVelocity();
    encoderFR.calculateVelocity();
    encoderRL.calculateVelocity();
    encoderRR.calculateVelocity();
    VelocityTimeStamp=millis();
  }
}


Code:
#ifndef __ENCODER_H__
#define __ENCODER_H__

#include "Arduino.h"

//This class is designed to manage the encoders
//Four instances of this class are created: one for each encoder
//The methods in this class are called by interupts (attached in the setup function of the main program)
//There is a wrapper function called 'doEncoder' in the main function designed to call the correct encoder method
//this function is required as interupts cannot call object methods directly (unless static there is no pointer)

class Encoder {
public:

  int pin_a;

  int pin_b;

  // constructor : sets pins as inputs and turns on pullup resistors

  Encoder(int A, int B){
    // Set pin a and b to be input
    Evelocity=0;
    Eposition=0;
    EpositionCache=0;
    pin_a=A;
    pin_b=B;

  };

  void initilise(){
    pinMode(pin_a, INPUT);
    pinMode(pin_b, INPUT);

    // Turn on pullup resistors
    digitalWrite(pin_a, HIGH);   
    digitalWrite(pin_b, HIGH);   
  }
 
  void doCount(){
    //Serial.println("Doing Count");
    State = (digitalRead(pin_a)<<1)|digitalRead(pin_b);
    switch(State)
    {
      case 0:
        if(PreviousState==1)Eposition++;
        else Eposition--;
      break;
      case 1:
        if(PreviousState==3)Eposition++;
        else Eposition--;
      break;
      case 2:
        if(PreviousState==0)Eposition++;
        else Eposition--;
      break;
      case 3:
        if(PreviousState==2)Eposition++;
        else Eposition--;
      break;
    }
    PreviousState=State;
  }

  // returns current position
  long int getPosition () {
    return Eposition;
  };

  float getVelocity () {
    return Evelocity;
  };

  void calculateVelocity(){
    Evelocity = (float(Eposition-EpositionCache)/float(millis()-TimeStamp))*1000/(ENCODER_COUNTS*4);
    TimeStamp = millis();
    EpositionCache=Eposition;
  }

  // set the position value
  void setPosition ( const long int p) {
    Eposition = p;
  };

private:

  float Evelocity;

  unsigned long TimeStamp;

  long int EpositionCache;

  volatile long int Eposition;
 
  byte State;
 
  byte PreviousState;
};

#endif // __ENCODER_H__
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The time spent doing those debugging prints (which probably use interrupts) might be affecting  it. Try adding a test to only do those once a second or so.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have also been having tons of problems running interrupts on the Due. Here's what I was trying to do: have a bunch of intterupts attached to a handful of switches. If any of them are switched (CHANGE), then toggle the Led HIGH->LOW and vice versa. In theory this should be super simple, but it was misbehaving badly. For example, sometimes it would miss if I switched one of the pins, flicker the LED when I wasn't doing anything. In some cases, I would hit one switch, and the Led would blink once or a few times.

I think the problem lies in the volatile int definition. I don't have a clear understanding of volatile ints, but getting rid of all the volatiles made everything work just fine. If using interrupts on something that is truly fast, like an encoder, the use of volatiles might actually be important. Try it, it might work.
Logged

Corinth, TX
Offline Offline
Jr. Member
**
Karma: 1
Posts: 92
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

could that behavior be related to switch bouncing?  Are you debouncing the input in code or with hardware?
Logged

Brighton, UK
Offline Offline
Newbie
*
Karma: 0
Posts: 47
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

@RocketManArduino
Any chance you could post your code please, for folks to ponder?  And have you tried reducing it to the bare minimum that misbehaves?  Also, pin debounce is a real must with switch input, as noted by djjoshuad.

From what you said:

Quote
I don't have a clear understanding of volatile ints, but getting rid of all the volatiles made everything work just fine

it sounds to me like a coding issue.  The 'volatile' just tells the compiler not to assume that the variable's value is constant, i.e. don't over-optimize it.

Jim
Logged

Offline Offline
Newbie
*
Karma: 2
Posts: 49
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

"Interrupt on change" is a deceptive feature, it makes it look like sampling digital inputs is quick and simple. In general, you must debounce all signals coming into the CPU, this applies particularly if you are sampling a mechanical switch. Even many solid state devices can generate a slow rise time signal, and with a small amount of noise which tends to be always present in digital circuits, can trigger multiple times around the logic high threshold.

The "interrupt on change" really means "something is happening". That something could be a spurious glitch due to noise, or it could be real a change of state. To tell the difference, one method is to start a timer when the signal changes. A short time, say 10ms, after the signal has stopped changing, test if the new state is different to the old state. Only then act on the new state if it has changed.
Logged

Brighton, UK
Offline Offline
Newbie
*
Karma: 0
Posts: 47
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

@RocketManArduino

Sometimes a good way to check interrupt routines is something like (pseudo code):

Code:
volatile int Count;
int LastCount;

ISR()
{
    Count++;
}

setup()
{
    Count = 0;
    LastCount = 0;
}

loop()
{

    if (Count > LastCount)
    {
        Serial.println(Count, DEC);      // maybe print the time as well?
        LastCount = Count;
    }

}

Any debug prints etc. in the interrupt routines will cetainly affect the timing and confuse things.

HTH
Jim
Logged

Bologna, Italy
Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I had some troubles with the new Due and interrupts on several pins but this patch has saved me: http://arduino.cc/forum/index.php/topic,146430.0.html

Hope it helps! smiley-wink
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Liarco, from initial testing the patch worked wonders.
Logged

Pages: [1]   Go Up
Jump to: