I believe wait() is flaky on a ST Nucleo F401RE

13 May 2014

ST Nucleo F401RE: I am having trouble with the wait() command, although wait_ms() seems to work fine.

#include "mbed.h"

DigitalOut myled(LED1);

int main() {
    while(1) {
        myled = 1; // LED is ON
        wait_ms(200); // 200 ms
        myled = 0; // LED is OFF
        wait_ms(1000); // 1 sec
    }
}

gives the expected result, but

#include "mbed.h"

DigitalOut myled(LED1);

int main() {
    while(1) {
        myled = 1; // LED is ON
        wait(0.2); // 200 ms
        myled = 0; // LED is OFF
        wait(1.0); // 1 sec
    }
}

and the LED stays off.

my software: Windows 7 with Firefox

13 May 2014
09 Jun 2014

I think there are still problems with the accuracy of wait()

The following loop through 200,000 times wait(100us) takes about 22-23 seconds to run on my F401RE vs the 20 seconds you would expect:

    int x=0;
    time_t begin=time(NULL);
    while(x<200000) { 
          x++;
          wait(.0001);
    }
    time_t end=time(NULL);
    pc.printf("diff %f",difftime(end,begin),"\n");

Clearly there is overhead from while() and x++ which could account for the extra two seconds. However, you still get 2-3 extra seconds when you increase the wait time and reduce the number of loops:

    int x=0;
    time_t begin=time(NULL);
    while(x<2000) { 
          x++;
          wait(.01);
    }
    time_t end=time(NULL);
    pc.printf("diff %f",difftime(end,begin),"\n");

This still runs in 22-23 seconds. So with 100 times less overhead you would expect the overhead to be only 20-30 milliseconds, not 2-3 seconds, and the total run time should be very close to 20 seconds. Therefore, the problem seems to be that wait() is off by 10-15%. Any suggestion on what function to use to get more accurate timing?

09 Jun 2014

Isn't this kind of skew expected since the F401RE doesn't have a crystal (on the chip side)? It's just using the internal oscillator which means it could be affected by small changes in voltage, right?

Nevermind, didn't realize that wait_ms worked just fine... so maybe this is a floating point precision issue?

09 Jun 2014

I had the same thought regarding wait_ms. But if you run wait_ms(1) through the above loop 20000 times you are still 2-3 seconds off.

09 Jun 2014

So, just for my own curiosity, I ran the following program on my F401RE

Serial pc(USBTX, USBRX);
DigitalOut myled(LED1);
 
int main() {
  Timer t;

  t.reset(); t.start(); wait(10); t.stop();
  pc.printf("wait(10) - %d ms\r\n", t.read_ms());

  t.reset(); t.start(); wait_ms(10000); t.stop();
  pc.printf("wait_ms(10000) - %d ms\r\n", t.read_ms());
  
  t.reset(); t.start(); for (int i = 0; i < 1000; i++) wait(0.01); t.stop();
  pc.printf("wait(0.01) x 1000 - %d ms\r\n", t.read_ms());

  t.reset(); t.start(); for (int i = 0; i < 1000; i++) wait_ms(10); t.stop();
  pc.printf("wait_ms(10) x 1000 - %d ms\r\n", t.read_ms());
}

With the following output...

wait(10) - 10000 ms
wait_ms(10000) - 10000 ms
wait(0.01) x 1000 - 10000 ms
wait_ms(10) x 1000 - 10000 ms

Which kind of surprised me a bit since that seems almost TOO perfect. Any ideas on what I might be doing wrong?

Edit: Also ran your wait_ms(1) x 20000 example with the following output:

wait_ms(1) x 20000 - 20000 ms

Edit2: Did one last time but used the same timing mechanism you did (eg. difftime):

wait_ms(1) x 20000 - 19.000000 s

So mine seems to be pretty accurate if I assume that last result is simply a rounding error?

09 Jun 2014

You are using Timer to measure the accuracy of wait, however they both use exactly the same timer. Wait is nothing else than:

while (timer.read() < wait_time);

Advantage of this way compared to a random for loop which some others use is that for example if an interrupt interrupts it, the wait time will still stay correct, and it is easier to use it with different clock speeds. But you cannot measure the accuracy of wait this way ;).

Also the error you see (which btw is another issue than what this topic was about) shouldn't be due to overhead, it is too large for that.

It might be related to: https://mbed.org/questions/3713/Nucleo-F302R8-Ticker-class/, but I don't have this module myself, so cannot do my own tests.

09 Jun 2014

Jason, you're onto something. If I combine your timer code with my code I get different time measurements. I'm still showing 22-23 seconds whereas your timer code produces 20000 ms. So it looks like there's a bug either in time(NULL) or in difftime()

output: Jason's timer - 20000 ms My timer/diff 22.000000 Jason's timer - 20000 ms My timer/diff 23.000000

    int x=0;
    Timer t;
    time_t begin=time(NULL);
    t.reset(); t.start();
    while(x<20000) { 
          x++;
          wait_ms(1);}
    t.stop();
    time_t end=time(NULL);
    pc.printf("Jason's timer - %d ms\r\n", t.read_ms());
    pc.printf("My timer/diff %f",difftime(end,begin),"\n");
09 Jun 2014

With those times, could you just check with a real clock which one is correct? The RTC doesn't have a dedicated crystal, so is probably running of a not too accurate clock, although often they are calibrated to something like 1% accuracy (dunno if that is the case here too, and which clock it uses exactly).

10 Jun 2014

Measuring the output of wait() with a scope. The problem clearly is not with wait() but with the timer. The signal on the photo below is a cycle of 100us off - 100us on. If there were a 10% discrepancy then over 5 cycles you should be one division off if the signal had the delay I reported earlier. Since we don't see this much discrepancy the problem must lie with using "time_t begin=time(NULL)" or "difftime()" - or both.

    while(1) {
        myled = 1; // LED is ON
        wait(0.0001); // 100 us
        myled = 0; // LED is OFF
        wait(0.0001); // 100 usec
    }

The output resulting from this code, where each horizontal division represents 0.1 ms: /media/uploads/kirchnet/img_20140609_224832.jpg Scope courtesy of Alphaonelabs makerspace

10 Jun 2014

I checked mbed code, and it uses indeed the LSI (low-speed internal) oscillator for RTC if there isn't a 32kHz crystal on the board, and pretty sure there isn't one.

Where I went wrong was when I made the assumption they would be trimmed to 1% accuracy. They aren't. From the datasheet:

  fLSI (2) Frequency 17 - 32 - 47kHz (min - typ - max)
  2. Based on characterization, not tested in production.

So yeah it is actually pretty good yours is only 10% off.