Need help with getting the time elapsed between two interrupts

Hi all!
I’ve been trying to figure out how to calculate the time between interrupts when a rotary encoder disc goes from low to high or vice versa. I use Arduino Uno (one year old) and are connected with ir sensor on pin 2 and 3, which is interrupt 0 and 1. I’ve learned that micros() should work with interrupt handling.

So my questions are:

  • Do i need to declare these variables as volatile?
  • Will the variables contain the truly read micros() values?
  • Is it needed to have noInterrupts()/interrupts() in main loop?
  • Is delay time needed in isr method?
  • Is this a good way to create delay time by using micros() as done?

volatile long ApastTime = 0;
volatile long AcurrTime = 0;

volatile long BpastTime = 0;
volatile long BcurrTime = 0;

int i = 0;
int y = 5;

void setup() {
Serial.begin(115200);
attachInterrupt(0, ISR0, CHANGE);
attachInterrupt(1, ISR1, CHANGE);
}

void loop() {
noInterrupts();

Serial.print(“A Time:”);
Serial.print(“\t”);
Serial.println(AcurrTime - ApastTime);

Serial.print(“B Time:”);
Serial.print(“\t”);
Serial.println(BcurrTime - BpastTime);

interrupts();
}

void myDelay(int x) {
for(i = 0; i <= x; i++)
{
delayMicroseconds(1000);
}
}

void ISR0(){
myDelay(y);
ApastTime = AcurrTime;
AcurrTime = micros() – y * 1000;
myDelay(y);

}

void ISR1(){
myDelay(y);
BpastTime = BcurrTime;
BcurrTime = micros() – y * 1000;
myDelay(y);
}

Is this a good way to create delay time by using micros() as done?

If you can live with / compensate for the “out-by-one” error, it’s OK. Of course, delaying in interrupt context is rarely a good idea.

for(i = 0; i <= x; i++)

Please use code tags when posting code.

Why put a delay in there only to have to subtract it back out of your timing variable. What is the purpose of the delay?

You should never be delaying inside an ISR. ISR should be fast, get in get done and get out.

I totally agree on delays in interrupts...
But so far nothing has worked for me.
Let's break it into pieces.

  1. Will this work?
void ISR0(){
 ApastTime = AcurrTime;
 AcurrTime = micros();
}
  1. With this:
void loop() {
 noInterrupts();

 Serial.print("A Time:");
 Serial.print("\t");
 Serial.println(AcurrTime - ApastTime);

 interrupts();
}
void loop() {
 noInterrupts();

 Serial.print("A Time:");
 Serial.print("\t");
 Serial.println(AcurrTime - ApastTime);

 interrupts();
}

Using that code most of the time interrupts will be disabled so when will the interrupts get a chance to run the ISR code ? To prevent corruption you need to guard the updating of multibyte variables by disabling interrupts but in loop() as written no variables are being updated. Inside ISRs interrupts are disabled automatically so volatile variables can safely be updated.

Oh, and by the way, Serial.print() uses interrupts so it is probably best not to use it with them turned off.

It's not clear to me what you are trying to do. Are you trying to determine the interval between two rising or two falling edges--that is a pulse to next pulse interval. Or are you trying to measure the interval between a rising and a falling edge (or between falling and rising) which will give you the length of a high or low pulse.

In neither case is delay() required within the ISR.

If you tell us more, we can help with the appropriate ISR and its trigger conditions.

Im building a 2 x DC motor system with one wheel on each DC. On the wheel there is a 16 bit encoder disc attached read by an IR transciever. I want to detect and measure time elapsed between each change (high to low or low to high situation) the ir delivers to its port. And to this add a PID-system to be able to control the speed. I've read some stuff regarding interups on Arduino but nothing up until now has gotten clear. I've just before i started writing this post found a way to get this code work.

All l...Time variables are declared volatile long.
The interupt flag is volatile int

There is no delay() nor noInterrupt()/interrupt() etc in the code.

In the first part you can see printouts of interrupt method with zero values. The value printed to the right is IDotTime / 1000 or (ICurrTime - ILastTime) / 1000 and should represent the time it takes to travel from high to low or low to high state.

