Maximum Interrupt Frequency Due

I was trying to setup an Arduino Mega 2560 using the interrupt pins and this library as an odometry node in ROS however, I realized I made a miscalculation in my gear ratio. Therefore, the speed of the Mega cannot keep up with the frequency of steps from the encoders. I currently have two 600 step (2400 counts/rev) quadrature encoders attached to two wheelchair motors for my robot. Ultimately, if I keep using these encoders, the arduino must be able to read interrupts for each wheel (165,500 steps/sec (Hz)) for a total of 331,000 Hz. I know this is not possible with a Mega, even for one wheel.

My question is what is the maximum interrupt frequency for the Arduino Due or similar board? The only other code that this will be running is a connection with ROS to publish the data.

Another question, is it possible to not read each rise/fall of pulses of the quadrature and just read 600 steps/rev?

Thanks,
Nathan

I was trying to setup an Arduino Mega 2560 using the interrupt pins and this library

That is not a libiary for a Mega, I am surprised it works.

know this is not possible with a Mega, even for one wheel.

You sure, The Arduino is running at 16MHz so that is about 1000 processor cycles per interrupt, it should be able to handle that.

The only other code that this will be running is a connection with ROS to publish the data.

That is quite a big ONLY. It is likely to be eating your time. Things like that hide a lot of inefficiencies.

is it possible to not read each rise/fall of pulses of the quadrature and just read 600 steps/rev?

Yes, or just trigger the interrupt off one of the encoder signals not both and simply look at the other one inside the ISR.

Maximum Interrupt Frequency Due

That's not an easy question to answer, and I don't have a clue...

The [u]datasheet[/u] should tell you how many clock cycles it takes to process an interrupt.

But then the more important question is, what does your program have to do in-between interrupts, and how long does that take? The datasheet will also tell you how many clock cycles each machine instruction takes, but unless you are programming in Assembly you don't know what machine code is running.

Grumpy_Mike:
Yes, or just trigger the interrupt off one of the encoder signals not both and simply look at the other one inside the ISR.

Could you explain this a little bit more and tell me what you mean by ISR. I think reducing the resolution of the encoder in this way makes sense because I do not need even close to this much resolution/accuracy for what I am doing.

Also, believe the library that I mentioned is supposed to be compatible with most of the Arduino and Teensy boards according to their website.

Thanks

n_spot:
Could you explain this a little bit more and tell me what you mean by ISR.

Interrupt service routine. The code that runs when your interrupt fires. Don't forget, google is your friend. Super helpful at figuring out what some word or acronym you don't know means.

For edge detection counting with a DUE, you can use one of the two builtin Quadrature Decoder.

Here is an example of speed detection from an Quadrature Encoder ( 3500 RPM, 1024 PPR * 4), reply #55:

Do make sure you connect your encoder to one of the pins with external interrupts. Only then you can set it to FALLING or RISING. A pin change interrupt fires whenever a change is recorded. You only need to see one of the two edges to know the speed and how much it rotated.

Then indeed read the second pin inside the ISR to see the direction you're moving in. Use a direct register read instead of digitalRead() to make it much faster (saves easily a couple dozen clock cycles). This way you should be able to do the whole process of counting and recording direction in much less than 100 clock cycles, and at 16 MHz that'd mean you can reach 160,000 Hz.

Your numbers suggest your wheels turn at about 70 revs/second or 4200 rpm, that's got to be one very fast robot! Is that number correct? 70 rpm sounds more sensible to me.

The wheels are only turning at about 110 rpm max, but because the encoders are attached to the wheel chair motors at a ratio of 32:1, they experience a much higher rpm.

I understand what essentially needs to happen to read less resolution, but I do not have experience with writing the ISR code necessary for this. I’ve been trying to implement code I found from Dr. Hessmer here, but for some reason it is not reading correctly. It uses the digitalWriteFast library.

Here is my specific implementation of the library. However, I cannot get a reading from the encoders using it. If anyone has a suggestion or other code which I could start from, that would be greatly appreciated. For now, I am trying to stick with the Mega 2560 and try to use 1/4 the resolution, but I haven’t found code that will allow me to do that yet.

#if defined(ARDUINO) && ARDUINO >= 100
 #include "Arduino.h"
 #else
 #include "WProgram.h"
 #endif
#include <Wire.h>
//#include <Servo.h>
#include <digitalWriteFast.h>  // library for high performance reads and writes by jrraines
                              // see http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1267553811/0
                              // and http://code.google.com/p/digitalwritefast/

// It turns out that the regular digitalRead() calls are too slow and bring the arduino down when
// I use them in the interrupt routines while the motor runs at full speed creating more than
// 40000 encoder ticks per second per motor.

// Quadrature encoders
// Left encoder
#define c_LeftEncoderInterrupt 4
#define c_LeftEncoderPinA 19
#define c_LeftEncoderPinB 25
//#define LeftEncoderIsReversed
volatile bool _LeftEncoderBSet;
volatile long _LeftEncoderTicks = 0;

// Right encoder
#define c_RightEncoderInterrupt 5
#define c_RightEncoderPinA 18
#define c_RightEncoderPinB 24
volatile bool _RightEncoderBSet;
volatile long _RightEncoderTicks = 0;

