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 are closed.