In the second part i used only one long variable in the interrupt method and then it works!

Why?


  leftInterrupt = !leftInterrupt;
  lLastITime = lCurrITime;
  lCurrITime = micros();
  lIDotTime = lCurrITime - lLastITime;

  //OR THIS CODE
  leftInterrupt = !leftInterrupt;
  lLastITime = lCurrITime;
  lCurrITime = micros();

  // MAKES THIS TYPE OF PRINTOUT
 
Time	DCSide	flag	Dot time
--------------------------------
4768	L	0	0
4929	L	1	0
5311	L	0	147
6335	L	1	0
6445	L	0	0
7008	L	1	0
7158	L	0	151
7780	L	1	0
7931	L	0	0
8584	L	1	0
8975	L	0	0
9096	L	1	122

  leftInterrupt = !leftInterrupt;
  lCurrITime = micros();

  // MAKES IT RIGHT

Time	DCSide	flag	Dot time
--------------------------------
3564	L	0	351
3785	L	1	214
4468	L	0	689
4608	L	1	141
5230	L	0	621
5612	L	1	384
5772	L	0	158

if the encoder bounces it is no problem as it is moving nowhere.
get rid of the delays as they are real stoppers.
besides the time between a puls better count the forward pulses in 1 second.that gives better speed. also a good way to see distance.

Im getting to where you aim (shooter) but first i want to know what is going on with interrups.

Here is some code which should measure both dark and light pulse lengths. I have not tested it with an IR sensor, but it works with a different rotary encoder.

volatile unsigned long lastPulseH;
volatile unsigned long lastPulseL;
volatile int triggerCount = 0;

unsigned long pulseLow[32];
unsigned long pulseHigh[32];


void setup(){
  Serial.begin (115200);
  attachInterrupt(0, HighLowPulse, CHANGE);
}

void loop(){
  if(triggerCount<32){
    pulseLow[triggerCount] = lastPulseL;
    pulseHigh[triggerCount] = lastPulseH;
  }
  else{
    for(int j=0;j<32;j++){
      Serial.print(j);
      Serial.print(" pulseLow ");
      Serial.print(pulseLow[j]);
      Serial.print("  ");
      Serial.print(" pulseHigh ");
      Serial.println(pulseHigh[j]);
      triggerCount=0;

    }
  }
}

void HighLowPulse() {
  triggerCount ++;
  static unsigned long startTimeH;
  static unsigned long startTimeL;
  if (digitalRead(2)){ // Gone HIGH
    startTimeH = micros();
    lastPulseL = startTimeH - startTimeL;
  }
  else { // Gone LOW
    startTimeL = micros();
    lastPulseH = startTimeL - startTimeH;
  }
}

What are the delays for, exactly?

http://www.gammon.com.au/interrupts

This is an example of timing a ball running down a ramp:

const byte LED = 12;
const byte photoTransistor = 2;

unsigned long startTime;
volatile unsigned long elapsedTime;
volatile boolean done;

void ballPasses ()
{
  // if low, ball is in front of light
  if (digitalRead (photoTransistor) == LOW)
    {
    startTime = micros (); 
    }
  else
    {
    elapsedTime = micros () - startTime;  
    done = true;
    }
    
  digitalWrite (LED, !digitalRead (LED));  
}


void setup ()
{
  Serial.begin (115200);
  Serial.println ("Timer sketch started.");  
  pinMode (LED, OUTPUT);
  attachInterrupt (0, ballPasses, CHANGE);
}

void loop ()
  {
  if (!done)
    return;
    
  Serial.print ("Time taken = ");
  Serial.print (elapsedTime);
  Serial.println (" uS");
  done = false;
  }

Your long variables should be unsigned long.

As I mentioned before i've no delays left in the code. And there is nor any noInterrupt()/interrupts() in there either.

The code in setup():

attachInterrupt(0, rightENCInterrupt, CHANGE);
attachInterrupt(1, leftENCInterrupt, CHANGE);

The interrupt methods:

void rightENCInterrupt(){
  rightMotorcontroller.setCurrDotTime(micros());
}

void leftENCInterrupt(){
  leftMotorcontroller.setCurrDotTime(micros());
}

