Como funciona a função delayMicroseconds (). Pelo que entendi, o prescaler do timer0 está definido como 64. Para um relógio de 16MHz, você fornece 4,0uS por contagem. Estou um pouco confuso em matemática para chegar ao intervalo de 1 uS?
Como funciona a função delayMicroseconds (). Pelo que entendi, o prescaler do timer0 está definido como 64. Para um relógio de 16MHz, você fornece 4,0uS por contagem. Estou um pouco confuso em matemática para chegar ao intervalo de 1 uS?
Respostas:
O código fonte desta função está bastante bem documentado e pode ser encontrado em /usr/share/arduino/hardware/arduino/cores/arduino/wiring.c nos sistemas Linux. Os sistemas Windows terão um caminho semelhante ao arquivo fiação.c. Faça o esforço de encontrar o arquivo e navegá-lo. Por enquanto, apenas foque nesta função única, ela não depende de nenhuma outra função.
Ao inspecionar o código, você notará que não se trata de cronômetros, mas de ciclos de instruções. O código depende muito da otimização do compilador, sendo exatamente a mesma para você e para o desenvolvedor da biblioteca. Que uma suposição do autor! O número de ciclos de CPU 'queimados' por cada instrução está bem documentado no documento do conjunto de instruções do Atmel AVR .
Primeiro, o valor do atraso é verificado como sendo igual a 1, nesse caso, retornando da rotina já gasta em um microssegundo de tempo de CPU.
Em seguida, o valor do atraso é multiplicado por quatro ( <<=2
). O __asm__
loop é compilado em um loop de ciclo de 4 CPU. 4 ciclos × 4 = 16 ciclos. 16MHz / (4 × 4) = 1MHz, o que leva 1 us de tempo de ciclo, a resolução que buscamos.
Os últimos -2 microssegundos (antes do início do loop) são novamente uma correção na sobrecarga do compilador introduzida. Chamar o __asm__
código de C requer algumas instruções extras para salvar os registros da CPU.
Para um Arduino normal a 16MHz, apenas o seguinte código será compilado:
/* 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
);
}
BTW: O código compilado é bastante preciso, mas esteja ciente do seguinte: No Arduino, há interrupções temporizadas configuradas das quais a maioria desconhece. Quando uma interrupção é recebida durante a execução do delayMicroseconds()
, o momento delayMicroseconds()
será errado. É claro que você pode interromper as interrupções antes de chamá delayMicroseconds()
-las e ativá-las posteriormente, mas isso novamente afeta a precisão do tempo pela duração do código compilado para ativar / desativar.
micros()
diz "Em placas Arduino de 16 MHz (por exemplo, Duemilanove e Nano), essa função tem uma resolução de quatro microssegundos (ou seja, o valor retornado é sempre um múltiplo de quatro)".