Accurate Delays on the Raspberry Pi

One of the issues on the Raspberry Pi is that I’m now seeing a lot of people coming to it from a traditional microcontroller background and trying to use the same ides on the Pi as they use on the microcontrollers – and promptly falling into a trap, or getting wildly unexpected results.

One of these areas is timed-delays. On a microcontroller, you can often simply loop for a number of times and get the result you desire… On the Pi, your program can be pre-emtively interrupted by Linux doing something else, it can be stalled by the GPU accessing memory and probably several other things. So timing loops in software is generally not that useful (Not to mention the CPU usage they consume – again, not an issue on a microcontroller without an operating system, but under Linux, it’s wasteful if you have something else to do!)

So… Linux provides a standard set of system calls – the issue is that you have the overhead of calling them – and that overhead can be quite variable, it depends on the system call and what it needs to do. The standard delay call, nanosleep(2) will sleep for at least how long you tell it, plus any operating system overhead and that comes to at least 20 to sometimes over 100 microseconds.

So.. when we need accurate short delays in the order of microseconds, it’s not always the best way, so to combat this, after studying the BCM2835 ARM Peripherals manual and chatting to others, I’ve come up with a hybrid solution for wiringPi. What I do now is for delays of under 100μS I use the hardware timer (which appears to be otherwise unused), and poll it in a busy-loop, but for delays of 100μS or more, then I resort to the standard nanosleep(2) call.

A quick look at the wiringPi code to see what I’m doing:

void delayMicrosecondsHard (unsigned int howLong)
{
  *(timer + TIMER_LOAD)    = howLong ;
  *(timer + TIMER_IRQ_CLR) = 0 ;

  while (*timerIrqRaw == 0)
    ;
}   

void delayMicrosecondsWPi (unsigned int howLong)
{
  struct timespec sleeper, dummy ;
  
  /**/ if (howLong ==   0)
    return ;
  else if (howLong  < 100)
    delayMicrosecondsHard (howLong) ;
  else
  {
    sleeper.tv_sec  = 0 ;
    sleeper.tv_nsec = (long)(howLong * 1000) ;
    nanosleep (&sleeper, &dummy) ;
  } 
}

I’m using the count-down timer and polling for the IRQ event (it doesn’t generate an IRQ, we’re just polling for the completion event here) This avoids any issues with having to take into account counters that wrap over.

This isn’t perfect though, and it can still take longer than we want it to, but for small delays it should be a lot closer to what we want than just calling the standard nanosleep(2) routine.

This will be in the next version of wiringPi which I’ll publish in a day or 2.

Comments

Accurate Delays on the Raspberry Pi — 4 Comments

    • The Gertboard is nothing more than a buffered IO board (well, it’s more than that, but essentially it has buffers and a selection of IO chips on it).

      The realy way to get accuracy is to not use Linux, however that’s not really an option for us here, so my intention is to provide a means to get more accurate timing, but not neccessarily super accurate.

      The reason this became an issue for me was reading a shift-register via the GPIO – it needed a delay between clocks, so putting in delayMicroseconds (1) was making it delay for 30-100 uS rather than 1 uS! And while it worked OK, it meant the time to read the register was 30-100 times longer than it really required!

      So the intention here is to turn reasonable efforts into best efforts… But it’ll never be perfect under Linux – and for me that’s OK. It’s still a good platform to demonstrate the principles of IO.

      -Gordon

  1. To make sure that you’re program is running with high priority you can use the command ‘nice’. This will make you’re delays a lot more accurate. Say we have a program called blink, you run:

    nice -n -19 blink