【问题标题】:precise timing in CC语言中的精确计时
【发布时间】:2013-01-23 18:57:23
【问题描述】:

我在下面有一个小代码。我使用此代码从嵌入式板的 GPIO 输出一些 1 和 0 (unsigned output[38])。

我的问题:两个输出值(1、0 或 0、1)之间的时间应该是 416 微秒,正如我在下面的代码 clock_nanosleep 中定义的那样,我还使用了 sched_priority()更好的时间分辨率。但是,示波器(下图)测量显示两个输出值之间的时间为 770 微秒。我想知道为什么我的信号之间有这么多的不准确?

附言。该板(beagleboard)具有 Linux 3.2.0-23-omap #36-Ubuntu Tue Apr 10 20:24:21 UTC 2012 armv7l armv7l armv7l GNU/Linux 内核,它具有 750 MHz CPU,top 显示几乎没有 CPU (~1%) 和内存(~0.5%) 在我运行我的代码之前被消耗。我使用没有校准问题的电子示波器。

#include <stdio.h>
#include <stdlib.h> //exit();
#include <sched.h>
#include <time.h>

void msg_send();
struct sched_param sp;

int main(void){
      sp.sched_priority = sched_get_priority_max(SCHED_FIFO);
      sched_setscheduler(0, SCHED_FIFO, &sp);
      msg_send();
    return 0;
}

void msg_send(){
    unsigned output[38] = {0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,1}; 

    FILE *fp8;
    if ((fp8 = fopen("/sys/class/gpio/export", "w")) == NULL){  //echo 139 > export
           fprintf(stderr,"Cannot open export file: line844.\n"); fclose(fp8);exit(1);
    }
    fprintf(fp8, "%d", 139);  //pin 3
    fclose(fp8);

    if ((fp8 = fopen("/sys/class/gpio/gpio139/direction", "rb+")) == NULL){
       fprintf(stderr,"Cannot open direction file - GPIO139 - line851.\n");fclose(fp8); exit(1);
    }
    fprintf(fp8, "out");
    fclose(fp8);

   if((fp8 = fopen("/sys/class/gpio/gpio139/value", "w")) == NULL) {
        fprintf(stderr,"error in openning value\n");  fclose(fp8); exit(1);
}

struct timespec req = { .tv_sec=0, .tv_nsec = 416000 }; //416 usec

/* here is the part that my question focus*/
    while(1){
        for(i=0;i<38;i++){
        rewind(fp8);
        fprintf(fp8, "%d", output[i]);
        clock_nanosleep(CLOCK_MONOTONIC ,0, &req, NULL);

        }
    }
}

编辑:我已经阅读了几天,clock_nanosleep() 或其他 nanosleep、usleep 等不能保证准时醒来。他们通常提供在定义的时间内使代码休眠,但唤醒进程取决于 CPU。我发现绝对时间提供了更好的分辨率(TIMER_ABSTIME 标志)。我找到了与 Maxime 建议的相同的解决方案。但是,当 for 循环完成时,我的信号出现故障。在我看来,在嵌入式平台上创建 PWM 或数据输出对任何睡眠功能都不好。花一些时间学习平台提供的 CPU 定时器以生成具有良好精度的 PWM 或数据输出是很好的。

【问题讨论】:

  • 你应该把你的解决方案放在一个正确的答案中(你可以回答你自己的问题)并接受它作为正确的答案。

标签: c linux time


【解决方案1】:

我不知道如何调用clock_getres() 来解决您的问题。手册页中说只读取时钟的分辨率。

正如 Geoff 所说,使用绝对睡眠时钟应该是更好的解决方案。这样可以避免其他代码意外的时序延迟。

struct timespec Time;
clock_gettime(CLOCK_REALTIME, &(Time));

while(1){
    Time.tv_nsec += 416000;
    if(Time.tv_nsec > 999999999){
        (Time.tv_sec)++;
        Time.tv_nsec -= 1000000000;
    }
    clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &(Time), NULL);
    //Do something
}

我在一些程序上使用它来在以太网上生成一些常规消息。而且运行良好。

【讨论】:

    【解决方案2】:

    如果您正在执行时间敏感的 I/O,您可能不应该使用 stdio.h 中的内容,而是使用 I/O 系统调用,因为 stdio 完成了缓冲。由于您的程序执行了以下步骤,因此您似乎也可能会受到缓冲的最坏影响:

    1. 填充缓冲区
    2. 睡觉
    3. 倒带,我相信这会刷新缓冲区

    您希望内核在您睡眠时为写入提供服务,而不是在您睡眠后刷新缓冲区,您必须等待内核处理它。

    我认为您最好的选择是使用open("/sys/class/gpio/gpio139/value", O_WRONLY|O_DIRECT) 以最大限度地减少缓存造成的延迟。

    如果您仍然需要刷新缓冲区以强制写入,您可能希望使用clock_gettime 来计算刷新数据所花费的时间并从睡眠时间中减去该时间。或者,将所需的时间间隔添加到 clock_gettime 的结果中并将其传递给 clock_nanosleep 并使用 TIMER_ABSTIME 标志等待该绝对时间的发生。

    【讨论】:

      【解决方案3】:

      我猜问题是clock_nanosleep 正在休眠 416 微秒 并且循环中的其他命令以及循环和clock_nanosleep 架构本身需要 354 微秒。操作系统也可能提出要求。

      如果你设置 sleep = 0,你会得到什么间隔?

      您是在计算机还是 PLC 上运行它?

      对评论的回应

      似乎您在硬件/软件中有些东西正在做一些意想不到的事情 - 它可能是一个错误的发现。

      根据时期的严重程度,我有 2 条建议:

      1. 低关键性 - 在程序中放置一个数字,使循环占用您想要的时间。但是,如果这是瞬态或时间/温度相关的影响,则需要定期检查漂移。
      2. 高临界 - 在硬件中构建温度稳定的振荡器。这些可以现成购买。

      【讨论】:

      • 我在运行 linux 的嵌入式板上运行它。我删除了“while(1)”循环中的“clock_nanosleep”行,以查看在该循环上执行“rewind()”、“fprintf()”和“for()”需要多少时间。它需要 11.1 纳秒。奇怪的是,如果将 20 分配给 'tv_nsec',那么两个值之间的时间是 102 纳秒..
      猜你喜欢
      • 2014-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-24
      • 2011-05-27
      • 1970-01-01
      • 1970-01-01
      • 2023-04-09
      相关资源
      最近更新 更多