Точное время в C

У меня есть небольшой код ниже. Я использую этот код для вывода некоторых 1s и 0s (unsigned output[38]) из GPIO встроенной платы.

Мой вопрос: время между двумя выходными значениями (1, 0 или 0, 1) должно быть 416 микросекунд, как я определяю на clock_nanosleep ниже кода, я также использовал sched_priority() для лучшее временное разрешение. Однако измерение осциллографа (pic ниже) показывает, что время между двумя выходными значениями 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 МГц, 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);

        }
    }
}

enter image description here

РЕДАКТИРОВАТЬ: Я читал в течение нескольких дней, что clock_nanosleep() или другой наномет, usleep и т.д. не гарантирует пробуждения вовремя. они обычно обеспечивают спящий код в течение определенного времени, но пробуждение процесса зависит от процессора. я обнаружил, что абсолютное время обеспечивает лучшее разрешение (флаг TIMER_ABSTIME). Я нашел то же самое решение, что и Maxime. однако у меня есть сбой в моем сигнале, когда цикл завершен. На мой взгляд, нехорошо выполнять какие-либо функции сна для создания ШИМ или вывода данных на встроенной платформе. Хорошо потратить некоторое время на изучение таймеров CPU, которые предоставляют платформы для генерации PWM или данных, которые имеют хорошую точность.

Ответ 1

Я не могу понять, как вызов функции 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
}

Я использую это в нескольких программах, которые у меня есть для создания некоторого регулярного сообщения в сети Ethernet. И он отлично работает.

Ответ 2

Если вы выполняете ввод/вывод с учетом времени, вы, вероятно, не должны использовать материал в stdio.h, но вместо этого система ввода-вывода вызывает из-за буферизации, выполняемой stdio. Похоже, вы можете получить худший эффект буферизации, потому что ваша программа выполняет следующие действия:

  • заполнить буфер
  • сон
  • перемотка назад, которая, как я полагаю, очистит буфер

Вы хотите, чтобы ядро ​​обслуживало запись во время сна, вместо этого буфер сбрасывается после того, как вы спите, и вам нужно дождаться, когда ядро ​​обработает его.

Я думаю, что лучше всего использовать 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 микросекунды. OS также может предъявлять требования.

Какой интервал вы получите, если вы установите sleep = 0?

Выполняется ли это на компьютере или ПЛК?

Ответ на комментарий

Похоже, что у вас есть кое-что в аппаратном/программном обеспечении, которое делает что-то неожиданное - это может быть искатель.

У меня есть 2 предложения в зависимости от того, насколько критичен период:

  • Низкая критичность - поместите фигуру в свою программу, которая заставит цикл принять требуемое время. Однако, если это временный или зависящий от времени/температуры эффект, вам необходимо периодически проверять дрейф.
  • Высокая критичность - создайте стабильный стабилизатор температуры в оборудовании. Их можно купить с полки.