Micros() rolling over after 59.652323 seconds, not 71 minutes [SOLVED]

Nods I gotcha. No worries about not mentioning the docs, after my last post I scrolled back and didn’t see any mention about them, but it’s not a big deal. I’ll make a change to them so others will be aware! :smile:

We spent a lot of time talking about this :smile:

Docs say: “Returns the number of microseconds since the Arduino board began running the current program. This number will overflow (go back to zero), after approximately 70 minutes.”

I think that’s a pretty obvious copy/paste from Arduino’s docs :wink:

1 Like

Well, that’s a silly bug. Adding a bug for this. Didn’t realize we were just talking about something wrong in the docs either. :slight_smile:

Thanks guys!
David

Sorry about the confusion. I wasn’t aware it was implemented already.
The document now reflects the observations. Fixed the Ctrl-c/v typo as well.

Thanks!

2 Likes

Sorry to reply to a dead topic, but this doesn’t seem right. Correct unsigned math spanning rollover will only occur if the rollover corresponds to the word length of the variables, right? In other words, (nibble)0001 - (nibble)FFFF is 2, but (ulong)1 - (ulong)59652322 isn’t 2 unless you’re doing math in mod(59652323). So if micros() happens to rollover at the time you’re trying to use it to measure something then you’re going to end up with rubbish results unless you do the modulo math. Isn’t that true or am I missing something obvious? Is there an easier solution than modulo math? For example, can we programmatically reset the micros() counter just before we use it to ensure rollover won’t occur?

1 Like

fwiw, here’s how I’m dealing with micros() rollovers:

#define MICROS_ROLLOVER ((unsigned long)59652323)
static unsigned long us_delta(unsigned long old_us, unsigned long new_us)
{
    new_us -= old_us;
    if((long)new_us < 0)
        new_us += MICROS_ROLLOVER;
    return new_us;
}

So when I want the delta between old_micros and micros() I use us_delta(old_micros, micros()) and never have to worry about getting caught in a rollover (given that the time between old_micros and micros() is less than MICROS_ROLLOVER)

1 Like

If you were looking for the reason for the non-base-2 rollover, this was the explanation further up in this thread.

1 Like

I got that part. I was responding to the assertion that one could just subtract these values and unsigned math would take care of rollover. It clearly doesn’t. Anyway, I’ll wager there are some spark programs out there showing anomalous behavior because the dev assumed rollover worked the “normal” way. Seems like there should be a function like us_delta in the sdk and referenced from the micros() documentation.

1 Like

You’re absolutely correct. There was a lot of talk and work on other areas of the code that did involve full 32-bit counters and I got that mixed up with this :wink: Because micros() doesn’t return values up to UINT_MAX, you won’t be able to take advantage of unsigned subtraction preventing rollover error.

What do you think about having an extra function that just returns the current DWT count to work with, and because it does count up to UINT_MAX, it’s error proof when performing a simple unsigned subtraction. Also it’s 72 times more accurate than micros() on the Core, and 120 times on the Photon.

##example

/* Returns 32-bit DWT count in 1/72th of a microsecond increments on Core
 * Returns 32-bit DWT count in 1/120th of a microsecond increments on Photon
 */
uint32_t getDWT() {
  return DWT->CYCCNT;
}

uint32_t startTime = getDWT();
while ( (getDWT() - startTime) < (30 * SYSTEM_US_TICKS) ) {
  // loop here for 30uS.
}

/* SYSTEM_US_TICKS = 72 on Core and 120 on Photon.  This can be used as a multiplier constant to determine ticks in microseconds from the DWT counter value. */

And to be super precise you can do away with the function call overhead and use DWT->CYCCNT directly.

2 Likes

I would like that for many uses. But the micros() call is so handy for many applications… So, yeah, I like that direct access to the DWT, but I think a couple of small tweaks to micros() (e.g. a helper function like my example above and a few more lines of explanation in the docs) would be beneficial for a broader set of users and might cause fewer porting problems (e.g. when moving between Spark boards with different architectures and/or clock rates).

1 Like

How about we fix the micros() call to use both millis() and the DWT counter so we get full 32-bit precision?

Sounds good if it can be done in very few cycles. How would you do it?

something like

millis() + DWT->CYCNT % (SYSTEM_US_TICKS*1000)

Although since 2^32 and (SYSTEM_US_TICKS*1000) are not common divisors, this won’t be perfect in all cases.

An improvement is to save the value of DWT->CYCNT at the start of each millisecond in the SysTick() handler.

This is rapidly spinning out into something much more complex, and probably more than we need for the original use case!

Did this get fixed in 0.4.3 to use the entire 32 bits before rolling over?

If not, the documentation still says:

Returns the number of microseconds since the device began running the current program. This number will overflow (go back to zero), after approximately 59.65 seconds.

Instead of “approximately 59.65 seconds” I suggest you put “exactly 59652323 microseconds (0 … 59652322)”. You need that number to do the math.

Hi @BDub,

I just re-read this post more carefully, and see that the DWT frequency for the Photon is different than for the core. Does that mean that the micros() rollover point is different too?

