Setting elapsedmillis within interrupt?

Status
Not open for further replies.

cermak

Member
I have a teensy 3.2 that I am using to count TTL pulses coming from a camera (one per frame, roughly 20Hz). I am using an interrupt to count rising edges on pin 8. Every 5 ms, the teensy reports the frame count to a computer via Serial. If for some reason the camera doesn't send a pulse for 2 seconds, the counter resets to zero.

I thought this was working perfectly (it seems to work great for periods up to a minute), but when I ran the camera for 30 minutes, I find that the frameNum counter seems to randomly reset to 0 two or three times (I can find no pattern to when this happens - it doesn't look like overflow or anything). I am quite certain that the reset is not occurring due to a two-second delay - the recorded timestamp on the computer shows beautiful 5ms spacing on the serial messages right around where the counter resets.

My best guess is that I have done something horrendously dangerous by setting tLastNewFrame=0 inside of my interrupt function. If this is the case, it'd be great if someone could tell me how to do this more safely? If that sounds unlikely, what else might cause such a rare reset behavior?

Thanks in advance!

Code:
volatile unsigned long frameNum; 
const int broadcastPeriod = 5000; // us
const int resetTime = 2000;            // ms
elapsedMicros t;
elapsedMillis tLastNewFrame;

void newFrame(){
  frameNum++;
  tLastNewFrame = 0;
}

void setup() {
  Serial.begin(9600);
  pinMode(1, OUTPUT);
  frameNum=0;
  attachInterrupt(8, newFrame, RISING);
  t=0;
}

void loop() {
  if (t > broadcastPeriod){
    Serial.println(frameNum);
    t=0;
  }
  if (tLastNewFrame > resetTime)
    frameNum=0;
}
 
did you try to declare
volatile elapsedMillis tLastNewFrame;
?
Unfortunately, the compiler doesn't like that because of passing the 'this' pointer to an overloaded operator that's not expecting a volatile pointer:
sketch_jan21a:25: error: passing 'volatile elapsedMillis' as 'this' argument discards qualifiers [-fpermissive]
So, declare a volatile uint32_t (volatile unsigned long) instead and use it to handle millis() the "old school" way rather than an elapsedMillis object.
 
I'd tried the volatile elapsedMillis approach and found the compiler didn't like it, and will try doing it directly via millis() with a a volatile uint32_t time variable. Regardless of whether that fixes it though, I'm curious though whether one would expect it to fail the way I did it originally? I would sort of imagine the compiler would treat an elapsedMillis object as volatile already (though I haven't looked into the source).
 
I'm running a slightly modified copy of your program on a Teensy 3.2 here.

Since I don't have your camera hardware to create the pulses, I added to lines to output a PWM waveform on pin 3.

Code:
volatile unsigned long frameNum; 
const int broadcastPeriod = 5000; // us
const int resetTime = 2000;            // ms
elapsedMicros t;
elapsedMillis tLastNewFrame;

void newFrame(){
  frameNum++;
  tLastNewFrame = 0;
}

void setup() {
  analogWriteFrequency(3, 20.0);
  analogWrite(3, 5); // short pulses at 20 Hz
  Serial.begin(9600);
  pinMode(1, OUTPUT);
  frameNum=0;
  attachInterrupt(8, newFrame, RISING);
  t=0;
}

void loop() {
  if (t > broadcastPeriod){
    Serial.println(frameNum);
    t=0;
  }
  if (tLastNewFrame > resetTime)
    frameNum=0;
}

I have a wire connected between pins 3 and 8, so the program sees an endless stream of pulses at 20 Hz.

DSC_0312_web.jpg

I'm watching the number scroll rapidly in the Arduino Serial Monitor. So far it's been running for about 6 minutes, with no apparent reset back to zero.
 
Last edited:
It's been running here for over 3.5 hours, no reset back to zero.

Any chance you could try running this PWM version?
 
