Hit a ceiling with interrupt speed on nano, 13Khz

void Count()

{
 
 
   _speed =  1.0 / (((micros() - lastTime-4) / 1000000.0) / 60.0);

  lastTime = micros();
 digitalWrite(13, HIGH);

}

This is part of a motor speed calculator. THis same code tested on a teensy 3.2 has seen up to 50khz no issues, but on the nano i can't get it much farther than 13khz. This is a direct pin connection from the source sim arduino to the measuring nano/teensy.

_speed tops out at ~13K

Question: Am I doing anything wrong? Has anyone been able to read pulses at much higher frequencies?

A Teensy is a 32 bit ARM, isn't it?

What is your question ?

The Arduino Nano uses a 16 MHz 8-bit microcontroller.
The Teensy 3.2 uses a 72 MHz ARM Cortex-M4 32-bit processor.
It is exactly what it is supposed to be.

The FreqCount library can probably measure into the Mega Hertz with an Arduino Nano.

This could take c. 20uS on a Nano:

digitalWrite(13, HIGH) ;

Post all your code for suggestions about optimisation.

donperry:
This is part of a motor speed calculator. THis same code tested on a teensy 3.2 has seen up to 50khz no issues, but on the nano i can't get it much farther than 13khz. This is a direct pin connection from the source sim arduino to the measuring nano/teensy.

_speed tops out at ~13K

Question: Am I doing anything wrong? Has anyone been able to read pulses at much higher frequencies?

You have not posted enough of your code to be able to form any opinion. Post the whole program.

Use the digitalWriteFast library - it is a great deal faster than the standard digitalRead() / digitalWrite() but the I/O pins must be known at compile time.

Floating point calculations are very slow - avoid them if you can.

I can see no need to calculate the speed for every pulse. What is the speed value used for?

...R

The 8 bit ATmegas even don't have hardware for integer division. Everybody can find out how much time takes the _speed calculation with 3 divisions.

For higher frequencies (above 10Hz) it's better to count pulses instead of their intervals. In either case perform calculations only when required, not normally inside an ISR.

Can someone delete this?

I made a very fundamental mistake. I had declared the variable as int, instead of unsigned or even long. As soon as whatever I was doing with it reached 32,xxx it wrapped around.

donperry:
Can someone delete this?

I made a very fundamental mistake. I had declared the variable as int, instead of unsigned or even long. As soon as whatever I was doing with it reached 32,xxx it wrapped around.

Leave it here so other readers can benefit from your learning.

Please post the revised program.

...R

Alright.

int triggerPin = 6;
long speed_now=0; //this was previously declared as int, causing an integer overflow, causing the nano to not read higher than what an integer would allow
long lastTime=0;
void setup()
{
 
    attachInterrupt(digitalPinToInterrupt(triggerPin), Count, FALLING);
    Serial.begin(9600);

}
 

void loop()
{
 Serial.print("Motor Speed: ");
 Serial.print(speed_now);
  Serial.println(" RPM");
}
 
void Count()
{
    
    speed_now = 1.0 / (((micros() - lastTime) / 1000000.0) / 60.0);
   lastTime = micros();
   
}
  


/* //The following was the old code that had the symptoms
 * int triggerPin = 6;
int speed_now=0; //this was previously declared as int, causing an integer overflow, causing the nano to not read higher than what an integer would allow
int lastTime=0;
void setup()
{
 
    attachInterrupt(digitalPinToInterrupt(triggerPin), Count, FALLING);
    Serial.begin(9600);

}
 

void loop()
{
 Serial.print("Motor Speed: ");
 Serial.print(speed_now);
  Serial.println(" RPM");
}
 
void Count()
{
    
    speed_now = 1.0 / (((micros() - lastTime) / 1000000.0) / 60.0);
   lastTime = micros();
   
}
  

 */

Doing a floating point calculation takes a lot of processor power - way more than the digitalRead() or digitalWrite() calls. That's probably what slows you down most. It's really not a good idea to try and do this inside an ISR.

If you want to go really fast, connect your pulse input to the TC1 pin - for the 16-bit Timer1, and count pulses in hardware, you can get up to 8 MHz. Read the counter value at appropriate intervals (to have a good number of pulses but no risk of overflow) to calculate the frequency.

Thanks for sharing that.

So instead of doing the calcs in the function, I could simply only mark the "last time" it was called

 lastTime = micros();

and then do the calcs outside of the ISR

...but don't forget to protect access to it in non-interrupt context, to prevent spurious results.

How about:

  • ISR counts pulses.
  • every now and then (a few times a second) you check how many pulses have been added over the past period, and calculate speed out of that.

If you calculate speed say ten times a second (enough for most practical purposes) you have lots of pulses. The half pulse you miss doesn't give a serious error. If it's for a human observer 2-3 times a second is enough, you can't read numbers much faster than that anyway.