Interrupt Overhead

Hey,

I have been trying to calculate the velocity for two motors, using encoder interrupts, which later would be used to apply PID control. Velocity Calculation code gives out correct velocity till a certain rpm (goes up as input voltage goes up), but then starts giving negative velocity (goes down as voltage goes up). Following is the code:

#define DirectionPin 3    // direction pin

int encoder0Pos = 0,oldPos = 0;  // encoder position
int dir = 0;
unsigned long oldTime, newTime, dt;
float vel;


void setup() {
  Serial.begin(9600);            // set up Serial library at 9600 bps
  pinMode(DirectionPin, INPUT); 

// encoder pin on interrupt 0 (pin 2)
  attachInterrupt(5, doEncoder, RISING);
  oldTime = millis(); 
}

void loop() {
  newTime = millis();              // read the current time stamp
  dt = newTime - oldTime;           // compute delta time
  oldTime = newTime;
  
  vel = (float) ((encoder0Pos - oldPos) * 4.25) / (dt);  // velocity estimate in ms  
  int temp = encoder0Pos - oldPos;
  oldPos = encoder0Pos;
  
  Serial.println(vel);
}

// Interrupt routine
void doEncoder()
{
  dir = digitalRead(DirectionPin);     // read the direction of motor
  if (dir == HIGH) {encoder0Pos = encoder0Pos - 1;}
  else {encoder0Pos = encoder0Pos + 1;}
  
}

I need to connect three of these motors later in near future, but this code seems to have lot of interrupt overhead. Interrupt frequency is around 8000 ticks per second per motor.

You could get rid of the digitalRead in the ISR, and use the fast version instead.

I did the same. Following is the code.

#define DirectionPin 3    // direction pin

int encoder0Pos = 0,oldPos = 0;  // encoder position
int dir = 0;
unsigned long oldTime, newTime, dt;
float vel;


void setup() {
  Serial.begin(9600);            // set up Serial library at 9600 bps
  pinMode(DirectionPin, INPUT); 

// encoder pin on interrupt 0 (pin 2)
  attachInterrupt(5, doEncoder, RISING);
  oldTime = millis(); 
}

void loop() {
  newTime = millis();              // read the current time stamp
  dt = newTime - oldTime;           // compute delta time
  oldTime = newTime;
  
  vel = (float) ((encoder0Pos - oldPos) * 4.25) / (dt);  // velocity estimate in ms  
  int temp = encoder0Pos - oldPos;
  oldPos = encoder0Pos;
  
  Serial.println(vel);
}

// Interrupt routine
void doEncoder()
{
  dir = (PINE & 0b00100000) >> 5;
  if (dir == HIGH) {encoder0Pos = encoder0Pos - 1;}
  else {encoder0Pos = encoder0Pos + 1;}
}

I could notice some improvement but, I finally need to implement this with three motors in parallel. Connecting two of them, I faced the same problem again. Any solutions?

That wasn't quite what I meant.
I see no reason here that "dir" should be global.
Either:

  if (digitalReadFast(DirectionPin) {encoder0Pos = encoder0Pos - 1;}
  else {encoder0Pos = encoder0Pos + 1;}

or

  if ((PINE & 0b00100000) >> 5) {encoder0Pos = encoder0Pos - 1;}
  else {encoder0Pos = encoder0Pos + 1;}

Cool ! Thanks a lot for your reply.

I will try that soon and post my results here !

I forgot: encoder0Pos should be declared "volatile"

Hey,

May I know the reason for declaring it 'volatile'?

By the way, forgot to mention, Interrupt frequency is around 12646 counts per sec per motor.
(1024 Counts per Rotation and 741 Rotations per Minute (RPM))

I tested my motor with your suggestions incorporated. It (two motors together) still gives erroneous velocity values.

Would you suggest me to consider following options?

  • Analog PID Controller (using resistors, Op-Amp and Capacitors)
  • Use of Motion Control Chip (PID Controller - LM629)

Thanks in advance !

May I know the reason for declaring it 'volatile'?

Sometimes, the reference material is a little deficient.
Not in this case, however:
http://arduino.cc/en/Reference/Volatile

AWOL:
I forgot: encoder0Pos should be declared "volatile"

And should be accessed atomically in loop() ?

Good point.