Как работает функция delayMicroseconds (). Из того, что я понял, prescaler таймера 0 установлен на 64. Для 16 МГц тактовая частота дает 4.0uS за счет. Я немного запутался в математике, чтобы добраться до интервала 1 мкс?
Как работает функция delayMicroseconds (). Из того, что я понял, prescaler таймера 0 установлен на 64. Для 16 МГц тактовая частота дает 4.0uS за счет. Я немного запутался в математике, чтобы добраться до интервала 1 мкс?
Ответы:
Исходный код этой функции довольно хорошо задокументирован и может быть найден в /usr/share/arduino/hardware/arduino/cores/arduino/wiring.c в системах Linux. Системы Windows будут иметь аналогичный путь к файлу wiring.c. Приложите усилия, чтобы найти файл и просмотреть его. А пока просто сосредоточьтесь на этой единственной функции, она не зависит от других функций.
Изучив код, вы заметите, что речь идет не о таймерах, а о циклах инструкций. Код в значительной степени опирается на оптимизацию компилятора, которая для вас точно такая же, как и для разработчика библиотеки. Это предположение автора! Количество циклов ЦП, «записанных» каждой инструкцией, хорошо задокументировано в документе набора команд Atmel AVR .
Сначала проверяется, что значение задержки равно 1, в этом случае просто возвращается из процедуры, уже потраченной за микросекунду времени ЦП.
Затем значение задержки умножается на четыре ( <<=2
). В __asm__
-loop компилируется в петле цикла 4 процессора. 4 цикла × 4 = 16 циклов. 16 МГц / (4 × 4) = 1 МГц, что занимает 1 цикл цикла, разрешение, которое мы получаем.
Последние -2 микросекунды (перед тем как цикл будет запущен) снова являются поправкой на вводимые компилятором служебные данные. Для вызова __asm__
-code из C требуются дополнительные инструкции для сохранения регистров процессора.
Для обычного Arduino @ 16MHz будет скомпилирован только следующий код:
/* Delay for the given number of microseconds. Assumes a 8 or 16 MHz clock. */
void delayMicroseconds(unsigned int us)
{
// calling avrlib's delay_us() function with low values (e.g. 1 or
// 2 microseconds) gives delays longer than desired.
//delay_us(us);
// for the 16 MHz clock on most Arduino boards
// for a one-microsecond delay, simply return. the overhead
// of the function call yields a delay of approximately 1 1/8 us.
if (--us == 0)
return;
// the following loop takes a quarter of a microsecond (4 cycles)
// per iteration, so execute it four times for each microsecond of
// delay requested.
us <<= 2;
// account for the time taken in the preceeding commands.
us -= 2;
// busy wait
__asm__ __volatile__ (
"1: sbiw %0,1" "\n\t" // 2 cycles
"brne 1b" : "=w" (us) : "0" (us) // 2 cycles
);
}
Кстати: скомпилированный код довольно точен, но имейте в виду следующее: на Arduino настроены прерывания по времени, о которых большинство не знают. Когда прерывание получено во время выполнения delayMicroseconds()
, время delayMicroseconds()
будет неправильным. Конечно, вы можете остановить прерывания перед вызовом delayMicroseconds()
и включить их впоследствии, но это опять-таки влияет на точность синхронизации на продолжительность скомпилированного кода для включения / выключения.
micros()
сказано: «На платах Arduino 16 МГц (например, Duemilanove и Nano) эта функция имеет разрешение в четыре микросекунды (т. Е. Возвращаемое значение всегда кратно четырем)».