arduino: delaymicroseconds ()

8

Cómo funciona la función delayMicroseconds (). Por lo que entendí, el preescalador del temporizador 0 está configurado en 64. Para un reloj de 16MHz da 4.0uS por conteo. ¿Estoy un poco confundido con las matemáticas para llegar al intervalo de 1uS?

Hawk_08
fuente
44
La documentación dice "Esta función funciona con mucha precisión en el rango de 3 microsegundos en adelante. No podemos asegurar que delayMicroseconds funcione con precisión para tiempos de demora más pequeños". La documentación micros()dice "En placas Arduino de 16 MHz (por ejemplo, Duemilanove y Nano), esta función tiene una resolución de cuatro microsegundos (es decir, el valor devuelto es siempre un múltiplo de cuatro)".
RedGrittyBrick
Ver también electronics.stackexchange.com/q/22584/2191
RedGrittyBrick

Respuestas:

9

El código fuente de esta función está bastante bien documentado y se puede encontrar en /usr/share/arduino/hardware/arduino/cores/arduino/wiring.c en sistemas Linux. Los sistemas Windows tendrán una ruta similar al archivo cableado.c. Haga el esfuerzo de encontrar el archivo y navegar por él. Por ahora solo concéntrese en esta única función, no depende de ninguna otra función.

Al inspeccionar el código, notará que no se trata de temporizadores, sino de ciclos de instrucción. El código depende en gran medida de que la optimización del compilador sea exactamente la misma para usted que para el desarrollador de la biblioteca. Que una suposición del autor! El número de ciclos de CPU 'quemados' por cada instrucción está bien documentado en el documento del conjunto de instrucciones Atmel AVR .

Primero se verifica que el valor de retraso sea igual a 1, en ese caso, solo regresando de la rutina que ya pasó más de un microsegundo de tiempo de CPU.

Luego, el valor del retraso se multiplica por cuatro ( <<=2). El __asm__bucle se compila en un bucle de ciclo de 4 CPU. 4 ciclos × 4 = 16 ciclos. 16MHz / (4 × 4) = 1MHz, lo que lleva 1 tiempo de ciclo, la resolución que buscamos.

Los últimos -2 microsegundos (antes de que se inicie el bucle) es nuevamente una corrección en la sobrecarga introducida por el compilador. Llamar a __asm__-code desde C requiere algunas instrucciones adicionales para guardar los registros de la CPU.

Para un Arduino @ 16MHz normal, solo se compilará el siguiente código:

/* 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
        );
}

Por cierto: el código compilado es bastante preciso, pero tenga en cuenta lo siguiente: en Arduino hay interrupciones programadas configuradas que la mayoría desconoce. Cuando se recibe una interrupción durante la ejecución del delayMicroseconds(), el momento delayMicroseconds()será incorrecto. Por supuesto, puede detener las interrupciones antes de llamar delayMicroseconds()y habilitarlas después, pero eso nuevamente afecta la precisión del tiempo por la duración del código compilado para habilitar / deshabilitar.

jippie
fuente
O si no tiene instalado el Arduino IDE, este archivo está disponible en github.com/arduino/Arduino/blob/master/hardware/arduino/cores/…
microtherion