Does ISR interrupt arithmetic operation in the loop?

I'm using external Interrupt to note the time when a signal goes HIGH (saved under variable tRising) and LOW (saved under variable tFalling) using millis(). In the loop{}, I'm performing a subtraction of these two values to get deltaT. When I printed out all the values (tRising, tFalling and deltaT) using serial.print(), I realised the deltaT is not calculated correctly for a short period of time. Hence my question is, does the ISR affect the arithmetic operation in the loop?

#update: using nano every

All variables shared between ISR and main code have to be volatile.
All variables of the above type with more than one byte need atomic access in the main code.

Have a look here:

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

1 Like

Additionally, all variable used to store millis need to be unsigned long or uint32_t.

You also need to be careful of times when the input has gone HIGH, but not yet gone LOW, that will be using tFalling from the previous pulse.

If you are trying to measure the time period that the signal is high, then you probably want to set a flag inside your ISR when the signal goes low. That will tell your main loop that it is time to perform the calculation.

And, as @Whandall says, make sure that you declare any variables shared betwrrn your ISR and the main loop as volatile.

@Whandall @markd833 thank you for your responses.

Since i'm using millis() in the ISR and i've defined all my variables as unsigned long, can i still define them as volatile since, from what i understood, volatile allows only 8-bit data to be stored? And another info regarding my code. Since i have 4 speed sensors, I have 4 ISRs(all on different pins) and hence x4 variables .

Yes i've done so. Regarding tFalling from previous pulse, is it a good approach to later convert my tRising and tFalling to double and for the calculation deltaT(also defined as double) calculating the absolute difference between the two values?

That understanding is incorrect.

1 Like

For the most efficient and meaningful assistance, post your code.

Which Uno are you using that has 4 ISR pins?

attachInterrupt() - Arduino Reference

Millis() relies on interrupts to count, so it will never increment inside an ISR. Micros will increment inside a ISR but will overflow in about 500us.

Means those programs relying on millis() will not be done in a timely fashion.

volatile has nothing to do with the storage of data. It tells the compiler that this variable can change its value without the compiler being able to detect it. It only influences the optimization of the compiler, nothing else.

An ISR should never run as long that millis would have to be incremented.

As millis also micros uses unsigned long. It will overflow after 4,294,967,295 µs or about 71.5 minutes.

I'm using Nano Every, where all digital pins are external interrupt pins.

So, I need to note the time at which Interrupt happens. So is it possible at all to use millis() in ISR?

It's always a good idea to tell what Arduino you use in the first post. If there is nothing mentioned everyone assumes a 'basic' Arduino ( UNO,Nano ).

Yes, you can ask millis() in an ISR. You will get the last value that was valid before the ISR. If millis() had to be incremented while your ISR is running, it will be incremented after that. Therefore your ISR should be short.

No. That is the overflow interval for micros() when interrupts are running. When inside an ISR, interrupts are stopped.
micros() uses the same interrupt as millis() and will lose time if interrupts are disabled too long. On an UNO, NANO, or MEGA the interrupt occurs every 1024 microseconds. If interrupts are disabled for 2048 microseconds then the first one will be lost and millis() will be slow by 1.024 milliseconds. The value of micros may go backward during that time.

1 Like

Obviously I misunderstood @Idahowalker if his overflow time was ment within an interrupt. But an ISR should never run for 500µs and within an ISR you don't know when the micros overflow occurs. It depends on the ISR time related to the millis/micros timer counter value. I think that is what you ment with micros going backward.
And all depends on the platform beeing used as well. And if I remember correctly, on AVR the timer 0 is clocked with 4µs. So it will overflow every 1024µs, not 500µ.

Thank you for all the responses and sorry for the late reponse. I have found the initial point of my problem and hope that someone could provide me a solution.

So I have the following code.

#define PIN1 2 //Hinten rechts
#define PIN2 3 //Hinten links
#define PIN3 4 //Vorne rechts
#define PIN4 5 //Vorne links
volatile unsigned long  tRising1, tFalling1, tLog1;
volatile unsigned long  tRising2, tFalling2, tLog2;
volatile unsigned long  tRising3, tFalling3, tLog3;
volatile unsigned long  tRising4, tFalling4, tLog4;

double  tRising1_d, tFalling1_d;
double  tRising2_d, tFalling2_d;
double tRising3_d, tFalling3_d;
double  tRising4_d, tFalling4_d;

void setup() {

  tRising1 = 0;
  tFalling1 = 0;
  tLog1 = 0;
  tRising2 = 0;
  tFalling2 = 0;
  tLog2 = 0;
  tRising3 = 0;
  tFalling3 = 0;
  tLog3 = 0;
  tRising4 = 0;
  tFalling4 = 0;
  tLog4 = 0;

  pinMode(PIN1, INPUT);
  pinMode(PIN2, INPUT);
  pinMode(PIN3, INPUT);
  pinMode(PIN4, INPUT);
  attachInterrupt(digitalPinToInterrupt(PIN1), pulse1, CHANGE);
  attachInterrupt(digitalPinToInterrupt(PIN2), pulse2, CHANGE);
  attachInterrupt(digitalPinToInterrupt(PIN3), pulse3, CHANGE);
  attachInterrupt(digitalPinToInterrupt(PIN4), pulse4, CHANGE);

  Serial.begin(9600);
  Serial1.begin(9600);
}

