C中的精确计时

时间:2013-02-08 03:39:10

标签: c linux time

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

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

PS。董事会(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,sched_priority()几乎显示在运行我的代码之前没有消耗CPU(~1%)和内存(~0.5%)。我使用没有校准问题的电子示波器。

top

enter image description here

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

3 个答案:

答案 0 :(得分:2)

我无法弄清楚对clock_getres()的调用如何解决您的问题。在手册页中,据说只读取时钟的分辨率。

正如杰夫所说,使用绝对睡眠时钟应该是更好的解决方案。这可以避免来自其他代码的不期望的定时延迟。

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
}

我在少数程序上使用它来生成以太网上的常规消息。它工作正常。

答案 1 :(得分:1)

如果您正在进行时间敏感的I / O,您可能不应该使用stdio.h中的内容,而是使用stdio完成缓冲的I / O系统调用。看起来你可能会得到最缓慢的缓冲效果,因为你的程序执行了以下步骤:

  1. 填充缓冲区
  2. 睡眠
  3. 倒带,我相信会刷新缓冲区
  4. 你想要的是内核在你睡觉时为写操作提供服务,而是在你睡觉之后刷新缓冲区并且你必须等待内核来处理它。

    我认为最好的办法是使用open("/sys/class/gpio/gpio139/value", O_WRONLY|O_DIRECT)来减少因缓存造成的延迟。

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

答案 2 :(得分:0)

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

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

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

对评论的回应

看起来你的硬件/软件中有些东西正在做出意想不到的事情 - 它可能是一个找不到的人。

根据期间的重要程度,我有2条建议:

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