RPI1上的clock_nanosleep有时会花费更长的时间

时间:2019-09-12 18:04:07

标签: c++ raspberry-pi delay scheduler wiringpi

我想用第一代RPI的gpio输出生成平方信号。

为此,我首先想使用wiringPi

代码语言是固定的,应为C或C ++。

根据wireingPi的眨眼示例文档,解决方案应该很简单:

#include <wiringPi.h>
int main (void)
{
  wiringPiSetup () ;
  pinMode (0, OUTPUT) ;
  for (;;)
  {
    digitalWrite (0, LOW) ; delay (500) ;
    digitalWrite (0,  HIGH) ; delay (500) ;
  }
  return 0 ;
}

但是我希望它们之间有约600微秒的暂停时间。

因此,我创建了另一个 delay 方法:

void myDelay(long int usec) {
  struct timespec ts, rem;

  ts.tv_sec = 0;
  ts.tv_nsec = usec * 1000;

  while (clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &rem)) {
      ts = rem;
  }
}

然后我将2个 delay(500)切换为 myDelay(600)

这通常可以正常工作,但是有时myDelay等待600毫秒以上。

请查看以下范围图像: enter image description here

我如何在C / C ++中具有完全相同的平方?

我还尝试了pigpio的Python脚本:

pi = pigpio.pi()
pi.wave_add_new()
pi.set_mode(1, pigpio.OUTPUT)
wf=[] 
for i in range (0, 100):
    wf.append(pigpio.pulse(0, 1<<1, 600))
    wf.append(pigpio.pulse(1<<1, 0, 600))
wf.append(pigpio.pulse(0, 1<<1, 1000))
pi.wave_add_generic(wf)
wid = pi.wave_create()
pi.wave_send_once(wid)
while pi.wave_tx_busy():
    pass
pi.wave_delete(wid)
pi.stop()

此python给出了预期的结果(即:作用域上的所有平方均相等)。

现在的问题是,我怎么能用纯C / C ++实现获得相同的结果(而不必弄乱gpioWave *函数)?

3 个答案:

答案 0 :(得分:2)

我通常喜欢睡到绝对的时间。剩余时间在不同平台上的处理方式有所不同,所以我尽量避免这种情况。

inline timespec init_clock() {
    timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts); // or try using CLOCK_MONOTONIC_RAW
    return ts;
}

inline void add_usec(timespec& ts, long int usec) {
    ts.tv_nsec += usec * 1000;
    time_t sec = ts.tv_nsec / 1000000000;
    ts.tv_sec += sec;
    ts.tv_nsec -= sec * 1000000000;
}

inline void myDelay(long int usec) {
    timespec ts = init_clock();

    add_usec(ts, usec);

    while(clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, nullptr));
}

另一件事可能是测量自上次循环以来的时间。由于系统中的其他事件,这将消除许多模糊性。然后,只需将通话设置为static,即可节省两次通话之间的时钟:

inline void myDelay(long int usec) {
    static timespec ts = init_clock();

    add_usec(ts, usec);

    while(clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, nullptr));
}

上述两种组合也可以使用标准C ++库<chrono>完成。此示例节省了两次调用之间的时钟,从而获得了更准确的方波:

#include <wiringPi.h>

#include <chrono>
#include <thread>

inline void myDelay2(std::chrono::microseconds sleep_time) {
    static auto cl = std::chrono::steady_clock::now();
    cl += sleep_time;
    std::this_thread::sleep_until(cl);
}

int main() {
    using namespace std::literals::chrono_literals;

    while(true) {
        digitalWrite (0, LOW) ; myDelay2(600us) ;
        digitalWrite (0,  HIGH) ; myDelay2(600us) ;
    }
}

答案 1 :(得分:2)

看看clock_nanosleep的说明(摘自http://man7.org/linux/man-pages/man2/clock_nanosleep.2.html,重点是我的内容)

  

clock_nanosleep()暂停调用线程的执行,直到经过至少由请求指定的时间,或者传递了导致导致信号处理程序被调用或终止进程的信号为止

也就是说,唯一的保证是您至少会睡600微秒-但对于您最终会睡多长时间没有任何上限。

我假设您正在RaspberryPi上运行默认的Linux发行版之一。除了您的应用程序外,Linux还运行许多其他功能,默认情况下,Linux不是所谓的实时操作系统。在这种意义上,实时并不意味着性能(就其运行或处理数据的速度而言),而是要保证最大的等待上限(如上述)。

如果您想更贴近自己的需求,可以尝试以下一项或两项:

  1. 使用实时调度程序。这提高了线程的优先级,基本上高于用户空间中运行的所有其他线程的优先级。这是一件非常快捷的尝试-看一下sched_setscheduler()http://man7.org/linux/man-pages/man2/sched_setscheduler.2.html
  2. 由于您仍在内核空间中运行某些程序,因此通过切换调度程序可能会获得更好的性能,但是内核仍然会出现“问题”。这就是PREEMPT-RT补丁发挥作用的地方-并使内核更适合诸如此类的事情。这将需要您编译自己的内核,这肯定比仅更改调度程序要复杂一些,但并不是没有可能。一个快速的Google为一直在做同样事情的其他人提供了很多热门。

答案 2 :(得分:0)

这可能是由于delay()的实现方式所致。在操作系统环境中,最好将睡眠用于延迟时间超过非常短的间隔的任务。这会使任务经历排定的延迟,该延迟可能比请求的延迟长,以允许其他等待任务有时间运行。但是,通常不会抢先暂停它们以使先前的任务在睡眠到期时继续执行。在这种情况下,睡眠是最短时间保证的时间,然后再运行。

在操作系统之外,延迟通常是繁忙等待,因此在无法中断的情况下是可靠的。