10

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

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

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,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 或数据输出是很好的。

4

3 回答 3

2

我不知道如何调用 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
}

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

于 2013-02-08T14:43:36.780 回答
1

If you are doing time sensitive I/O, you probably shouldn't use the stuff in stdio.h but instead the I/O system calls because of the buffering done by stdio. It looks like you might be getting the worst effect of the buffering too because your program does these steps:

  1. fill the buffer
  2. sleep
  3. rewind, which I believe will flush the buffer

What you want is for the kernel to service the write while you are sleeping, instead the buffer is flushed after you sleep and you have to wait for the kernel to process it.

I think your best bet is to use open("/sys/class/gpio/gpio139/value", O_WRONLY|O_DIRECT) to minimize delays due to caching.

if you still need to flush buffers to force the write through you probably want to use clock_gettime to compute the time spent flushing the data and subtract that from the sleep time. Alternatively add the desired interval to the result of clock_gettime and pass that to clock_nanosleep and use the TIMER_ABSTIME flag to wait for that absolute time to occur.

于 2013-02-08T04:58:18.123 回答
0

我猜问题是clock_nanosleep睡眠时间为 416 微秒,循环中的其他命令以及循环和clock_nanosleep架构本身需要 354 微秒。操作系统也可能提出要求。

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

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

回应评论

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

根据时期的关键程度,我有 2 条建议:

  1. 低临界 - 在你的程序中放置一个数字,使循环花费你想要的时间。但是,如果这是瞬态或时间/温度相关的影响,您将需要定期检查漂移。
  2. 高临界 - 在硬件中构建温度稳定的振荡器。这些可以现成购买。
于 2013-02-08T03:44:05.540 回答