arduino: delaymicroseconds ()

8

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?

Hawk_08
fonte
4
A documentação diz "Esta função funciona com muita precisão no intervalo de 3 microssegundos ou mais. Não podemos garantir que os delayMicrosegundos sejam executados com precisão em tempos de atraso menores". A documentação para 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)".
RedGrittyBrick
Veja também electronics.stackexchange.com/q/22584/2191
RedGrittyBrick

Respostas:

9

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.

jippie
fonte
Ou, se você não possui o IDE do Arduino instalado, esse arquivo está disponível em github.com/arduino/Arduino/blob/master/hardware/arduino/cores/…
microtherion