Estoy tratando de explotar los datos DMX y eso requiere 4 pulsos. No tengo mucha suerte con los resultados que estoy comprobando para ver qué tan bueno es el Arduino para retrasar ... Parece ser bastante terrible en eso.
Aquí hay una pequeña prueba rápida que hice:
unsigned long ptime;
void setup() {
Serial.begin(9600);
}
void loop() {
ptime = micros();
delayMicroseconds(4);
Serial.println(micros() - ptime);
delay(1000); //just to keep the serial monitor from going nuts
}
Y los resultados:
8 4 8 4 4 4 4 4 8 8 8 8 4 8 8 4 4 8 4 4 4 8 8 8 4 8 4
Me sorprendió lo mala que es su precisión. ¡Es el doble del tiempo que quería retrasar, pero ni siquiera es consistente con donde podría dividir por 2!
¿Hay algo que pueda hacer para obtener resultados correctos y consistentes?
arduino-mega
timers
time
bwoogie
fuente
fuente
Respuestas:
Como se explicó en las respuestas anteriores, su problema real no es la precisión
delayMicroseconds()
, sino la resolución demicros()
.Sin embargo, para responder a su pregunta real , hay una alternativa más precisa para
delayMicroseconds()
: la función_delay_us()
del AVR-libc es precisa para el ciclo y, por ejemplohace exactamente lo que dice. La advertencia principal es que el argumento tiene que ser una constante de tiempo de compilación. Debe hacerlo
#include <util/delay.h>
para tener acceso a esta función.Tenga en cuenta también que debe bloquear las interrupciones si desea algún tipo de demora precisa.
Editar : Como ejemplo, si tuviera que generar un pulso de 4 µs en PD2 (pin 19 en el Mega), procedería de la siguiente manera. Primero, observe que el siguiente código
hace un pulso de 0.125 µs de largo (2 ciclos de CPU), porque ese es el tiempo que lleva ejecutar la instrucción que establece el puerto
LOW
. Luego, solo agregue el tiempo faltante en un retraso:y tiene un ancho de pulso con precisión de ciclo. Vale la pena señalar que esto no se puede lograr
digitalWrite()
, ya que una llamada a esta función tarda unos 5 µs.fuente
Los resultados de su prueba son engañosos.
delayMicroseconds()
en realidad se retrasa con bastante precisión (para retrasos de más de 2 o 3 microsegundos). Puede examinar su código fuente en el archivo /usr/share/arduino/hardware/arduino/cores/arduino/wiring.c (en un sistema Linux; o en alguna ruta similar en otros sistemas).Sin embargo, la resolución de
micros()
es de cuatro microsegundos. (Ver, por ejemplo, la página de garretlab sobremicros()
). Por lo tanto, nunca verá una lectura entre 4 microsegundos y 8 microsegundos. El retraso real puede ser de unos pocos ciclos durante 4 microsegundos, pero su código lo informará como 8.Intente hacer 10 o 20
delayMicroseconds(4);
llamadas seguidas (duplicando el código, no utilizando un bucle) y luego informe el resultado demicros()
.fuente
digitalWrite()
, lo que requiere varios microsegundos para ejecutarse.delayMicroseconds()
? No veo eso como algo mejor que redondear. ¶ Con respecto a la fuente de inexactitud, si la rutina se alinea, el tiempo depende del código circundante. Puede leer las listas de ensamblaje o desensamblaje para ver. (Consulte la sección "Creación de listas de ensamblaje" en mi respuesta a la pregunta Equivalente para PORTB en Arduino Mega 2560 , que de todos modos puede ser relevante para su proyecto de bitbangingmicros()
tiene una resolución bien documentada de 4 µs.Puede mejorar la resolución cambiando el preescalador para el Temporizador 0 (por supuesto, eso arroja las cifras, pero puede compensar eso).
Alternativamente, use el Temporizador 1 o el Temporizador 2 con un preescalador de 1, que le brinda una resolución de 62.5 ns.
Eso va a ser lento de todos modos.
Su salida es exactamente consistente con la resolución de 4 µs
micros()
junto con el hecho de que a veces obtendría dos "ticks" y otras una, dependiendo exactamente de cuándo comenzó la sincronización.Su código es un ejemplo interesante de error de medición.
delayMicroseconds(4);
se retrasará por cerca de 4 µs. Sin embargo, sus intentos de medirlo tienen la culpa.Además, si se produce una interrupción, alargará un poco el intervalo. Debe desactivar las interrupciones si desea un retraso exacto.
fuente
Cuando se mide con un osciloscopio, descubrí que:
delayMicroseconds(0)
=delayMicroseconds(1)
= 4 μs de retraso real.Entonces, si desea un retraso de 35 μs, necesita:
fuente
La implementación de Arduino es bastante genérica, por lo que puede no ser tan efectiva en algunas aplicaciones. Hay algunas formas de demoras cortas, cada una con sus propias deficiencias.
Use nop. Cada una es una instrucción, así que somos 16 de nosotros.
Use tcnt0 directamente. Cada uno es 4us, ya que el preescalador está configurado en 64. Puede cambiar el preescalador para lograr la resolución 16a.
Use ticks, puede implementar un clon de systick y usarlo como base del retraso. Ofrece una resolución más fina más precisión.
editar:
Usé el siguiente bloque para cronometrar los diversos enfoques:
antes de eso, había restablecido el preescalador timer0 a 1: 1, por lo que cada tick TCNT0 es 1/16 de microsegundo.
Espero eso ayude.
fuente
TCNT0
toma 1 ciclo de CPU.