The motorcontroller lib i've created:

volatile bool 		_Tick;
volatile unsigned long 	_currDotTime;


void MotorcontrollerAll::setCurrDotTime(volatile unsigned long aDotTime){
	_currDotTime = aDotTime;
	_Tick = !_Tick;
}

All of this works. But if i f ex. change:

_Tick = !_Tick;

to:

_Tick++;

and

volatile bool 		_Tick;

to:

volatile int _Tick;

The code will not behave correctly

For goodness sake post all your code.

It's more than 1000 lines of code in this project so i won't post all of it. :wink:

The question is rather simple:

A method inside a sketch is called when an interrupt fires a CHANGE.
The method is the one being declared within attachedInterrupt() in the setup() block.

This method passes (when fired) the current time in mircoseconds to a specific method in a lib by using the function micros().

The reciveing method is a void method with one in parameter declared as volatile unsigned long.

Inside the lib method there is two rows of code:
One taking care of the time value (volatile unsigned long)
and one variable that toggles itself (volatile unsigned int)

Now the question: If i make the reciveing lib: setCurrDotTime, method more complex by adding som more code the time value varable isn't correctly updated everrytime anymore. I tested this by changing _Tick to Tick++, and got time values near zero and of course =0 when divided by 1000 as in printout above.

Code in Arduino sketch:

MotorcontrollerAll rightMotorcontroller();
MotorcontrollerAll leftMotorcontroller();

int lastLeftTick  = 0;
int lastRightTick = 0;

void rightENCInterrupt(){
  rightMotorcontroller.setCurrDotTime(micros());
}

void leftENCInterrupt(){
  leftMotorcontroller.setCurrDotTime(micros());
}

Setup(){
  Serial.begin(115200);
  attachInterrupt(0, rightENCInterrupt, CHANGE);
  attachInterrupt(1, leftENCInterrupt, CHANGE);
}