Thanks! I'll run your test today and get back to you. It sounds like what I'm seeing isn't obviously due to my surface-level understanding of how interrupts or elapsedMillis objects behave. Maybe the culprit is something outside of my code? I'll keep digging around, and will get back to you after I try your PWM input program.
 
I ran it for 3.5 hours and saw no resets. I switched over to my camera, and saw a couple resets within half an hour. The camera is outputting a 50us 5V TTL pulse, that up until now I've assumed was perfect, but now I'm suspicious of.

Is it possible to interrupt an interrupt? Like, if two rising edges occurred extremely close to one another, could the increment of frameNum interrupt itself? My googling-around-based impression is that this should not happen and that the 2nd rising edge would be ignored if it occurred while the first ISR call was being executed.

I could also switch the code to polling for rising edges, but I really need to make sure I don't accidentally miss a pulse due to being busy with the Serial or something.

Outside of that, I'm confused what in my code could possibly be causing this other than a 2-second failure (which, according to the computer timestamps from recording data from the serial, has not happened when the reset occurs).

Thanks again for your help!
 
Is it possible to interrupt an interrupt?

Simple answer is no. The hardware prevents recursive interrupts.

The more complicated answer is each interrupt has a priority setting, from 0 to 255, where lower numbers mean the interrupt has high priority. By default, most interrupts use priority 128. The systick interrupt, which updates millis, elapsedMills, elapsedMicros, etc has priority 32.

So while your interrupt function is running at priority 128, all other interrupts from 128-255 are blocked from running. But if a higher priority interrupt happens, it is allowed to interrupt your interrupt. So technically, and interrupt can interrupt another interrupt, if it has a higher (lower number) priority setting. But the same interrupt can number recursively re-interrupt itself, because it has the same priority level. Now if you go and mess with your interrupt's priority within its ISR, well, that's just crazy and I have no idea what would happen. Don't do that.

But for this usage, you can rule out the possibility that a 2nd edge or noise is causing this interrupt to somehow interrupt itself. The hardware absolutely prevents that from happening. Such a signal at the pin would cause the interrupt's flag to be set again, so when you return from the ISR function, it would be immediately run again. The hardware does have a special optimization called "tail chaining" where it avoids needlessly restoring & then re-saving the main program context when your function returns and the hardware known another interrupt is pending at the same or lower priority. It will re-run your function with very little delay if another edge has set the flag while your code ran for the first one.

The camera is outputting a 50us 5V TTL pulse, that up until now I've assumed was perfect, but now I'm suspicious of.

That does seem likely.

I left the PWM code running all night. It's counted up to ~1247000.

If you have a modern oscilloscope with pulse width triggering (older & cheaper ones only offer edge triggers), you could try setting it to trigger on a low pulse wider than ~50us, or even 2 seconds if your scope offers a setting that high.
 
The program is missing a PINMODE(8,INPUT) statement but apparently that doesn't matter here.

One thing I can think of that explains the described behavior is if the Teensy in resetting. If reset it would start over at zero.
Reset caused by what: flaky power cable, ground loops between camera and computer, static ?
 
A bit more debugging revealed that the program was evaluating "if(tLastNewFrame > resetTime)" as true, under conditions where that absolutely should not have been true.

I tried to capture this by printing tLastNewFrame inside the if statement, but tLastNewFrame evaluated to 0 when I then printed it (presumably it had updated since the condition was evaluated).

To get around this, I assigned tLastNewFrame to a new variable, used this for the condition, and then printed the state of the variable.

Code:
volatile unsigned long frameNum; 
const int broadcastPeriod = 5000; // us
const int resetTime = 2000;    // ms
elapsedMicros t;
elapsedMillis tLastNewFrame;

void newFrame(){
  frameNum++;
  tLastNewFrame = 0;
}
void setup() {
  Serial.begin(9600);
  Serial.println("Booting up!");
  while(!Serial.available())        //only start on character press (enables checking for reboot on serial in arduino)
    Serial.flush();
  pinMode(1, OUTPUT);
  pinMode(8, INPUT);
  frameNum=0;
  attachInterrupt(8, newFrame, RISING);
  t=0;
  delay(1000);
}