//Servo _RightServo;  // create servo object to control right motor
//Servo _LeftServo;  // create servo object to control left motor

//int potpin = 0;  // analog pin used to connect the potentiometer
//int val;    // variable to read the value from the analog pin

void setup()
{
 Serial.begin(115200);

//  _RightServo.attach(2);  // attaches the servo on specified pin to the servo object
//  _LeftServo.attach(3);  // attaches the servo on specified pin to the servo object

 // Quadrature encoders
 // Left encoder
 pinMode(c_LeftEncoderPinA, INPUT);      // sets pin A as input
 digitalWrite(c_LeftEncoderPinA, LOW);  // turn on pullup resistors
 pinMode(c_LeftEncoderPinB, INPUT);      // sets pin B as input
 digitalWrite(c_LeftEncoderPinB, LOW);  // turn on pullup resistors
 attachInterrupt(c_LeftEncoderInterrupt, HandleLeftMotorInterruptA, RISING);

 // Right encoder
 pinMode(c_RightEncoderPinA, INPUT);      // sets pin A as input
 digitalWrite(c_RightEncoderPinA, LOW);  // turn on pullup resistors
 pinMode(c_RightEncoderPinB, INPUT);      // sets pin B as input
 digitalWrite(c_RightEncoderPinB, LOW);  // turn on pullup resistors
 attachInterrupt(c_RightEncoderInterrupt, HandleRightMotorInterruptA, RISING);
}

void loop()
{
// val = analogRead(potpin);            // reads the value of the potentiometer (value between 0 and 1023)
// val = map(val, 0, 1023, 0, 179);     // scale it to use it with the servo (value between 0 and 180)

// _RightServo.write(val);
// _LeftServo.write(val);

 Serial.print(_LeftEncoderTicks);
 Serial.print("\t");
 Serial.print(_RightEncoderTicks);
 Serial.print("\n");

delay(20);
}

// Interrupt service routines for the left motor's quadrature encoder
void HandleLeftMotorInterruptA()
{
 // Test transition; since the interrupt will only fire on 'rising' we don't need to read pin A
 _LeftEncoderBSet = digitalReadFast(c_LeftEncoderPinB);   // read the input pin

 // and adjust counter + if A leads B
 #ifdef LeftEncoderIsReversed
   _LeftEncoderTicks -= _LeftEncoderBSet ? -1 : +1;
 #else
   _LeftEncoderTicks += _LeftEncoderBSet ? -1 : +1;
 #endif
}

// Interrupt service routines for the right motor's quadrature encoder
void HandleRightMotorInterruptA()
{
 // Test transition; since the interrupt will only fire on 'rising' we don't need to read pin A
 _RightEncoderBSet = digitalReadFast(c_RightEncoderPinB);   // read the input pin

 // and adjust counter + if A leads B
 #ifdef RightEncoderIsReversed
   _RightEncoderTicks -= _RightEncoderBSet ? -1 : +1;
 #else
   _RightEncoderTicks += _RightEncoderBSet ? -1 : +1;
 #endif
}

The Arduino Encoder Library might look like a Teensy library but it runs on all Arduinos. It has many optimisations dependent on the exact Arduino or Teensy you are using. I would not bother using anything else unless I had some weird requirement that Paul didn’t already think of.

I have tried using that and did get it working, but it only works to read the full resolution of quadrature encoders. I want to read 1/4 of the full resolution. Do you know if that could be done with that library. If so, that would be great! Thanks in advance.

Divide the result by 4.

That defeats the purpose. If you read the whole thread, the point of 1/4 the resolution is to minimize the number of interrupts in order to maximize the speed (rpm) at which the encoders can read without going over 100% of CPU time consumption.

Have you figured out already whether your Arduino is really too busy to be responsive to the rest it has to do? I'd worry about these optimisations later. First try to get it to work. It's good to think about it now, so when you run into limits you know where to start looking. But if you don't run into the limits you've saved yourself a lot of work.

I've already tried it with Paul Stoffregen's Library and I have run into these limits. That is why I was considering upgrading to a Due, but the quicker option would be to use a library that only counted 1/4 the resolution of the quadrature interrupts...

1/4 the interrupts = 1/4 the CPU use

So my question stands. Does anybody know how to implement Paul Stoffregen's Encoder Library that only interrupts for every step, not 4x for each step? Thanks.

You can be sure that the Library uses an input capture, that is to say: interruption for every (rising or falling) edge and you can't do that with another method. Your uc must speed up waaay faster than the max frequency of your quadrature encoder.

You can go down to half the interrupts by not connecting the second output of your encoder and using a normal encoder library, rather than quadrature. You tell the motors to turn in a certain direction so you know which way they're running.

Then use a pin with external interrupt and interrupt on RISING or FALLING rather than CHANGE.

Well, I've decided to purchase a Teensy 3.2 which can run at speeds up to 96Mhz. According to my calculations this should be plenty of bandwidth to capture all the interrrupts at the maximum speed of my robot. Should receive tomorrow and will follow up with my results.