This is a real problem. It can be solved if you know exactly where the rollover point is by doing the right arithmetic. But now it looks like there may be different rollover points for micros() for the Photon and Core, and code needs to account for that.

You previous posts where you suggest:

I’m afraid that suggestion does not work. If I need to time an event that takes less than a millisecond, I can’t use the millis() counter. But remember, that event might occur right near and span the 59.652323 second boundary (for the Core), so I need to take into account that exact rollover point to do the subtraction right to get the event’s duration.

Also, looking at the code of micros() there is a divide in it. This is a bit unfortunate for those who would hope that such a call would be as efficient as possible.

And looking at the code for delayMicroseconds : It caps the maximum delay time to UINT_MAX / SYSTEM_US_TICKS, which is probably OK since who would use delayMicroseconds for such a long delay? But that should be documented. And, I still think that number is different for Photon seeing as that function uses SYSTEM_US_TICKS which will be different for Photon.

And yet another thing … neither 72 or 120 divides evenly into 2^32. This means that due to the division’s truncation, that last microsecond of timing will be different as it rolls over. It will be shorter. This might not seem like a big deal, but imagine a sensor that emits pulses every few milliseconds from 5 to 25 microseconds in duration that measures something. That means every so often, sort of randomly, the measurement might be 10% off or so. A user not knowing about this anomaly would be unable to explain this and probably suspect something was wrong with the sensor.

I think this problem is a big deal. This is why I’ve posted my second post to insist that this is either fixed, or this exact rollover points for the Core and Photon are published. Or, your very good suggestion of the getDWT() function be implemented and recommended in the documentation for doing precise timing.

@rvnash, I will leave the details of the micros() code to the pros but one goal of the new SparkIntervalTimer library will be to implement new timer functions including input capture. This mode allows precise timing of an input pulse using a hardware timer. It is too early yet on how and when everything will be implemented but it is absolutely on the hit list :smile:

@peekay123 I am very interested in that. For an RC project I did last year, I implemented such a thing on a Teensy3 but it had narrow scope to do servo in functionality, so I could read a 6-channel remote control and have my thingy do things in response. It worked well. I’m assuming that what you have in mind is a more generalized input capture that could be used for this. Let me know if you want help with ideas, coding, or testing.

@rvnash, once the official Photon firmware is released to the compile farm (IDE, CLI, DEV) and auto-update is deployed then I will be releasing a quick port of the existing SparkIntervalTimer for folks to port their existing libraries and code. Having a stable firmware is key. After that, I will be working on a new version of the library with all the features discussed. And yes, like the existing library, it will be generalized.

The goal is to a) Exploit all hardware timers on the STM32F205 even if they have no associated GPIO pins (perfect for timer interrupts only), b) Implement new timer functions like Input Capture and Output Capture (Tone does that). There is possibly more but we have to start somewhere!

Yay! I’m glad to see you digging into this :slight_smile: @rvnash

If you read again I am saying if you want precise timing for something that’s almost a minute long (>59.652323s micros() rollover), that you should use the millis() counter. If you need such dynamic range, <1ms ~ >59652ms you could check the micros() counter first and if you get to be over (10246464)us aka 0b10000000000000000000000 (a quick bit mask check), you could switch to testing against the millis() counter. If your very short even is happening asynchronously, then you need to use interrupts. Read on :smile:

Agreed this makes the result not super accurate, but you can roll your own precision counter pretty easily using interrupts and the DWT counter Stopwatch for Pinewood Derby

SYSTEM_US_TICKS is the number of ticks per microsecond, calculated as SystemCoreClock / 1000000. So it is calculated based on each system’s core clock speed (72MHz vs 120MHz). You’re right we should document this rollover number for both systems (the Core is done, Photon would need to be calculated). If you are doing the math and would like to submit a pull request to update the comments of that code, please do! Even if you just submit the information as a firmware/docs issue that would be sweet.

It would also be good to document this anomaly, which could be solved by rolling your own DWT/Interrupt timer, or Peekay’s proposed timer capture mode.

@BDub good answers to all my questions except one. I’m still concerned about the issue of timing things that span the 59652323 boundary. Take your own code example from above, but substituting 50 for 5000.

If the first line happens to execute at a random time when micros() happens returns 59652322, the next while loop will never exit. This is because micros() will always be <= 59652322 and the subtraction will roll around in an unsigned way and be some very large positive number. Your code would hang randomly and intermittently. A terrible problem to have with what looks like such a simple line of code.

So the user would need to know that magic number exactly, and adopt it into the subtraction the way @gorsat suggests above. THAT is why the exact numbers need to be documented. And since this number is different from Photon, both numbers need to be documented. In fact, it would be a good idea if there were #defines for these numbers.

However, I still think micros() should be fixed to cover the entire 2^32 bits. The reason is that very simple code like this exists in peoples code all over the Arduino (and other devices) world. And none of it will work right unless you know precisely when it will rollover.

On your suggestion that I contribute to the code and documentation … I feel I’m still a bit of a newby in this world, give me a few months with my new toys.