void loop() {
  if (t > broadcastPeriod){
    Serial.println(frameNum);
    t=0;
  }
  unsigned long tNow = tLastNewFrame;
  if (tNow> resetTime){
    Serial.println("last newFrameTime is");
    Serial.println(tNow);
    Serial.println("RESETTING");
    while (true); //hang forever so i can see the serial output when this goes down. 
  }
}


Output (in arduino serial terminal, with timestamp)
18:57:51.761 -> 6021
18:57:51.761 -> 6021
18:57:51.761 -> 6021
18:57:51.761 -> 6021
18:57:51.795 -> 6022
18:57:51.795 -> 6022
18:57:51.795 -> 6022
18:57:51.795 -> 6022
18:57:51.795 -> 6022
18:57:51.828 -> last newFrameTime is
18:57:51.828 -> 4294967295
18:57:51.828 -> RESETTING

This suggests that the elapsed time is somehow being briefly corrupted to -1?
 
Add the interrupts wrapper to this version:

noInterrupts();
unsigned long tNow = tLastNewFrame;
interrupts();
 
32 bit-transfers are atomic on a 32-bit ARM.

Yes and you probably know more about this than I do. I know very little about C++ objects.
Still, tLastNewFrame is not a simple variable and I suspect the answer to this puzzle lies somewhere in these overloaded functions and operators:

So I think the wrapper is still worth a try.

