Tabla de contenidos
La multitarea ha llevado a los ordenadores a una revolución en la que uno o más programas pueden ejecutarse simultáneamente, lo que aumenta la eficiencia, la flexibilidad, la adaptabilidad y la productividad. En los sistemas embebidos, los microcontroladores también pueden manejar la multitarea y realizar dos o más tareas simultáneamente sin detener las instrucciones actuales.
En este tutorial aprenderemos cómo Arduino realiza la multitarea con la función Arduino millis. Generalmente se utiliza una función delay() en Arduino para una tarea periódica como el parpadeo del LED, pero esta función delay() detiene el programa durante un tiempo definitivo y no permite que se realicen otras operaciones. Así que este artículo explica cómo podemos evitar el uso de la función delay() y sustituirla por millis() para realizar más de una tarea simultáneamente y hacer de Arduino un controlador multitarea. Antes de entrar en detalles vamos a empezar con la comprensión de la multitarea.
¿Qué es la multitarea?
La multitarea simplemente significa ejecutar más de una tarea o programa simultáneamente al mismo tiempo. Casi todos los sistemas operativos disponen de multitarea. Este tipo de sistemas operativos se conocen como MOS (sistema operativo multitarea). El MOS puede ser un sistema operativo de PC móvil o de escritorio. Un buen ejemplo de la multitarea en los ordenadores es cuando los usuarios ejecutan la aplicación de correo electrónico, el navegador de Internet, el reproductor multimedia, los juegos, al mismo tiempo y si los usuarios no quieren utilizar la aplicación, ésta se ejecuta en segundo plano si no se cierra. El usuario final utiliza todas estas aplicaciones al mismo tiempo, pero el sistema operativo toma este concepto un poco diferente. Vamos a discutir cómo el sistema operativo gestiona la multitarea.


Componentes necesarios:
- Arduino UNO
- Tres LEDs (cualquier color)
- Resistencias (470, 10k)
- Puentes
- Tablero de pruebas
Diagrama del circuito:
El diagrama del circuito para demostrar el uso de la función Millis() de Arduino es muy fácil y no tiene muchos componentes para conectar como se muestra a continuación

