delayMicroseconds()函数如何工作。据我了解,timer0的预分频器设置为64。对于16MHz时钟,每个计数为4.0uS。我对达到1uS的时间间隔感到困惑吗?
delayMicroseconds()函数如何工作。据我了解,timer0的预分频器设置为64。对于16MHz时钟,每个计数为4.0uS。我对达到1uS的时间间隔感到困惑吗?
Answers:
该函数的源代码已被很好地记录,可以在Linux系统上的/usr/share/arduino/hardware/arduino/cores/arduino/wiring.c中找到。Windows系统将具有与connection.c文件类似的路径。尽力查找文件并浏览它。现在仅关注此单个功能,它不依赖于任何其他功能。
通过检查代码,您会发现它与计时器无关,而与指令周期有关。该代码在很大程度上依赖于编译器优化,对于您而言,该优化与对于库开发人员而言完全相同。那是作者的一个假设!Atmel AVR指令集文档中详细记录了每个指令“消耗”的CPU周期数。
首先,检查延迟值是否等于1,在这种情况下,只是从已经花费了微秒CPU时间的例程中返回。
然后,将延迟值乘以四(<<=2
)。所述__asm__
-loop编译成4个CPU周期循环。4个周期×4 = 16个周期。16MHz /(4×4)= 1MHz,需要1 us的循环时间,即我们追求的分辨率。
最后的-2微秒(在循环开始之前)再次纠正了编译器引入的开销。__asm__
从C 调用-code需要一些额外的指令来保存CPU寄存器。
对于普通的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()
说:“在16 MHz Arduino板上(例如Duemilanove和Nano),此功能的分辨率为4微秒(即返回的值始终是4的倍数)。”