void loop() {
  tRising1_d = (double)tRising1;
  tRising2_d = (double)tRising2;
  tRising3_d = (double)tRising3;
  tRising4_d = (double)tRising4;
  tFalling1_d = (double)tFalling1;
  tFalling2_d = (double)tFalling2;
  tFalling3_d = (double)tFalling3;
  tFalling4_d = (double)tFalling4;

 Serial.println("Start");
  Serial.print(tRising1_d); Serial.print(" // "); Serial.print(tFalling1_d); Serial.print(" // "); Serial.print(tRising1); Serial.print(" // "); Serial.println(tFalling1);
  Serial.print(tRising2_d); Serial.print(" // "); Serial.print(tFalling2_d); Serial.print(" // "); Serial.print(tRising2); Serial.print(" // "); Serial.println(tFalling2);
  Serial.print(tRising3_d); Serial.print(" // "); Serial.print(tFalling3_d); Serial.print(" // "); Serial.print(tRising3); Serial.print(" // "); Serial.println(tFalling3);
  Serial.print(tRising4_d); Serial.print(" // "); Serial.print(tFalling4_d); Serial.print(" // "); Serial.print(tRising4); Serial.print(" // "); Serial.println(tFalling4);
}

void pulse1() {
  uint8_t pinLevel;

  pinLevel = digitalRead(PIN1);
  if (pinLevel) {
    processRisingEdge1();    // must be a rising edge
  } else {
    processFallingEdge1();   // must be a falling edge
  }
}

void processRisingEdge1() {
  // Do rising edge stuff
  tRising1 = millis();
  tLog1 = millis();
}

void processFallingEdge1() {
  // Do falling edge stuff
  tFalling1 = millis();
}

For the ISR. I have only copied one ISR funtion. The 3 other ISRs are identical. (with different name and variable)

So the problem is when i convert my tRising and tFalling to doubles, which i need for further calculation, the values are often not correct. For example,

11:27:55.348 -> Start
11:27:55.348 -> 607332.00 // 607333.00 // 607365 // 607376
11:27:55.395 -> 607332.00 // 607330.00 // 607412 // 607422
11:27:55.442 -> 607333.00 // 607330.00 // 607459 // 607468
11:27:55.489 -> 607331.00 // 607333.00 // 607503 // 607513
11:27:55.536 -> Stop

What wrong with the way the numbers are being printed out?

You are only seeing 2 digits in the printout? Well you can see 6 if you did the other things Serial.print(tRising4_d,6); but you only get to see a max of 6 digits after the decimal place.

You need to temporarily disable interrupts and make a copy of the variables so that you can work with the data without the risk of an interrupt occurring while accessing the data - unsigned long is four bytes, an interrupt can occur between accessing each individual byte. The risk of this happening is even greater since you are doing a conversion to float, and that takes considerable time. If the timing is especially tight, you may want to re-enable interrupts between variables, but since you are timing in milliseconds that is unlikely to be needed.

The explicit cast to (double) is unnecessary, the compiler knows to do the conversion. I'm not certain about the Nano Every, but double is generally not supported on the 8-bit processors, instead being implemented as float, so you will lose a few digits of precision in the conversion.

Also, if possible increase the baud rate for Serial, part of your problem with the posted code is that the Serial buffer if getting full, at which point the print function will wait for space to be available in the serial buffer before returning.

  noInterrups();
  unsigned long tRising1temp = (double)tRising1;
  unsigned long tRising2temp = (double)tRising2;
  unsigned long tRising3temp = (double)tRising3;
  unsigned long tRising4temp = (double)tRising4;
  unsigned long tFalling1temp = (double)tFalling1;
  unsigned long tFalling2temp = (double)tFalling2;
  unsigned long tFalling3temp = (double)tFalling3;
  unsigned long tFalling4temp = (double)tFalling4;
  interrupts();

  tRising1_d = tRising1temp;
  tRising2_d = tRising2temp;
  tRising3_d = tRising3temp;
  tRising4_d = tRising4temp;
  tFalling1_d = tFalling1temp;
  tFalling2_d = tFalling2temp;
  tFalling3_d = tFalling3temp;
  tFalling4_d = tFalling4temp;

the first 2 numbers in a line is supposed to be the type double of the last 2 numbers. The conversion is not correct.

I'm not sure if I understand your question or maybe I don't understand the printed values. But, shouldn't the conversion of unsigned long 607365 to doubles be 607365.00 and not 607332.00 ?