Концепция времени в Neutrino

В связи с появлением нескольких сотен тысяч новых пользователей и разработчиков, мы абсолютно уверены, что у вас появятся вопросы по программированию. Чтобы помочь вам глубже ознакомиться с технологией программирования в реальном времени, мы решили объяснить вам концепцию времени в Neutrino.

Каждый момент времени для микроядра Neutrino — это такт. Такт измеряется в миллисекундах; его начальная длительность определяется частотой вашего процессора. Если ваш процессор работает на частоте 40 Мгц или выше, то длительность одного такта — одна миллисекунда. Для более медленных процессоров длительность такта 10 миллисекунд. Изменить длительность такта программно можно функцией ClockPeriod().

Это очень важно при обращении к функциям ядра, приостанавливающим выполнение процесса. Это такие функции, как select(), alarm(), nanosleep(), nanospin(), sigaction(), delay() , это также и все функции работы с таймерами ( timer_*() ). Обычно мы используем эти функции напрямую : «Остановить на 8 секунд», «Остановить на 1 минуту» и т.д. К сожалению, если вы запросите «Остановить на 1 милисекунду , 10000 раз», вы не получите ожидаемого результата.

Выполнится ли этот код за одну секунду ?

void OneSecondPause()
{
for ( i=0; i<1000; i++ )
delay(1); /* Ждать 1000 милисекунд */
}
К сожалению, нет, этот код не вернет управление через одну секунду на IBM PC. Скорее всего, он выполнится за три секунды. Фактически, когда вы вызываете функции nanosleep() или select() с аргументом в n милисекунд, это может занять от n милисекунд до бесконечности.

А почему это функция выполняется ровно за три секунды ?

То, что вы видите, называется «ошибка квантования таймера». Это столь обычно, что даже зарегистрировано в стандарте расширений реального времени POSIX (1003.1b-1993/1003.1i-1995). В нем говорится, что можно ждать дольше, но нельзя ждать меньше. Я уверен, что все вы понимаете, что преждевременное срабатывание таймера нежелательно.

Так как мы вызываем delay() асинхронно с прерываением системного таймера, это означает, что мы должны добавить один такт, чтобы убедиться, что мы отмеряем время корректно ( представьте себе, что мы этого не делаем, и однотактовая задержка была запрошена до прерывания таймера). Обычно это добавляет половину милисекунды каждый раз, но в приведенном примере мы должны синхронизироваться с таймером каждый раз, что приводит к милисекундной задержке каждый раз.

Это занимает 2 секунды, откуда берется еще одна ?

Это проблема возникает из-за того, что когда мы запрашиваем задержку в одну милисекунду, мы получаем задержку меньше — это зависит от системного таймера. На IBM PC длительность одного такта 999,847 наносекунд, То есть мы получаем:

1,000,000 ns + 999,847 ns = 1,999,847 ns реальной задержки

1,999,847 ns / 999,847 ns = 2.000153 ns — с учетом времени ожидания таймера

В итоге каждый раз delay (1) в действительности ожидает:

999,847 ns * 3 = 2,999,541 ns

Умноженное на 1000 это дает полное время 2.9999541 секунды.

А этот код будет работать ?

void OneSecondPause()
{
for ( i=0; i<100; i++ ) delay(10);
// Ждать 1000 милисекунд
}
Да, вы получите очень близкое к требуемому значение, ошибка будет составлять не более 1/10 секунды.

В заключение…

Конечно, это простейшая ошибка, но скорее всего с начала вы столкнетесь именно с такими! Не позволяйте себе останавливаться на таких вещах — когда вы видите, что что-то должно работать, но не работает, а в документации об этом не сказано — обращайтесь в конференции — там вам всегда помогут! Ваши разработки будут идти гораздо лучше — ведь мы знаем, что наша документация далека от совершенства.

Original document:

Добавить комментарий