0

我正在使用 Windows 7 PC 以 1kHz 的速率输出电压。起初我只是简单地用 sleep_until(nextStartTime) 结束线程,但事实证明这是不可靠的,有时工作正常,有时长达 10 毫秒。

我在这里找到了其他答案,说繁忙的循环可能更准确,但是由于某种原因,我的循环有时也需要太长时间。

while (true) {
        doStuff();  //is quick enough
        logDelays();

        nextStartTime = chrono::high_resolution_clock::now() + chrono::milliseconds(1);

        spinStart = chrono::high_resolution_clock::now();

        while (chrono::duration_cast<chrono::microseconds>(nextStartTime - 
                         chrono::high_resolution_clock::now()).count() > 200) {
            spinCount++; //a volatile int
        }
        int spintime = chrono::duration_cast<chrono::microseconds>
                              (chrono::high_resolution_clock::now() - spinStart).count();

        cout << "Spin Time micros :" << spintime << endl;

        if (spinCount > 100000000) {
            cout << "reset spincount" << endl;
            spinCount = 0;
        }

}

我希望这可以解决我的问题,但是它会产生输出:

  Spin Time micros :9999
  Spin Time micros :9999
  ...

在过去的 5 个小时里,我一直被困在这个问题上,如果有人知道解决方案,我将非常感激。

4

2 回答 2

0

在 Windows 上,我认为不可能得到如此精确的时间,因为你无法保证你的线程实际上在你想要的时间运行。即使 CPU 使用率低并将您的线程设置为实时优先级,它仍然可以被中断(据我了解,硬件中断。从未完全调查过,但即使是while(true) ++i;实时的简单类型循环,我也看到被中断然后在 CPU 内核之间移动)。虽然实时线程的这种中断和切换非常快,但如果您尝试直接驱动信号而不进行缓冲,它仍然很重要。

相反,您确实想要读取和写入数字样本的缓冲区(因此在 1KHz 时,每个样本为 1ms)。您需要确保在最后一个缓冲区完成之前将另一个缓冲区排队,这将限制它们的大小,但如果代码简单且没有其他 CPU 争用单个样本缓冲区(1ms)甚至可能以 1KHz 实时优先级有可能,这比“立即”多出 1 毫秒的延迟,但您必须进行测试。然后,您将其留给硬件及其驱动程序来处理精确的时序(例如,确保每个输出样本“精确”为 1 毫秒,以达到供应商声称的精度)。

这基本上意味着您的代码在最坏的情况下只需精确到 1ms,而不是试图说服远小于操作系统真正支持的东西,例如微秒精度。

只要您能够在硬件用完前一个缓冲区之前排队一个新缓冲区,它就能够以所需的频率运行而不会出现问题(再次以音频为例,而容忍的延迟通常要高得多,并且因此缓冲区也是如此,如果您使 CPU 过载,您有时仍会听到应用程序没有及时排队新的原始音频的可听见的故障)。

通过仔细的计时,您甚至可以通过尽可能长时间地等待处理和排队您的下一个样本(例如,如果您需要减少输入和输出之间的延迟)将时间缩短到几分之一毫秒,但请记住,距离越近削减它的风险越大,你提交它就太晚了。

于 2017-09-04T15:29:44.777 回答
0

根据评论,此代码正确等待:

auto start = std::chrono::high_resolution_clock::now();
const auto delay = std::chrono::milliseconds(1);
while (true) {
    doStuff();  //is quick enough
    logDelays();

    auto spinStart = std::chrono::high_resolution_clock::now();
    while (start > std::chrono::high_resolution_clock::now() + delay) {}
    int spintime = std::chrono::duration_cast<std::chrono::microseconds>
                          (std::chrono::high_resolution_clock::now() - spinStart).count();

    std::cout << "Spin Time micros :" << spintime << std::endl;
    start += delay;
}

重要的部分是忙等待while (start > std::chrono::high_resolution_clock::now() + delay) {}start += delay;它结合起来确保delay等待的时间量,即使外部因素(Windows 更新保持系统繁忙)干扰它。如果循环花费的时间长于delay循环将在不等到它赶上的情况下执行(如果doStuff足够慢,可能永远不会)。

请注意,错过更新(由于系统繁忙)然后一次发送 2 个更新可能不是处理这种情况的最佳方法。您可能需要检查内部的当前时间doStuff,如果时间错误超过某个可接受的量,则中止/重新启动传输。

于 2017-09-04T16:33:35.703 回答