问题表现:当树莓派运行其它进程导致CPU负载上升后,delay()函数延时出现问题,延时过长。
问题原因:由于linux是多任务的,所以实际延时时间可能会更长。delay()实际上是释放CPU,这时其它线程会占用CPU资源,导致重新唤醒CPU时出现延时。

作者在源码中指出:
This is somewhat intersting. It seems that on the Pi, a single call to nanosleep takes some 80 to 130 microseconds anyway, so while obeying the standards (may take longer), it's not always what we want!

So what I'll do now is if the delay is less than 100uS we'll do it in a hard loop, watching a built-in counter on the ARM chip. This is somewhat sub-optimal in that it uses 100% CPU, something not an issue in a microcontroller, but under a multi-tasking, multi-user OS, it's wastefull, however we've no real choice )-:

Plan B: It seems all might not be well with that plan, so changing it to use gettimeofday () and poll on that instead...
可见,作者有注意到此问题,故当延时小于100uS时使用了另外的delayMicrosecondsHard函数,这个函数是占用式延时,不会释放CPU故延时更加精确。

delay()源码:

void delay (unsigned int howLong)
{
  struct timespec sleeper, dummy ;

  sleeper.tv_sec  = (time_t)(howLong / 1000) ;
  sleeper.tv_nsec = (long)(howLong % 1000) * 1000000 ;

  nanosleep (&sleeper, &dummy) ;
}

delayMicroseconds()源码:

void delayMicroseconds (unsigned int howLong)
{
  struct timespec sleeper ;
  unsigned int uSecs = howLong % 1000000 ;
  unsigned int wSecs = howLong / 1000000 ;

  /**/ if (howLong ==   0)
    return ;
  else if (howLong  < 100)
    delayMicrosecondsHard (howLong) ;
  else
  {
    sleeper.tv_sec  = wSecs ;
    sleeper.tv_nsec = (long)(uSecs * 1000L) ;
    nanosleep (&sleeper, NULL) ;
  }
}

delayMicrosecondsHard()源码:

void delayMicrosecondsHard (unsigned int howLong)
{
  struct timeval tNow, tLong, tEnd ;

  gettimeofday (&tNow, NULL) ;
  tLong.tv_sec  = howLong / 1000000 ;
  tLong.tv_usec = howLong % 1000000 ;
  timeradd (&tNow, &tLong, &tEnd) ;

  while (timercmp (&tNow, &tEnd, <))
    gettimeofday (&tNow, NULL) ;
}

解决方法:
使用delayMicrosecondsHard()函数进行延时,这个函数在.h文件里没有声明,但已经编译完成,只要用 void delayMicrosecondsHard (unsigned int howLong); 把此函数声明一下就可以使用了。
经实际测试,此方法仅能减小延时程度及其出现的频率,并不能完全消除延时误差。更好的方法暂时未知。。。