Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 25 of 25

Thread: Setting elapsedmillis within interrupt?

  1. #1
    Junior Member
    Join Date
    Jan 2019
    Posts
    7

    Setting elapsedmillis within interrupt?

    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;
    }

  2. #2
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,452
    did you try to declare

    volatile elapsedMillis tLastNewFrame;

    ?

  3. #3
    Senior Member
    Join Date
    Feb 2017
    Posts
    299
    Quote Originally Posted by Theremingenieur View Post
    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.

  4. #4
    Junior Member
    Join Date
    Jan 2019
    Posts
    7
    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).

  5. #5
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,296
    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.

    Click image for larger version. 

Name:	DSC_0312_web.jpg 
Views:	17 
Size:	137.3 KB 
ID:	15698

    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 by PaulStoffregen; 01-21-2019 at 08:59 PM.

  6. #6
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,296
    It's been running here for over 3.5 hours, no reset back to zero.

    Any chance you could try running this PWM version?

  7. #7
    Junior Member
    Join Date
    Jan 2019
    Posts
    7
    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.

  8. #8
    Junior Member
    Join Date
    Jan 2019
    Posts
    7
    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!

  9. #9
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,296
    Quote Originally Posted by cermak View Post
    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.

  10. #10
    Senior Member
    Join Date
    May 2017
    Posts
    208
    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 ?

  11. #11
    Junior Member
    Join Date
    Jan 2019
    Posts
    7
    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?

  12. #12
    Senior Member
    Join Date
    Feb 2017
    Posts
    299
    Did you try switching to a volatile uint32_t instead of an elapsedMillis variable as suggested in Post #3?

  13. #13
    Senior Member
    Join Date
    May 2017
    Posts
    208
    Add the interrupts wrapper to this version:

    noInterrupts();
    unsigned long tNow = tLastNewFrame;
    interrupts();

  14. #14
    Senior Member
    Join Date
    Feb 2017
    Posts
    299
    Quote Originally Posted by rcarr View Post
    Add the interrupts wrapper to this version:

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

  15. #15
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,296
    Quote Originally Posted by cermak View Post
    This suggests that the elapsed time is somehow being briefly corrupted to -1?
    Can you get this to happen when using the PWM signal? Or any other way I can reproduce here without that camera?

  16. #16
    Senior Member
    Join Date
    May 2017
    Posts
    208
    Quote Originally Posted by gfvalvo View Post
    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);

  17. #17
    Senior Member
    Join Date
    Feb 2017
    Posts
    299
    Quote Originally Posted by rcarr View Post
    So I think the wrapper is still worth a try.
    You're right. Although getting rid of it all together and going with a plain 'volatile unsigned long' would be the thing I'd try first.

  18. #18
    Senior Member
    Join Date
    May 2017
    Posts
    208
    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);
    }

  19. #19
    Junior Member
    Join Date
    Jan 2019
    Posts
    7
    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!

  20. #20
    Senior Member
    Join Date
    Feb 2017
    Posts
    299
    @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.

  21. #21
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,012
    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.

  22. #22
    Senior Member
    Join Date
    Feb 2017
    Posts
    299
    Quote Originally Posted by defragster View Post
    the 5 ms pulse is to emulate the actual needed camera device.
    Understood. My question was whether it's necessary to print out from the Serial port every 5ms.

  23. #23
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,012
    Quote Originally Posted by gfvalvo View Post
    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.

  24. #24
    Senior Member
    Join Date
    Feb 2017
    Posts
    299
    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:
    Quote Originally Posted by cermak View Post
    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 by gfvalvo; 01-27-2019 at 02:19 PM.

  25. #25
    Junior Member
    Join Date
    Jan 2019
    Posts
    7
    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
       }
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •