IS there a problem with Feather M0 Adalogger micros()?

I am totally confused about micros() in my Feather M0!
I was getting inconsistent results using micros() for a timer on a debounce circuit. To test out if i was nuts, i put micros() prints throughout the code and things look even worse. I am not posting the entire code since it is long and full of debug notes.

  1. Interrupts are enabled during all of the calls to micros(). Micros() calls are outside of the two ISRs.
  2. All of the micros() calls look exactly like this to put the data into a csv file:
#ifdef DEBUG  // DELETE ME!!!
  #ifdef WRITE_TO_SDCARD
    logFile.print( "begin loop(),,,,,,,");
    logFile.println( micros());
  #endif // WRITE_TO_SDCARD
#endif // DEBUG

I use the RTC to print on a 5 second timer to the SD card. According to the RTC, the logging is happening as scheduled. However, according to the micros() reading, the delta between these 5 second prints ranges from "47809" to "929153".

I can post the entire code if someone wants it, but i have been chasing this for several days and it has become quite an embarrassing mess.

If you want help, that is one option. The snippet is completely useless.

The other option is to post the minimum code that compiles, runs and demonstrates the problem.

1 Like

Thank you. I should have done that to rule out anything stupid. It will take some time for me to get it to the minimum that repeats the problem and then I'll post it. Sometimes you get so frustrated when a section of could you assume will work correctly isn't. It started by risking a call to micros() in an ISR... removal didn't help.

You can call micros() in an ISR, but time is not kept properly while the interrupts are turned off.

If you are basing this post on stuff done in ISRs, I imagine you are breaking some of the rules.

1 Like

Thanks. I have pulled all the micros() out of the ISR based off my reading in the forum and am still having the problem. I am trying to pare down the code so i can post it.

Thank you everyone-- especially for your patience and recommendations.
I think i figured out how stupid i am when i started stripping things down. Micros() is based off the duration of the operating sysClock. If the Arduino is put to sleep, that clock stops. DUH. That's what you get for just reading the manual and not thinking when writing code in a hurry.

Yes, there IS a problem with micros() on an Arduino Feather M0 Adalogger. After digging deeper and being VERY confused, here's the summary.

If you call micros() with interrupts disabled, it will wrap around between 1 mS and 2 mS, i.e. it can never hit an elapsed time greater than 2 mS and will be unpredictable after 1 mS. It works fine below 1 mS.

Try this code running as is and then run it with noInterrupts() uncommented. You can also look at it when changing timeDelayMicroS to see when it stops working with interrupts disabled.

Is there a workaround other than adding a separate counter for milliseconds elapsed?

[code]
// code to show micros() wraps at 1 to 2 mS for Arduino Feather M0 Adalogger; i.e. can't be used as a timer without a separate mS counter if noInterrupts()

boolean notYet = true;

void setup() {

  while (! Serial); // Wait until Serial is ready
    Serial.begin(115200);
  Serial.print( "\n\nin Setup. notYet = ");
  Serial.print( notYet );
  Serial.print( ";  Time at Entry is " );
  Serial.println( micros() );
}

void loop() {
  unsigned long timeDelayMicroS = 2000;  // when noInterrupts(), will never stop (wraps around between 1 and 2 mS depending on start time)  Works fine with interrupts() enabled
  unsigned long timeAtEntry;
  unsigned long elapsedTimeNow;
  unsigned long thisTime;
  unsigned long maxElapsedTimeNow = 0;
  int foo = 0;
  int poo = 0;

  delay(1000);
  // uncomment noInterrupts() to see if timeDelayMicroS can be reached by the timer
  //noInterrupts();
  timeAtEntry = micros();
  Serial.print( "Loop() Entry. notYet = ");
  Serial.print( notYet );
  Serial.print( ";  micros() is " );
  Serial.println( timeAtEntry );
  if( ! notYet ) {
    Serial.print("\n\n We are done. This will repeat every 3 seconds w/ interrupts() or will mess up when noInterrupts().\nThe elapsed time upon completion was ");
    Serial.println( elapsedTimeNow );
    delay ( 2000 );
  }
  while ( notYet ) {
    foo++;
    poo++;
    thisTime = micros();
    if( poo > 100 ) {  // print a dot every 100 cycles
      Serial.print(".");  // dots to indicate something is going on.
      poo = 0;
    }
    elapsedTimeNow = micros() - timeAtEntry;
    if (elapsedTimeNow > maxElapsedTimeNow ) {
      maxElapsedTimeNow = elapsedTimeNow;
    }
    if ( elapsedTimeNow > timeDelayMicroS ) {  // print the final elapsed time when reached and stop the print loop values when/if timeDelayMicroS is reached
      notYet = false;
      Serial.print( "\n\nFinished. Duration was " );
      Serial.println( elapsedTimeNow );
    }
    // print values if while( notYet ) begin time is greater than present micros(). (i.e. wrap around that shouldn't happen)
    if( thisTime > micros() and foo >1000) {  // only do it once every 10000 cycles so we don't fill the screen
      Serial.print("\n\nmicros()= ");
      Serial.print( micros() );
      Serial.print(", timeAtEntry= ");
      Serial.print(timeAtEntry);
      Serial.print(", thisTime= ");
      Serial.println( thisTime );
      Serial.print("elapsedTimeNow= ");
      Serial.print( elapsedTimeNow );
      Serial.print(", maxElapsedTimeNow= ");
      Serial.print( maxElapsedTimeNow );
      Serial.print( ", Target time delay in uS= " );
      Serial.println( timeDelayMicroS );
      foo = 0;
    }
  }
}
[/code]

The workaround is to not disable interrupts for potentially lengthy times, such as during Serial output.

In the context of noInterrupts/interrupts, 1mS is a very long time.

It's for a debounce that can last from 800 uS to 1.2 mS. I was getting unreliable results with multiple interrupts and put in the noInterrupts() during the debounce timer. It worked 95%+ of the time, but i can't have it crash. I may have a workaround with a second flag within the interrupt.

Post your code, or at the very least your ISR.

What are you debouncing?

It isn't a problem with micros(), it is a problem with expecting interrupt-dependent code to work reliably without interrupts.

It's a reed switch on a mouse wheel that can vary widely based off the mouse velocity. It chatters quite a bit during make/break.

My main reason for posting is that i couldn't find any documentation ANYWHERE saying that micros() also fails to count properly during noInterrupt(), while it is clearly documented for millis(). It was very painful figuring that out. I wish such things were in the manuals.

The ISR is simple (below with all references to it)

[code]
// in the globals
#define wheelPin 5  // pin for the unfiltered mouse wheel reed switch input * pin D5 translates to "5" *

volatile bool checkingDebounce = false; // flags if an ISR has been received on the wheel's reed switch.
volatile unsigned int wheelCount = 0; // number of complete rotations of the mouse wheel. MAX = 65,535. Should count at LEAST 3 hours of data before overflow
volatile bool wheelInterruptHappened = false; // this is set so we can determine when the wheel ISR happened- gets around micros() invalid during ISR
volatile unsigned int wheelInterruptCalls = 0; // FOR VERIFICATION OF INTERRUPT CALLS, Delete me later?

// setUpInterrupts() is called in setUp() 
// garbage for all the processing here and left off

void setUpInterrupts( void ) {  // turns on the interrupts and enables the ones we use
  interrupts ();           // enables interrupts on case they were turned off during debug.  Delete later
  // timer alarm interrupt
  #ifdef SLEEP_ENABLE
    rtc.attachInterrupt(timerAlarmIRQ);
  #endif // SLEEP_ENABLE end
  // Attach interrupt to the wheel reed switch when it goes from high (normal) to low (pulled down by reed switch)
  LowPower.attachInterruptWakeup(digitalPinToInterrupt(wheelPin), wheelInterrupt, FALLING);
}

void wheelInterrupt ( void ) {
  
  wheelInterruptCalls++; // FOR VERIFICATION OF INTERRUPT CALLS, Delete me later?
  checkingDebounce = true; // tell the main that a change has been found and the timer has started
  wheelInterruptHappened = true; // this is set for the debounce to determine if already processing an interrupt to reset timer.
} // end wheelInterrupt

[/code]

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