Arduino Usando La Función Millis en Lugar de Delay
Arduino Usando La Función Millis en Lugar de Delay
Arduino Usando La Función Millis en Lugar de Delay
delay()
El código que sigue es un típico ejemplo de escritura a través de la comunicación serie de Arduino. Escribe
“Hola” a través del puerto COM serie (visible con el Monitor Serie de Arduino, en la pestaña Herramientas), y
espera durante 1000 milisegundos (1 segundo) al final de cada iteración del bucle.
1 void setup() {
2 Serial.begin(9600);
3}
4
5 void loop() {
6 Serial.println("Hola");
7 delay(1000);
8}
La función delay() es muy fácil de usar para crear esperas, pero tiene un inconveniente: deja al
microcontrolador «atrapado» dentro de la ejecución de esta función durante el tiempo que se ha indicado. Si
hubiese un cambio en un pin que debería detectar, o si llegase información a través de cualquiera de las
comunicaciones posibles (serie, I2C o SPI) el microcontrolador sólo se enteraría luego de completarse el
retardo.
Una solución es crear un retardo que no deje insensible al sistema durante un tiempo tan extenso.
La función millis()
millis() devuelve el número de milisegundos desde que la placa Arduino empezó a ejecutar, luego de un reinicio
o el encendido. Este número se desbordará (volverá a cero), después de aproximadamente 50 días.
Ejemplo
Arduino
unsigned long tiempo;
void setup(){
Serial.begin(9600);
}
Nota: Tenga en cuenta que como el valor de retorno para millis() es un long sin signo (unsigned long), pueden
producirse errores lógicos si un programador intenta hacer operaciones aritméticas con tipos de datos más
pequeños, como de tipo int. Incluso con los long con signo se pueden producir errores ya que su valor máximo
es la mitad que la de su contraparte sin signo.
void setup() {
Hacerlo de esta manera solo tiene sentido como ejemplo, ya que es evidente que en este caso directamente se
puede usar una función delay(1000). Pero de todos modos la diferencia entre este ejemplo y el código que usa
la función delay(1000) es que el ciclo del código que usa la función millis() se ejecutará una vez por segundo
con la máxima precisión posible. El bucle en un código con delay(1000) se ejecutará en algo más de tiempo, ya
que se produce un retardo al ejecutar Serial.println(«Hola»). Ocurrirá igual con cualquier otra serie de
instrucciones que se incluyan dentro del bucle.
Presentaré dos ventajas al utilizar millis() para crear retardos, en comparación con el uso habitual de delay().
1. Cronometraje preciso
Con millis() podemos garantizar que el bucle se ejecute tantas veces como queramos dentro del retardo sin
afectar su extensión, independientemente del tiempo de ejecución (obviamente, siempre que el tiempo de
ejecución de todas las instrucciones sea menor al retardo deseado).
Con delay() esto no es posible, ya que no sabemos cuánto tiempo durará el tiempo de ejecución de todas las
instrucciones de programa que están dentro del ciclo.
Una sincronización precisa como esta es muy útil cuando se muestrea a una cierta frecuencia, o cuando se
utilizan filtros, entre otras cosas.
2. Sin bloqueo
La ventaja principal de la función millis() es que no nos impide ejecutar otro código mientras estamos
«esperando».
Ejemplo: digamos que queremos imprimir «Hola» en el puerto serie una vez por segundo mientras hacemos
otras cosas. Esto no es posible con delay(), ya que entrar a la función delay() pausa todo el código.
void setup() {
Este fragmento de código es bastante similar al primer ejemplo que mostramos, excepto que este no bloquea el
resto del programa mientras no se está imprimiendo hacia la línea serie.
3. Un simple secuenciador
Vamos a escribir un ejemplo simple en el que creamos un planificador que imprime ciertos trozos de texto a
través de la línea serie a diferentes intervalos.
Planificador de mensajes
Arduino
#define INTERVALO_MENSAJE1
#define INTERVALO_MENSAJE2
#define INTERVALO_MENSAJE3
#define INTERVALO_MENSAJE4
La función delay() estándar podemos reemplazarla por una implementada con la función millis(). Si bien parece
que es lo mismo, ya veremos a continuación qué ventaja nos puede ofrecer su estructura de programa:
Función Espera()
Arduino
unsigned long Contador = 0;
void Espera(int valor)
{
Contador = millis() + valor;
Además de esperar el tiempo indicado, esta función monitorea un pin de entrada del Arduino que haya sido
cableado para detectar que ha sucedido algún evento externo. Si esa señal va a nivel BAJO (LOW), se
interrumpe el retardo.
Para conectar varias entradas capaces de interrumpir el ciclo de retardo, se pueden agregar más elementos en la
comparación de cierre del while, por ejemplo
O se puede conectar a la entrada por el pin 12 (u otro pin elegido) un circuito como el que sigue, un AND
realizado con lógica de diodos de señal (1N914 o 1N4148), que tiene la ventaja de que se puede ampliar
indefinidamente. También se puede implementar con un circuito integrado compuerta AND.
Nota: quede claro que el circuito se representa con pulsadores, pero cada una de estas entradas puede ser un
microswith, los contactos de un relé, o cualquier otro sensor que cierre circuito hacia GND (tierra o común).
Al igual que delay() tiene una versión en microsegundos llamada delayMicroseconds(), la función millis()
tiene como compañera para tiempos breves la función micros(). Si necesitamos una mejor resolución, micros()
puede ser el camino a seguir. Sin embargo, tenga en cuenta que el valor devuelto por micros() se desbordará
(volverá a cero) después de aproximadamente 70 minutos, en comparación con los 50 días de millis().
“Desbordamiento” significa que el conteo llega a su máximo, y entonces los valores de retorno de la función
recomienzan desde cero.
Resumen
millis() y micros() son funciones realmente útiles para usar en tareas relacionadas con el tiempo. La opción
inicial y típica de un programador de Arduino es usar delay(), que no siempre funcionará tan bien,
principalmente cuando se programan muchas funciones que tienen que ver con el tiempo y existen eventos que
no se pueden perder.