Hola y bienvenidos a nuestro post de hoy. Hoy se trata de un boceto que probablemente todos conozcan y que ya haya subido a su microcontrolador varias veces: el Blink Sketch.
Yo mismo nunca he prestado mucha atención al boceto, y solo lo uso para verificar si un módulo da alguna señal de vida.
El uso de delay (1000) en el Blink Sketch tiene una desventaja importante: el microcontrolador espera un segundo (1000 ms) y no puede hacer nada más a tiempo. Por lo tanto, no es posible encender y apagar brevemente un segundo LED durante el retraso.
También se vuelve problemático cuando está trabajando en un proyecto en el que se debe consultar el estado de un pin mientras parpadea, p. para verificar si se presionó un interruptor. Si se presiona un botón durante el retraso y se suelta antes del final del retraso, el microcontrolador no se da cuenta.
Tomemos el siguiente fragmento de código como ejemplo:
nulo configuración() {
pinMode(3, SALIDA); pinMode(4, SALIDA); pinMode(8, ENTRADA); } nulo bucle() { si (digitalRead(8) == ALTO){ digitalWrite(4, !digitalRead(4)); // Enciende / apaga el LED en el pin 4 } digitalWrite(3, ALTO); // LED en el pin 3 ENCENDIDO retrasar(1000); // pausa digitalWrite(3, BAJA); // LED en el pin 3 apagado retrasar(1000); // pausa }
Al comienzo del bucle "void loop ()" leemos el estado del botón en el pin 8. Si esto es ALTO, el valor del pin 4 se invierte usando digitalWrite en el pin 4.
Luego cambiamos el pin 3 a ALTO, esperamos un segundo, cambiamos el pin nuevamente a BAJO y esperamos nuevamente por un segundo.
Entonces, si presionamos el botón durante los dos retrasos, no pasa nada. Solo cuando mantenemos el botón presionado mientras se llama a digitalRead (8), activamos o desactivamos el segundo LED.
Esto se volvió problemático, p. con el sistema de alarma que presentamos recientemente en el blog. Un sensor de movimiento activó la alarma allí. Si ahora estaba parado frente al dispositivo y deseaba ingresar el código de seguridad, la alarma se activó una y otra vez, lo que provocó una pausa en la entrada.
Por lo tanto, se recomienda no utilizar la función delay ().
Una posible solución sería, p. Disminuya el retraso a 100 ms o incluso menos, y cuente un contador con cada pasada. Cuando ha alcanzado un cierto valor, lo restablece a 0 y cambia el LED.
Podemos guardar todo el trabajo gracias a la función millis (). La función millis () devuelve el número de milisegundos que han pasado desde que se inició el programa actual. El número se restablece a 0 después de aproximadamente 50 días.
Para guardar este valor en una variable, debe ser del tipo "unsigned long". Esto permite un número entre0 y 4.294.967.295.
En nuestro ejemplo, creamos una variable llamada "previousMillis" y creamos una variable del tipo "const" en la que definimos el intervalo de 1000 ms.
Al ejecutar "void loop ()" colocamos la salida de milis () en la variable "currentMillis". Luego se verifica si han pasado 1000 ms desde la última ejecución. Si es así, el valor de previousMillis se sobrescribe con el de currentMillis y el LED se enciende o apaga.
sin firmar largo anteriorMillis = 0; // ahorra la hora a la que se realizó el último cambio const largo intervalo = 1000; // duración de la pausa en ms nulo configuración() { pinMode(3, SALIDA); // LED 1 pinMode(4, SALIDA); // LED 2 pinMode(8, ENTRADA); // botón } nulo bucle() { si (digitalRead(8) == ALTO){ digitalWrite(4, !digitalRead(4)); // Enciende / apaga el LED en el pin 4 } sin firmar largo currentMillis = millis(); // La hora actual se guarda en currentMillis si (currentMillis - anteriorMillis >= intervalo) { // Si han pasado más de 1000 ms anteriorMillis = currentMillis; // Se registra la hora del último cambio digitalWrite(3, !digitalRead(3)); // LED encendido o apagado } }
Ahora podemos cambiar el segundo LED con el botón, independientemente de lo que haga el primer LED.
Si ha copiado todo e intentado el código, verá que el bucle principal se ejecuta con tanta frecuencia que no es tan fácil cambiar el segundo LED con un botón.
Aquí deberíamos eliminar el botón, pero ese es un tema para otra publicación de blog.
Espero que la publicación de hoy te haya mostrado lo fácil que puede ser trabajar con millis ().
Le agradecemos el creciente interés y los comentarios de las últimas semanas, y nos despedimos hasta mañana.
Tuyo Markus Neumann
2 comentarios
W. Parschfeld
Alternativ wäre eine parallele Programmierung zu empfehlen – kann für viele zufälligen parallele Ereignisse benutz werden: z.B. Analog-Uhr auf graf. Display, Abfrage verschiedener Steuerimpulse, Kommunikation über I2C etc. (z.B. vom WiFi-Modul), laden von SD-Card… Gute Erfahrungungen habe ich mit der Bibliothek TimedAction gemacht …
A. Deppe
Im Prinzip eine gute Idee – allerdings gibt es genau für dieses Problem (wenigstens für die MKR’s) die “scheduler” Funktion. Zusammen mit dem Scheduler macht die Delay (und Yield) Funktion dann wieder absolut Sinn , denn Delay legt die entsprechende Funktion in der Delay angewendet wird für den Delay Zeitraum schlafen und gib die Rechenzeit für andere “geschedulte” Funktionen frei. Das ist erheblich effizienter und ermöglicht multitasking.
Noch eine Bemerkung zu dem angeschnittenen Thema “Alarmanlage”: Wenn man Eingaben sicher verarbeiten will, ist es meistens eine gute Idee das Interupt handling dafür zu bemühen. Ich würde versuchenden die void loop so klein wie möglich zu halten und da nur das Interupt handling drin zu realisieren. Die eigentliche Verarbeitung würde ich in eigene Funktionen auslagern. Das ist nicht nur übersichtlicher sondern auch Speicher effizienter, weil die Funktionen/Variablen nur zur Laufzeit instanziert werden.
Auch hier kann der Scheduler helfen – um z.B. Ein- und Ausgabe zu deserialisieren.