Programación del Arduino UNO para la multitarea
La programación de Arduino UNO para la multitarea sólo requerirá la lógica detrás de cómo funciona millis() que se ha explicado anteriormente. Se recomienda practicar el parpadeo del LED utilizando millis una y otra vez para hacer la lógica clara y sentirse cómodo con millis() antes de comenzar a programar Arduino UNO para la multitarea. En este tutorial la interrupción también se utiliza con millis() simultáneamente para la multitarea. El botón será una interrupción. Así que cada vez que se genere una interrupción, es decir, que se pulse el botón, elLED cambiará al estado ON o OFF.
La programación comienza con la declaración de los números de los pines donde se conectan los LEDs y el pulsador.
int led1 = 6;
int led2 = 7;
int toggleLed = 5;
int pushButton = 2;
A continuación escribimos una variable para almacenar el estado de los LEDs para su uso
futuro.
int ledState1 = LOW;
int ledState2 = LOW;
Tal y como se ha explicado en el ejemplo del parpadeo, las variables period y previousmillis se
declaran para comparar y generar el retardo de los LEDs. El primer LED parpadea después de cada 1 segundo y otro LED parpadea después de 200ms.
unsigned long previousMillis1 = 0;
constlong period1 = 1000;
unsigned long previousMillis2 = 0;
const long period2 = 200;
Se utilizará otra función de milis para generar el retardo de rebote para evitar las múltiples
pulsaciones del pulsador. El enfoque será similar al anterior.
int debouncePeriod = 20;
int debounceMillis = 0;
Las tres variables se utilizarán para almacenar el estado del pulsador como interrupción, LED de
conmutación y estado del pulsador.
bool buttonPushed = false;
int ledChange = LOW;
int lastState = HIGH;
Define la acción del pin que funcionará como INPUT o OUTPUT.
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(toggleLed, OUTPUT);
pinMode(pushButton, INPUT);
Ahora define el pin de interrupción adjuntando interrupción con la definición de ISR y el Modo de
interrupción. Tenga en cuenta que se recomienda utilizar digitalPinToInterrupt(pin_number) al declarar la función attachInterrupt() para traducir el pin digital real al número de interrupción específico.
attachInterrupt(digitalPinToInterrupt(pushButton),
pushButton_ISR, CHANGE);
La subrutina de interrupción está escrita y sólo cambiará la bandera de buttonPushed. Tenga en
cuenta que, la subrutina de interrupción debe ser lo más corta posible, así que trate de escribirla y minimizar las instrucciones extra.
void pushButton_ISR(){ buttonPushed = true;}
El bucle comienza almacenando el valor de los milis en una variable currentMillis que almacenará el valor del tiempo transcurrido cada vez que el bucle itera.
unsigned long currentMillis = millis();
Hay un total de tres funciones en la multitarea, parpadeo de un LED en 1 segundo, parpadeo del
segundo LED en 200ms y si se presiona el botón entonces se apaga/enciende el
LED. Así que vamos a escribir tres partes para hacer esta tarea.
La primera es cambiar el estado del LED después de cada 1 segundo comparando los milis transcurridos.
if (currentMillis - previousMillis1 >= period1) {
previousMillis1 = currentMillis;
if (ledState1 == LOW) {
ledState1 = HIGH;
} else {
ledState1 = LOW;
}
digitalWrite(led1, ledState1);
}
Del mismo modo, el segundo conmuta el LED después de cada 200ms mediante la comparación de los milisegundos transcurridos. Esto ya está explicado anteriormente en este artículo.
if (currentMillis - previousMillis2 >= period2) {
previousMillis2 = currentMillis;
if (ledState2 == LOW) {
ledState2 = HIGH;
} else {
ledState2 = LOW;
}
digitalWrite(led2, ledState2);
}
Por último, la bandera buttonPushed es monitoreada y luego de generar un retardo de debounce de 20ms sólo conmuta el estado del LED que corresponde al botón push conectado como interrupción.
if (buttonPushed = true) // check if ISR is called
{
if ((currentMillis - debounceMillis) > debouncePeriod && buttonPushed) // generate 20ms debounce delay to avoid multiple presses
{
debounceMillis = currentMillis; // save the last debounce delay time
if (digitalRead(pushButton) == LOW && lastState == HIGH) // change the led after push button is pressed
{
ledChange = ! ledChange;
digitalWrite(toggleLed, ledChange);
lastState = LOW;
}
else if (digitalRead(pushButton) == HIGH && lastState == LOW)
{
lastState = HIGH;
}
buttonPushed = false;
}
}
Con esto termina el tutorial de Arduino sobre millis(). Ten en cuenta que para
acostumbrarse a millis(), sólo tienes que practicar para implementar esta
lógica en algunas otras aplicaciones. También puedes ampliarla para usar
motores, servomotores, sensores y otros periféricos.
El código completo para demostrar el uso de la función millis en Arduino se
proporciona a continuación.
Código
/* Multitasking using Arduino millis() function
Author : CircuitDigest (circuitdigest.com)
*/
int led1 = 6; // led1 connected at pin 6
int led2 = 7; // led1 connected at pin 7
int toggleLed = 5; // push button controlled led connected at pin 5
int pushButton = 2; // push butoon connected at pin 2 which is also interrupt pin
int ledState1 = LOW; // to determine the states of led1 and led2
int ledState2 = LOW;
unsigned long previousMillis1 = 0; //store last time LED1 was blinked
const long period1 = 1000; // period at which led1 blinks in ms
unsigned long previousMillis2 = 0; //store last time LED2 was blinked
const long period2 = 200; // period at which led1 blinks in ms
int debouncePeriod = 20; // debounce delay of 20ms
int debounceMillis = 0; // similar to previousMillis
bool buttonPushed = false; // interrupt routine button status
int ledChange = LOW; // to track the led status last
int lastState = HIGH; // to track last button state
void setup() {
pinMode(led1, OUTPUT); // define pins as input or output
pinMode(led2, OUTPUT);
pinMode(toggleLed, OUTPUT);
pinMode(pushButton, INPUT);
attachInterrupt(digitalPinToInterrupt(pushButton), pushButton_ISR, CHANGE); // use interrupt pin2
}
void pushButton_ISR()
{
buttonPushed = true; // ISR should be as short as possible
}
void loop() {
unsigned long currentMillis = millis(); // store the current time
if (currentMillis - previousMillis1 >= period1) { // check if 1000ms passed
previousMillis1 = currentMillis; // save the last time you blinked the LED
if (ledState1 == LOW) { // if the LED is off turn it on and vice-versa
ledState1 = HIGH; //change led state for next iteration
} else {
ledState1 = LOW;
}
digitalWrite(led1, ledState1); //set LED with ledState to blink again
}
if (currentMillis - previousMillis2 >= period2) { // check if 1000ms passed
previousMillis2 = currentMillis; // save the last time you blinked the LED
if (ledState2 == LOW) { // if the LED is off turn it on and vice-versa
ledState2 = HIGH;
} else {
ledState2 = LOW;
}
digitalWrite(led2, ledState2);//set LED with ledState to blink again
}
if (buttonPushed = true) // check if ISR is called
{
if ((currentMillis - debounceMillis) > debouncePeriod && buttonPushed) // generate 20ms debounce delay to avoid multiple presses
{
debounceMillis = currentMillis; // save the last debounce delay time
if (digitalRead(pushButton) == LOW && lastState == HIGH) // change the led after push button is pressed
{
ledChange = ! ledChange;
digitalWrite(toggleLed, ledChange);
lastState = LOW;
}
else if (digitalRead(pushButton) == HIGH && lastState == LOW)
{
lastState = HIGH;
}
buttonPushed = false;
}
}
}