loop(){
  if (rightMotorcontroller.getTick() != lastRightTick) {
    Serial.print(millis());
    Serial.print("\t");
    Serial.println(rightMotorcontroller.getDotTimeDelta());
    
    lastRightTick = rightMotorcontroller.getTick();
  }

  if (leftMotorcontroller.getTick() != lastLeftTick) {
    Serial.print(millis());
    Serial.print("\t");
    Serial.println(leftMotorcontroller.getDotTimeDelta());

    lastLeftTick = leftMotorcontroller.getTick();
  }

Code in lib:

void MotorcontrollerAll::setCurrDotTime(volatile unsigned long aDotTime){
  _currDotTime = aDotTime;
  _Tick = !_Tick;
}

unsigned long MotorcontrollerAll::getDotTimeDelta(){
  _timeDelta = (_currDotTime - _prevDotTime) / 1000;
  _prevDotTime = _currDotTime;
  return _timeDelta;
}

bool  MotorcontrollerAll::getTick(){
  return _Tick;
}

SvanteJ:
It's more than 1000 lines of code in this project so i won't post all of it. :wink:

A pity.

I can read code. I can't read an essay describing code.

Particularly when the essay is written by the person having the problem. Quite possibly the real problem is something you haven't included because you don't perceive it as being a problem point.

You can attach .zip files.

Now the question: If i make the reciveing lib: setCurrDotTime, method more complex by adding som more code the time value varable isn't correctly updated everrytime anymore. I tested this by changing _Tick to Tick++, and got time values near zero and of course =0 when divided by 1000 as in printout above.

That's not a question, it's a statement.

And this is a contradiction...
:wink:
I posted it before i wrote.

Why____?

I still haven't seen code example where there is a lot more operations done in the trigged interrupt method (pluse calls it makes) then i have, so I still think there is some limitation behind this.

If someone is still willing to prove else I can create a more simple project where this problem is isolated (if its still there then)

Setup(){
  Serial.begin(115200);
  attachInterrupt(0, rightENCInterrupt, CHANGE);
  attachInterrupt(1, leftENCInterrupt, CHANGE);
}

loop(){
  if (rightMotorcontroller.getTick() != lastRightTick) {
    Serial.print(millis());
    Serial.print("\t");
    Serial.println(rightMotorcontroller.getDotTimeDelta());
    
    lastRightTick = rightMotorcontroller.getTick();
  }

These snippets are not even valid C++.

If I cut it down to remove the data declarations that I don't have, it becomes this:

Setup(){
  Serial.begin(115200);
}

loop(){
  }

That gives the following errors:

sketch_feb05a:1: error: ISO C++ forbids declaration of ‘Setup’ with no type
sketch_feb05a.ino: In function ‘int Setup()’:
sketch_feb05a.ino:3: warning: no return statement in function returning non-void
sketch_feb05a.ino: At global scope:
sketch_feb05a:5: error: ISO C++ forbids declaration of ‘loop’ with no type
sketch_feb05a.ino: In function ‘int loop()’:
sketch_feb05a:5: error: new declaration ‘int loop()’
/home/nick/Development/arduino-1.0.6/hardware/arduino/cores/arduino/Arduino.h:140: error: ambiguates old declaration ‘void loop()’
sketch_feb05a.ino:6: warning: no return statement in function returning non-void

You don't have return types. Setup is not capitalized.

So you are basically pasting made-up code and asking for help with it. That is, not only is the code not complete but the bits you post can't compile.

Enough time-wasting. Post your code or ask on another forum where they don't mind guessing, like a Psychics forum.

Im not saying loop and setup is valid C++.

Its wiring!

The lib is C++

For all of you... Sorry if you think i('m) messed up. I'm a newbie and i tried my best.

Thanks for the help so far!

Just to finish this i'll post a code sketch that shows what i get.
The code is declaring a input sensor on digital port 2 and i use a ir tranciever there.
The problem is the count which doesn't increment 1 at the time. At least not if you read the printouts.

And my question is what this depends on.
I have a way to get around it so my project is fine. But im curious and want to know about this.

Here's the code:

// This is a sketch to test an ir-transmitter
// Ir input on digital port 2 which is interrupt 0

#include "Arduino.h"

const byte ENC_RIGHT_READ_PIN      = 2;

// Vars
unsigned long lastCount            = 0;
unsigned long currCount            = 0;

volatile unsigned long count       = 0;
volatile unsigned long currDotTime = 0;


unsigned long prevDotTime          = 0;
unsigned long timeDelta            = 0;


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

  pinMode(ENC_RIGHT_READ_PIN, INPUT);

  attachInterrupt(0, ENCInterrupt, CHANGE);
}

void loop(){
  currCount = getCount();
  
  if (currCount > lastCount) {
    
    Serial.print("Time");
    Serial.print("\t");
    Serial.print(millis());
    Serial.print("\t");

    Serial.print("Interval");
    Serial.print("\t");
    Serial.print(getDotTimeDelta());
    Serial.print("\t");

    Serial.print("Count");
    Serial.print("\t");
    Serial.println(currCount);

    lastCount = currCount;
  }
}

void ENCInterrupt(){
  setCurrDotTime(micros());
}

void setCurrDotTime(volatile unsigned long aDotTime){
  currDotTime = aDotTime;
  count++;
}

unsigned long getDotTimeDelta(){
  timeDelta = (currDotTime - prevDotTime) / 1000;
  prevDotTime = currDotTime;
  return timeDelta;
}

unsigned long getCount(){
  return count;
}

My printouts when i move my finger in front of the sensor:
Time 940 Interval 940 Count 1
Time 941 Interval 1 Count 211
Time 942 Interval 0 Count 219
Time 999 Interval 57 Count 220
Time 1000 Interval 0 Count 252
Time 1855 Interval 855 Count 268
Time 1856 Interval 0 Count 318
Time 1871 Interval 18 Count 319
Time 1874 Interval 0 Count 844

BR

void ENCInterrupt() {
  setCurrDotTime(micros());
}

void setCurrDotTime(volatile unsigned long aDotTime) {
  currDotTime = aDotTime;
  count++;
}

As setCurrDotTime() does very little why not just do

void ENCInterrupt() {
  currDotTime = micros();
  count++;
}

And what is this all about ?

unsigned long getCount() {
  return count;
}