Blink Sketch ohne delay() - AZ-Delivery

Ciao e benvenuto al nostro post oggi. Oggi si tratta di uno schizzo che probabilmente tutti conoscono, e ha certamente caricato più volte sul suo microcontrollore: il Blink Sketch.

Io stesso non ho mai prestato molta attenzione allo schizzo, e lo uso solo per verificare se un modulo dà un segno di vita a tutti.

Tuttavia, l'uso di delay(1000) nello schizzo lampeggiante ha un grave inconveniente: il microcontrollore attende un secondo (1000 ms) nel punto e non può fare altro in tempo. Non è quindi possibile accendere e spegnere brevemente un secondo LED durante il ritardo.

Diventa anche problematico se si sta lavorando su un progetto in cui lo stato di un pin deve essere interrogato durante il lampeggiamento, ad esempio per verificare se è stato premuto un interruttore. Se un pulsante viene premuto durante il ritardo e rilasciato prima della fine del ritardo, il microcontrollore non otterrà alcuno di esso.

Prendiamo i frammenti di codice seguenti come esempio:Let's take the following small code snippets as an example:

  Vuoto Installazione() {

  PinMode (Modalità pin)(3, Output);   PinMode (Modalità pin)(4, Output);   PinMode (Modalità pin)(8, Input);
}

Vuoto Ciclo() {   Se (digitalRead (Lettura digitale)(8) == alto){     digitalWrite (Scrittura digitale)(4, !digitalRead (Lettura digitale)(4)); Commuta LED per bloccare 4 ON/OFF   }   digitalWrite (Scrittura digitale)(3, alto); LED per pin 3 ON     Ritardo(1000); Pausa               digitalWrite (Scrittura digitale)(3, Basso); Led per pin 3 OFF       Ritardo(1000); Pausa            
}  

All'inizio del ciclo "void loop()", leggiamo lo stato dal pulsante sul pin 8. Se è impostato su HIGH, il valore del pin 4 viene invertito utilizzando digitalWrite sul pin 4.

Poi si passa pin 3 a HIGH, attendere un secondo, tenere il pin di nuovo a LOW, e attendere un altro secondo.

Quindi, se premiamo il pulsante durante i due ritardi, non succede nulla. Solo quando teniamo premuto il pulsante mentre viene chiamato il digitalRead(8), accendiamo o spegniamo il secondo LED.

Questo è diventato problematico, per esempio, con il sistema di allarme, che abbiamo recentemente presentato nel blog. Un sensore di movimento ha attivato l'allarme lì. Se eri in piedi davanti al dispositivo e volevi inserire il codice di sicurezza, l'allarme è stato attivato più e più volte, il che ha portato a una pausa nell'ingresso.

Pertanto, si consiglia di fare a meno della funzione delay().

Una possibile soluzione potrebbe essere quella di ridurre il ritardo a 100 ms o anche meno e di contare un contatore alto per ogni passaggio. Quando raggiunge un certo valore, reimpostarlo su 0 e spegnere il LED.

Ma possiamo salvare tutto il lavoro grazie alla funzione millis(). La funzione millis() restituisce il numero di millisecondi trascorsi dall'avvio del programma corrente. Il numero viene reimpostato su 0 dopo circa 50 giorni.

Per archiviare questo valore in una variabile, deve essere di tipo "unsigned long". Ciò consente un numero tra 0 e 4.294.967.295.

Nel nostro esempio, creiamo una variabile denominata "previousMillis" e creiamo una variabile di tipo "const" in cui impostiamo l'intervallo di 1000 ms.

Quando passiamo attraverso "void loop()" inpostiamo l'output di milis() nella variabile "currentMillis". Quindi viene controllato se 1000 ms sono passati dall'ultimo passaggio. In tal caso, il valore di previousMillis viene sovrascritto con quello di currentMillis e il LED viene attivato o disattivato.

 

Unsigned Lungo previousMillis = 0; salva l'ora in cui l'ultimo interruttore è stato
Const Lungo Intervallo = 1000; Durata della pausa in ms

Vuoto Installazione() {   PinMode (Modalità pin)(3, Output); LED 1   PinMode (Modalità pin)(4, Output); LED 2   PinMode (Modalità pin)(8, Input);  Pulsante
}

Vuoto Ciclo() {   Se (digitalRead (Lettura digitale)(8) == alto){     digitalWrite (Scrittura digitale)(4, !digitalRead (Lettura digitale)(4)); Commuta LED per bloccare 4 ON/OFF   }
 Unsigned Lungo currentMillis = millis(); L'ora corrente viene memorizzata in currentMillis   Se (currentMillis - previousMillis >= Intervallo) { Se sono trascorsi più di 1000 ms      previousMillis = currentMillis; L'ora dell'ultimo circuito viene registrata    digitalWrite (Scrittura digitale)(3, !digitalRead (Lettura digitale)(3)); Led acceso o spento   }
}

 

Ora possiamo cambiare il secondo LED con il pulsante, indipendentemente da ciò che fa il primo LED.

Se avete ricreato il tutto e provato il codice, troverete che il ciclo principale viene eseguito attraverso così spesso che non è così facile cambiare il secondo LED con un pulsante.

Qui avremmo dovuto sbottonare il pulsante (Engl. debouncing), ma questo è un argomento per un altro post sul blog.

Spero che il nostro contributo di oggi vi abbia mostrato quanto sia facile lavorare con millis(). 

Vorremmo ringraziarvi per il crescente interesse e le numerose puntate di feedback delle ultime settimane e salutarvi fino a domani.

Il tuo Markus Neumann

 

 

 

 

 

Grundlagen software

2 commenti

W. Parschfeld

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

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.

Lascia un commento

Tutti i commenti vengono moderati prima della pubblicazione