Мне нужно сделать точное время до уровня 1 us к времени изменения рабочего цикла волны pwm.
Фон
Я использую Gumstix Over Water COM (https://www.gumstix.com/store/app.php/products/265/), у которого есть одноядерный процессор ARM Cortex-A8, работающий на 499.92 BogoMIPS ( страница Gumstix требует до 1 ГГц с рекомендуемым 800 МГц) в соответствии с /proc/cpuinfo. ОС представляет собой версию Angstrom Image Linux на базе ядра версии 2.6.34, и она является запасом на Gumstix Water COM.
Проблема
Я довольно много читал о точном времени в Linux (и пробовал большую часть этого), и, похоже, консенсус заключается в том, что использование clock_gettime() и обращение к CLOCK_MONOTONIC - лучший способ сделать это. (Я бы хотел использовать регистр RDTSC для синхронизации, так как у меня есть одно ядро с минимальными возможностями энергосбережения, но это не процессор Intel.) Итак, вот нечетная часть, а clock_getres() возвращает 1, предлагая разрешение в 1 нс, фактические временные тесты предполагают минимальное разрешение 30517 нс или (это не может быть совпадением) ровно время между отметками времени 32,768 кГц. Вот что я имею в виду:
// Stackoverflow example
#include <stdio.h>
#include <time.h>
#define SEC2NANOSEC 1000000000
int main( int argc, const char* argv[] )
{
// //////////////// Min resolution test //////////////////////
struct timespec resStart, resEnd, ts;
ts.tv_sec = 0; // s
ts.tv_nsec = 1; // ns
int iters = 100;
double resTime,sum = 0;
int i;
for (i = 0; i<iters; i++)
{
clock_gettime(CLOCK_MONOTONIC, &resStart); // start timer
// clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &ts);
clock_gettime(CLOCK_MONOTONIC, &resEnd); // end timer
resTime = ((double)resEnd.tv_sec*SEC2NANOSEC + (double)resEnd.tv_nsec
- ((double)resStart.tv_sec*SEC2NANOSEC + (double)resStart.tv_nsec);
sum = sum + resTime;
printf("resTime = %f\n",resTime);
}
printf("Average = %f\n",sum/(double)iters);
}
(Не волнуйтесь о двойном кастинге, tv_sec в time_t и tv_nsec длиннее.)
Скомпилировать с помощью:
gcc soExample.c -o runSOExample -lrt
Запустить с помощью:
./runSOExample
Когда нанослоп прокомментирован, как показано, результат равен 0ns или 30517ns, причем большинство из них составляет 0ns. Это заставляет меня думать, что CLOCK_MONOTONIC обновлен на 32,768 кГц, и большую часть времени часы не обновлялись до второго вызова clock_gettime(), и в тех случаях, когда результат 30517ns, часы были обновлены между вызовами.
Когда я делаю то же самое на своем компьютере разработки (шестиступенчатый процессор AMD FX (tm) -6100, работающий на 1,4 ГГц), минимальная задержка является более постоянной 149-151 нс без нулей.
Итак, сравните эти результаты со скоростью процессора. Для Gumstix это 30517ns (32,768 кГц) соответствует 15298 циклам 499,93 МГц. Для моего компьютера-разработчика, которому 150ns соответствует 210 циклам 1.4Ghz CPU.
При вызове clock_nanosleep() раскомментированы средние результаты: Gumstix: Среднее value = 213623, и результат изменяется, вверх и вниз, кратным минимальному разрешению 30517 нс Dev компьютер: 57710-68065 нс без четкой тенденции. В случае с dev-компьютером я ожидаю, что разрешение действительно будет на уровне 1 нс, а измеренное ~ 150ns - это время, прошедшее между двумя вызовами clock_gettime().
Итак, мой вопрос таков: Что определяет минимальное разрешение? Почему разрешение компьютера-разработчика 30000X лучше, чем Gumstix, когда процессор работает только на 2.6X быстрее? Есть ли способ изменить, как часто обновляется CLOCK_MONOTONIC и где? В ядре?
Спасибо! Если вам нужна дополнительная информация или пояснения, просто спросите.