Las otras respuestas son muy buenas, pero quiero dar más detalles sobre cómo micros()
funciona. Es siempre lee el temporizador de hardware actual (posiblemente TCNT0
), que se actualiza constantemente por el hardware (de hecho, cada 4 mu s debido a la pre-escalador de 64). Luego agrega el recuento de desbordamiento del temporizador 0, que se actualiza mediante una interrupción de desbordamiento del temporizador (multiplicado por 256).
Por lo tanto, incluso dentro de un ISR, puede confiar en la micros()
actualización. Sin embargo, si espera demasiado , pierde la actualización de desbordamiento y el resultado devuelto disminuirá (es decir, obtendrá 253, 254, 255, 0, 1, 2, 3, etc.)
Esto es micros()
- ligeramente simplificado para eliminar define para otros procesadores:
unsigned long micros() {
unsigned long m;
uint8_t oldSREG = SREG, t;
cli();
m = timer0_overflow_count;
t = TCNT0;
if ((TIFR0 & _BV(TOV0)) && (t < 255))
m++;
SREG = oldSREG;
return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
}
El código anterior permite el desbordamiento (verifica el bit TOV0) para que pueda hacer frente al desbordamiento mientras las interrupciones están desactivadas, pero solo una vez ; no hay ninguna disposición para manejar dos desbordamientos.
TLDR;
- No hagas demoras dentro de un ISR
- Si debe hacerlo, puede cronometrar con
micros()
pero no millis()
. También delayMicroseconds()
es una posibilidad.
- No demore más de 500 µs, o perderá un desbordamiento del temporizador.
- Incluso los retrasos breves pueden hacer que pierda datos en serie entrantes (a 115200 baudios obtendrá un nuevo carácter cada 87 µs).
micros()
No es un ISR. Es una función normal. El indicador TOV0 le permite probar el hardware para ver si se ha producido un desbordamiento del temporizador (pero aún no se ha procesado).No está mal usar
millis()
omicros()
dentro de una rutina de interrupción.Que es incorrecto utilizar de forma incorrecta.
Lo principal aquí es que mientras estás en una rutina de interrupción "el reloj no está funcionando".
millis()
ymicros()
no cambiará (bueno,micros()
inicialmente lo hará, pero una vez que pasa ese punto mágico de milisegundos donde se requiere una marca de milisegundos, todo se desmorona).Por lo tanto, puede llamar
millis()
omicros()
averiguar la hora actual dentro de su ISR, pero no espere que esa hora cambie.Es esa falta de cambio en el tiempo que se le advierte en la cotización que proporciona.
delay()
confía enmillis()
cambiar para saber cuánto tiempo ha pasado. Como no cambiadelay()
, nunca puede terminar.Así que, esencialmente
millis()
, ymicros()
le indicará el tiempo cuando el ISR se llama no importa cuando en su ISR se usan.fuente
micros()
actualizaciones. Siempre lee el registro del temporizador de hardware.La frase citada no es una advertencia, es simplemente una declaración sobre cómo funcionan las cosas.
No hay nada intrínsecamente malo en usar
millis()
omicros()
dentro de una rutina de interrupción escrita correctamente.Por otro lado, hacer cualquier cosa dentro de una rutina de interrupción escrita incorrectamente es, por definición, incorrecto.
Una rutina de interrupción que lleva más de unos pocos microsegundos para hacer su trabajo, con toda probabilidad, está escrita incorrectamente.
En resumen: una rutina de interrupción escrita correctamente no causará ni encontrará problemas con
millis()
omicros()
.Editar: con respecto a "por qué micros ()" comienza a comportarse de manera errática "", como se explica en un " examen de la página web de la función de micros Arduino ", el
micros()
código en un Uno común es funcionalmente equivalente aEsto devuelve un largo sin signo de cuatro bytes compuesto por los tres bytes más bajos
timer0_overflow_count
y un byte del registro de conteo del temporizador-0.El controlador de interrupciones
timer0_overflow_count
incrementa aproximadamente una vez por milisegundoTIMER0_OVF_vect
, como se explica en un examen de la página web de la función arduino millis .Antes de que comience un controlador de interrupciones, el hardware AVR desactiva las interrupciones. Si (por ejemplo) un controlador de interrupciones se ejecutara durante cinco milisegundos con las interrupciones aún deshabilitadas, se perderían al menos cuatro desbordamientos del temporizador 0. [Las interrupciones escritas en código C en el sistema Arduino no son reentrantes (capaces de manejar correctamente ejecuciones superpuestas múltiples dentro del mismo controlador) pero se podría escribir un controlador de lenguaje ensamblador reentrante que vuelva a activar las interrupciones antes de que comience un proceso lento.]
En otras palabras, los desbordamientos del temporizador no se "acumulan"; cada vez que ocurre un desbordamiento antes de que se haya manejado la interrupción del desbordamiento anterior, el
millis()
contador pierde un milisegundo y la discrepanciatimer0_overflow_count
a su vez también semicros()
equivoca en un milisegundo.Con respecto a "más corto que 500 μs" como límite de tiempo superior para el procesamiento de interrupción, "para evitar bloquear la interrupción del temporizador durante demasiado tiempo", podría subir a poco menos de 1024 μs (por ejemplo, 1020 μs) y
millis()
seguiría funcionando, la mayoría de las veces hora. Sin embargo, considero un controlador de interrupciones que toma más de 5 μs como un perezoso, más de 10 μs como perezoso, más de 20 μs como un caracol.fuente
micros()
"comenzar a comportarse de manera errática"? ¿Y qué quieres decir con "rutina de interrupción correctamente escrita"? Supongo que significa "menos de 500us" (para evitar el bloqueo de la interrupción del temporizador durante demasiado tiempo), "usar variables volátiles para la comunicación" y "no llamar al código de la biblioteca" tanto como sea posible, ¿hay algo más?