Difference between elapsed time of two consecutive interrupts

Hi all,

I have a question regarding the capability of arduino nano in reading elapsed time between two interrupts . So far I saw some similar posts, but none gave me a definitive answer.

I am working on a project in which i would use a photointerrupter like in the following link put between a gear pair where one gear is plastic and tends to loose a tooth fairly quickly. The idea is to record time intervals between rising edge pulses of the photointerrupter. If the gear is spinning at constant rpm, the time intervals should be roughly the same, but should the gear loose a teeth the time interval at that place would be much larger which would trigger a stop signal, in my case to stop the motors spinning via PLC and to VFD drives.
The gears are quite small and the time between interrupts could be up to around 400 microseconds. To my knowledge, I believe it should be enough for arduino to compare time difference between two pulses, yet i am having trouble and the arduino gives off the stop signal even when the gear is whole and cannot find a reason why. I am attaching the code below.
The second problem i have as well is how to deal with overflow, as i believe there is a better way than the way it is handled in my IF statement.

Thank you in advance for any answers and apologies if there is anything written unclearly in my question.


volatile unsigned long interval;
volatile unsigned long interval_prev;
volatile unsigned long elapsedTime_prev = 0;
volatile unsigned long elapsedTime;


//---------------------------------------------------------------------------------------------------

void timecapture() {
  elapsedTime = micros();
  interval = elapsedTime - elapsedTime_prev ;
  elapsedTime_prev  = elapsedTime;
}

//---------------------------------------------------------------------------------------------------

void setup() {
  pinMode(11, OUTPUT); //Stop signal
  attachInterrupt(digitalPinToInterrupt(2), timecapture, RISING);
}


void loop() {
  

  if (interval > abs((interval_prev << 1)) && interval_prev != 0) { 
    detachInterrupt(digitalPinToInterrupt(2));
    digitalWrite(11, HIGH);
    delay(2000);
    digitalWrite(11, LOW);
    interval = 0;
    interval_prev = 0;
    attachInterrupt(digitalPinToInterrupt(2), timecapture, RISING);
  }
  interval_prev = interval;

}

You are reading/comparing the value of interval without disabling the interrupt. You can disable the interrupt long enough to store it in a temporary variable. Also, it more efficient to use disable interrupts rather than detaching and attaching.

I see, but do you think the code would still execute fast enough if I move the IF statement and compare diretly in ISR like this:

void timecapture() {
  elapsedTime = micros();

  if (elapsedTime > abs((elapsedTime_prev << 1)) && elapsedTime_prev != 0)
  noInterrupts();
  digitalWrite(11, HIGH);
  delay(2000);
  digitalWrite(11, LOW);
  interrupts();
  }
 elapsedTime_prev  = elapsedTime;
}

I need quick execution, as the time between rising edges would be about 400 microseconds after all.

I tried using noInterrupts() before, but it somewhow worked strange in my previous code.
Also, I tested the code I posted by pushing a pencil through the photointerrupter slot and mimicked the longer time interval and the code worked, but not when I apply it directly to the gears.

Surely you need to move as much code as possible out of the ISR rather than putting more in it

Yes, I would say so myself, but do you have any idea why it is not working in my situation?

I would say that the photo interrupter sensor you are using is not appropriate for reading gear teeth. A slotted disc is not the same as a gear. If you forget about timing, can you just count the correct number of teeth for a rotation?

Why do you think so? I believe it might be possible, the only difference is the shape of the "slots". As far as the counting of the pulses goes, it might not be as accurate since the rpm might change during operation.

I would try it this way:

const byte PhotosensorPin = 2;
const byte MissingToothOutputPin = 11;

volatile boolean MissingTooth = false;

void PinChanged()
{
  static unsigned long interruptTime_prev = 0;
  static unsigned long interval_prev = 0;

  unsigned long interruptTime = micros();
  unsigned long interval = interruptTime - interruptTime_prev;
  interruptTime_prev = interruptTime;

  // Ignore times over 1/10th second (stopped or stopping)
  if (interval > 100000ul)
  {
    interval_prev = 0;
    return;
  }

  if (interval_prev != 0 && interval > 2 * interval_prev)
  {
    MissingTooth = true;
  }

  interval_prev = interval;
}

void setup()
{
  pinMode(PhotosensorPin, INPUT);
  pinMode(MissingToothOutputPin, OUTPUT); //Stop signal

  // Interrupt on each rising and falling edge
  // A missing tooth will have two consecutive missing 
  // edges, making the interval three times as long.
  attachInterrupt(digitalPinToInterrupt(PhotosensorPin), PinChanged, CHANGE);
}

void loop()
{
  if (MissingTooth)
  {
    digitalWrite(MissingToothOutputPin, HIGH);
    delay(2000);
    digitalWrite(MissingToothOutputPin, LOW);
    MissingTooth = false;
  }
}

You cannot call delay() in an interrupt! Also, there is no reason to use noInterrupts() or interrupts() in an interrupt routine. Indeed, you should not enable interrupts in an interrupt routine unless you know what you are doing!

@johnwasser has a very nice solution in his post.

@ToddL1962 that I do know I thought you suggested something like this in your initial post regarding the interval value comparison without disabling the interrupt.

@johnwasser thank you for the code, will try it out, hopefully it works!

Not only can you not, it makes no sense whatsoever! :roll_eyes:

I was away from this project for a while, but after a thorough examination I believe the problem was EMI from a photointerrupter cable. I used a shielded one and rechecked all connections.
As far as the code goes, I used mine but I believe the one proposed by @johnwasser will work just as well!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.