Code:
class elapsedMillis
{
private:
	unsigned long ms;
public:
	elapsedMillis(void) { ms = millis(); }
	elapsedMillis(unsigned long val) { ms = millis() - val; }
	elapsedMillis(const elapsedMillis &orig) { ms = orig.ms; }
	operator unsigned long () const { return millis() - ms; }    Maybe here?
	elapsedMillis & operator = (const elapsedMillis &rhs) { ms = rhs.ms; return *this; }
	elapsedMillis & operator = (unsigned long val) { ms = millis() - val; return *this; }  Maybe here?
	elapsedMillis & operator -= (unsigned long val)      { ms += val ; return *this; }
	elapsedMillis & operator += (unsigned long val)      { ms -= val ; return *this; }
	elapsedMillis operator - (int val) const           { elapsedMillis r(*this); r.ms += val; return r; }
	elapsedMillis operator - (unsigned int val) const  { elapsedMillis r(*this);
 
This program will log errors on my Teensy 3.6. The idea of variables t1,t2,t3,t4,t5 is to align 3 events in time.
First is the millis() tick over to a new value
Second is the evaluation of this statement: if (tLastNewFrame > resetTime){
Third is the interrupt on pin 8.

Code:
volatile unsigned long frameNum; 
const int broadcastPeriod = 5000; // us
const int resetTime = 2000;            // ms
elapsedMicros t;
elapsedMillis tLastNewFrame;

unsigned int resets;
unsigned int dly = 500;    // 5.00
volatile unsigned int t1,t2,t3,t4,t5;

void newFrame(){
  t5 = micros()%1000;
  frameNum++;
  tLastNewFrame = 0;
}

void setup() {
  analogWriteFrequency(3, 20.001);
  analogWrite(3, 5); // short pulses at 20 Hz
  Serial.begin(9600);
  pinMode(1, OUTPUT);
  frameNum=0;
  attachInterrupt(8, newFrame, RISING);
  t=0;
}

void loop() {
static unsigned int m = 0;
static int sec = 0;    // some fraction of a second
  
  if (t > broadcastPeriod){
    Serial.print(frameNum); Serial.print(" "); Serial.print(dly); Serial.print(" ");
    Serial.print(t5);  Serial.print(" ");
    Serial.print("Resets ");
    Serial.println(resets);
    t=0;
  }
  t4 = t3;
  t3 = micros();
  t1 = millis();

  if (tLastNewFrame > resetTime){
    frameNum=0;
    ++resets;
  }
  t2 = millis();

  if( t1 != t2 ) Serial.print("E ");   // Serial.println("     Event #1 time");

  
  t3 = t4 - t3;
  if( t3 < 1000 ) ++dly;      // I think the sometimes serial prints make
  if( t3 > 1000 ) --dly;      // tuning the delay time difficult
  if( dly < 1 ) dly += 1000;

  if( m != millis()){
    m = millis();
    if( ++sec > 126 ){   // too frequent analogWriteFreq really messes things up
      sec = 0;
      if( t5 > 500 && t5 < 998) analogWriteFrequency(3, 19.9995);
      if( t5 <= 500 && t5 > 5) analogWriteFrequency(3, 20.0005);      
    }
  }
  delayMicroseconds(dly/100);
}
 
I can also replicate this using a 2nd teensy (a 3.5 but i figure that's irrelevant) generating 50us pulses at 20Hz.

Teensy 3.5 acting as pulse generator on pin 1:
Code:
void setup() {
  pinMode(1, OUTPUT);
}

void loop() {
  digitalWrite(1, HIGH);
  delayMicroseconds(50);
  digitalWrite(1, LOW);
  delay(50);
}

Teensy 3.2, receiving pulses on pin 8:
Code:
volatile unsigned long frameNum; 
const int broadcastPeriod = 5000; // us
const int resetTime = 2000;    // ms
elapsedMicros t;
elapsedMillis tLastNewFrame;

void newFrame(){
  frameNum++;
  tLastNewFrame = 0;
}
void setup() {
  Serial.begin(9600);
  pinMode(8, INPUT);
  frameNum=0;
  attachInterrupt(8, newFrame, RISING);
  t=0;
}

void loop() {
  if (t > broadcastPeriod){
    Serial.println(frameNum);
    t=0;
  }
  unsigned long tlnf = tLastNewFrame;
  if (tlnf > resetTime){
    Serial.println("Reached condition, halting!"); 
    Serial.println("t last new frame = ");
    Serial.println(tlnf);
    while(true);
    frameNum=0;
  }
}


Teensy 3.2 output:
14:33:57.423 -> 9894
14:33:57.423 -> 9894
14:33:57.472 -> 9895
14:33:57.472 -> 9895
14:33:57.472 -> 9895
14:33:57.472 -> 9895
14:33:57.472 -> 9895
14:33:57.472 -> 9895
14:33:57.472 -> 9895
14:33:57.472 -> 9895
14:33:57.506 -> Reached condition, halting!
14:33:57.506 -> t last new frame =
14:33:57.506 -> 4294967295


I seem to get the same behavior WITHOUT an elapsedMillis object.
Code:
volatile unsigned long frameNum; 
const int broadcastPeriod = 5000; // us
const int resetTime = 2000;    // ms
elapsedMicros t;
volatile unsigned long tLastNewFrameMillis;

void newFrame(){
  frameNum++;
  tLastNewFrameMillis = millis();
}
void setup() {
  Serial.begin(9600);
  pinMode(8, INPUT);
  frameNum=0;
  attachInterrupt(8, newFrame, RISING);
  t=0;
}

void loop() {
  if (t > broadcastPeriod){
    Serial.println(frameNum);
    t=0;
  }

  unsigned long currentTime = millis();
  if (currentTime-tLastNewFrameMillis > resetTime){
    Serial.println("Reached condition, halting!"); 
    Serial.println("tLastNewFrameMillis = ");
    Serial.println(tLastNewFrameMillis);
    Serial.println("currentTime = ");
    Serial.println(currentTime);
    while(true);
    frameNum=0;
  }
}

Output:
14:50:55.399 -> 11424
14:50:55.432 -> 11425
14:50:55.432 -> 11425
14:50:55.432 -> 11425
14:50:55.432 -> 11425
14:50:55.432 -> 11425
14:50:55.432 -> 11425
14:50:55.482 -> 11425
14:50:55.482 -> 11425
14:50:55.482 -> Reached condition, halting!
14:50:55.482 -> tLastNewFrameMillis =
14:50:55.482 -> 572179
14:50:55.482 -> currentTime =
14:50:55.482 -> 572178

From this, I'm concluding that any calculation of the sort (millis() - tVar) where tVar can be assigned millis() in an interrupt is susceptible to be interrupted between evaluation of millis() and subtraction, and therefore may reasonably return -1. Clearly, the same logic holds for elapsedMillis objects, which can be interrupted during evaluation and thus produce a -1.

In the first code above, if I replace "unsigned long tlnf = tLastNewFrame;" with "signed long tlnf = tLastNewFrame;", I do not have any problem.

Thank you all for your help debugging this!
 
@cermak: what happens if you take your second T3.2 code above and don't blast the serial port every 5ms? Make it every 5 seconds.
 
the 5 ms pulse is to emulate the actual needed camera device.

If the attached interrupt were set at the same priority (#32) of the systick it wouldn't get interrupted during millis()/micros()

Also the T_3's have a cycle counter that is updated on each tick at F_CPU. If that is enabled in setup() then time can be tracked independent of millis interrupt updated time.
Code:
  ARM_DEMCR |= ARM_DEMCR_TRCENA; // Assure Cycle Counter active
  ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;

Then getting this number will change over time by F_CPU counts per second: uint32_tLastCnt = ARM_DWT_CYCCNT;

With scaling to F_CPU for the desired time interval the code above could replace the millis with references to ARM_DWT_CYCCNT for start and running time.

Using uint32_t is critical to have time values that are that type compare properly to each other when they wrap around to zero and keep going.
 
Understood. My question was whether it's necessary to print out from the Serial port every 5ms.

opps ... Missed that detail - indeed that is a lot of printing - could at least be buffered to only print on change. But the problem is _isr fight over time and keeping it atomic ... just (hopefully) worked through that on PJRC's new beta hardware with cycle counter for getting micros.
 
At first I thought that this issue might be due to the rapid (every 5 ms) printing to the Serial. So, I upped the baud to 115200 and decreased the printing rate to once every 5 seconds. The code below is very similar to that in Post #19:

A T3.2 supplies interrupt pulses at ~20 Hz:
Code:
void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  digitalWrite(13, HIGH);
  delayMicroseconds(50);
  digitalWrite(13, LOW);
  delay(50);
}

The pulses trigger interrupts on a second T3.2:
Code:
#include "Arduino.h"

volatile uint32_t frameNum;
const uint32_t broadcastPeriod = 5000000; // us
const uint32_t resetTime = 2000;    // ms
const uint8_t interruptPin = 16;
elapsedMicros t;
volatile uint32_t tLastNewFrameMillis;
uint32_t eventCount = 0;

void newFrame() {
	frameNum++;
	tLastNewFrameMillis = millis();
}
void setup() {
	Serial.begin(115200);
	delay(1000);
	Serial.println("Starting");
	pinMode(interruptPin, INPUT);
	frameNum = 0;
	attachInterrupt(digitalPinToInterrupt(interruptPin), newFrame, RISING);
	t = 0;
}

void loop() {
	uint32_t localFrameNum, localFameMillis;
	noInterrupts();
	localFrameNum = frameNum;
	localFameMillis = tLastNewFrameMillis;
	interrupts();
	if (t > broadcastPeriod) {
		Serial.print("Frame Number: ");
		Serial.print(localFrameNum);
		Serial.print(", Event Count: ");
		Serial.println(eventCount);
		t = 0;
	}

	uint32_t currentTime = millis();
	if (currentTime - localFameMillis > resetTime) {
		Serial.print("Event Detected, Frame Number: ");
		Serial.print(localFrameNum);
		Serial.print(", tLastNewFrameMillis: ");
		Serial.print(localFameMillis);
		Serial.print(", currentTime: ");
		Serial.println(currentTime);
		frameNum = 0;
		eventCount++;
	}
}

I ran this setup for more than 14 hours with no anomalous events detected. Here's the tail of the Serial log:
Code:
Frame Number: 1069156, Event Count: 0
Frame Number: 1069656, Event Count: 0
Frame Number: 1069756, Event Count: 0
Frame Number: 1069856, Event Count: 0
Frame Number: 1069956, Event Count: 0
Frame Number: 1070055, Event Count: 0
Frame Number: 1070155, Event Count: 0
Frame Number: 1070255, Event Count: 0
Frame Number: 1070355, Event Count: 0
Frame Number: 1070455, Event Count: 0
Frame Number: 1070555, Event Count: 0
Frame Number: 1070655, Event Count: 0
Frame Number: 1070755, Event Count: 0
Frame Number: 1070855, Event Count: 0
Frame Number: 1070954, Event Count: 0
Frame Number: 1071054, Event Count: 0
Frame Number: 1071154, Event Count: 0
Frame Number: 1071254, Event Count: 0
Frame Number: 1071354, Event Count: 0
Frame Number: 1071454, Event Count: 0

So, I figured I was on to something. I dropped the baud back down to 9600 and upped the printing rate back to once every 5 ms:
Code:
#include "Arduino.h"

volatile uint32_t frameNum;
const uint32_t broadcastPeriod = 5000; // us
const uint32_t resetTime = 2000;    // ms
const uint8_t interruptPin = 16;
elapsedMicros t;
volatile uint32_t tLastNewFrameMillis;
uint32_t eventCount = 0;

void newFrame() {
	frameNum++;
	tLastNewFrameMillis = millis();
}
void setup() {
	//Serial.begin(115200);
	Serial.begin(9600);
	delay(1000);
	Serial.println("Starting");
	pinMode(interruptPin, INPUT);
	frameNum = 0;
	attachInterrupt(digitalPinToInterrupt(interruptPin), newFrame, RISING);
	t = 0;
}

void loop() {
	uint32_t localFrameNum, localFameMillis;
	noInterrupts()
	;
	localFrameNum = frameNum;
	localFameMillis = tLastNewFrameMillis;
	interrupts()
	;
	if (t > broadcastPeriod) {
		Serial.print("Frame Number: ");
		Serial.print(localFrameNum);
		Serial.print(", Event Count: ");
		Serial.println(eventCount);
		t = 0;
	}

	uint32_t currentTime = millis();
	if (currentTime - localFameMillis > resetTime) {
		Serial.print("Event Detected, Frame Number: ");
		Serial.print(localFrameNum);
		Serial.print(", tLastNewFrameMillis: ");
		Serial.print(localFameMillis);
		Serial.print(", currentTime: ");
		Serial.println(currentTime);
		frameNum = 0;
		eventCount++;
	}
}

Ran this configuration for more than 45 minutes without issue:
Code:
Frame Number: 57701, Event Count: 0
Frame Number: 57701, Event Count: 0
Frame Number: 57701, Event Count: 0
Frame Number: 57701, Event Count: 0
Frame Number: 57701, Event Count: 0
Frame Number: 57701, Event Count: 0
Frame Number: 57701, Event Count: 0
Frame Number: 57701, Event Count: 0
Frame Number: 57701, Event Count: 0
Frame Number: 57701, Event Count: 0
Frame Number: 57702, Event Count: 0
Frame Number: 57702, Event Count: 0
Frame Number: 57702, Event Count: 0
Frame Number: 57702, Event Count: 0
Frame Number: 57702, Event Count: 0
Frame Number: 57702, Event Count: 0
Frame Number: 57702, Event Count: 0
Frame Number: 57702, Event Count: 0
Frame Number: 57702, Event Count: 0
Frame Number: 57702, Event Count: 0
Frame Number: 57703, Event Count: 0
Frame Number: 57703, Event Count: 0
Frame Number: 57703, Event Count: 0
Frame Number: 57703, Event Count: 0
Frame Number: 57703, Event Count: 0
Frame Number: 57703, Event Count: 0
Frame Number: 57703, Event Count: 0
Frame Number: 57703, Event Count: 0
Frame Number: 57703, Event Count: 0
Frame Number: 57703, Event Count: 0
Frame Number: 57704, Event Count: 0
Frame Number: 57704, Event Count: 0
Frame Number: 57704, Event Count: 0
Frame Number: 57704, Event Count: 0
Frame Number: 57704, Event Count: 0
Frame Number: 57704, Event Count: 0
Frame Number: 57704, Event Count: 0
Frame Number: 57704, Event Count: 0
Frame Number: 57704, Event Count: 0
Frame Number: 57704, Event Count: 0
Frame Number: 57705, Event Count: 0
Frame Number: 57705, Event Count: 0
Frame Number: 57705, Event Count: 0
Frame Number: 57705, Event Count: 0
Frame Number: 57705, Event Count: 0
Frame Number: 57705, Event Count: 0
Frame Number: 57705, Event Count: 0
Frame Number: 57705, Event Count: 0
Frame Number: 57705, Event Count: 0
Frame Number: 57705, Event Count: 0
Frame Number: 57706, Event Count: 0
Frame Number: 57706, Event Count: 0
Frame Number: 57706, Event Count: 0
Frame Number: 57706, Event Count: 0
Frame Number: 57706, Event Count: 0
Frame Number: 57706, Event Count: 0
Frame Number: 57706, Event Count: 0
Frame Number: 57706, Event Count: 0
Frame Number: 57706, Event Count: 0
Frame Number: 57706, Event Count: 0


I don't know if the critical section for accessing the volatile variables is necessary. But, as far as I can tell, there is no evidence of this alleged mysterious interaction:
From this, I'm concluding that any calculation of the sort (millis() - tVar) where tVar can be assigned millis() in an interrupt is susceptible to be interrupted between evaluation of millis() and subtraction, and therefore may reasonably return -1
 
Last edited:
Hi gfvalvo,
I agree with your assessment and I think I worded my newfound understanding poorly. I agree that I would not expect your code to have this problem, as you're enforcing an evaluation order:
1) assign localFameMillis = tLastNewFrameMillis;
2) assign currentTime = millis();
3) evaluate if (currentTime - localFameMillis > resetTime)

Even if an interrupt occurs between 1 and 2, or between 2 and 3, it doesnt affect the outcome; currentTime must be >= localFameMillis.

However, in my case, I evaluated the following statements in order:
1) unsigned long currentTime = millis();
2) if (currentTime-tLastNewFrameMillis > resetTime)
where tLastNewFrameMillis is volatile and liable to be updated to the latest value of millis() between statements 1 and 2, potentially producing a situation where tLastNewFrameMillis is greater than currentTime.

This is what I meant when I said evaluation of differences like currentTime-tLastnewFrameMillis (or presumably, equivalently, millis()-tLastNewFrameMillis) might be interrupted between evaluation of millis() and fetching tLastNewFrameMIllis for subtraction.

This is not surprising, and I should have expected this. The reason I was originally confused is because I used an elapsedMillis object, which produced the same behavior (because I didn't realize could be interrupted during evaluation). Here's a simple example of what I was initially confused about, but now think I understand.

Code:
elapsedMillis t;
IntervalTimer myTimer;

void myISR(){
  t=0;
}

void setup() {
  Serial.begin(9600);
  myTimer.begin(myISR, 19);  // 19 chosen out of a hat.
  t=0;
}

void loop() {
   // if time is greater than 1000 seconds (which shouldnt happen if we're running myISR every 19 microseconds).
   if (t > 1e6){ 
     Serial.println("you might not expect this to print, but it does");
     // this prints because sometimes t evaluates to 2^32-1, because evaluating t is not atomic
   }
}
 
Status
Not open for further replies.
Back
Top