delay() Arduino Functie: Tight Loops and Blocking Code
Heb je ooit een Arudino project gemaakt en je wilt iets laten gebeuren op een getimed interval? Misschien wil je elke 3 seconden dat een servo beweegt, of misschien wil je elke 1 minuut een status update naar een webserver sturen.
Hoe doe je dat? Is er een eenvoudige functie die ons daarbij helpt? Ja, die is er! We bespreken de vertragingsfunctie, evenals de millis() functie, in de onderstaande video:
Dit is deel 2 van onze millis() functie mini-serie. Deel 1 helpt ons te begrijpen wat de millis() functie doet, deel 2 bespreekt tight loops en blocking code, en deel 3 bespreekt wanneer de millis() functie de delay() functie overtreft.
Onderwerpen in deze les
- Tight loops
- Blocking code
- Oeroud recept voor inspirerende muziek
Millis() versus Delay()
Dus je wilt iets met een vast interval laten gebeuren en je zoekt een oplossing. Als je antwoord het gebruik van de vertragingsfunctie is, wel, dan heb je een beetje gelijk. Maar er is een andere manier.
De koelere, snazzier optie is de Arduino millis() functie. En hoe meer vertrouwd u bent met het gebruik van de millis-functie, om u te helpen tijd gebeurtenissen in uw Arduino-code, hoe gemakkelijker het zal zijn om later andere delen in uw programma op te nemen.
Als je eenmaal de millis()-functie begint te gebruiken, zul je gelukkiger en vrolijker zijn, en verdorie, mensen zullen je aardig vinden.
Tight Loops
Laten we het eerst eens hebben over het concept van een tight loop. En als we het over een strakke lus hebben, wat betekent dat dan?
Laten we eens kijken naar een Arduino-sketch voor een demonstratie van een strakke lus. Beginnend met de meest elementaire schets, hebben we slechts twee functies: void setup, en void loop. En zoals u wellicht weet, wordt void setup slechts één keer uitgevoerd, waarna het de show overdraagt aan void loop.
Void loop doorloopt vervolgens elke regel code die zich binnen de lus zou kunnen bevinden (binnen deze gekrulde haakjes).
Hij voert de eerste regel uit, dan de tweede, en dan de derde, enzovoort, enzovoort, totdat hij helemaal onderaan staat. En dan gaat het terug naar de top.
Hoe snel voert het de lus uit? Het hangt af van welk Arduino bord je gebruikt, maar een Arduino Uno heeft een kloksnelheid van 16 megahertz. Dat betekent dus dat er elke seconde 16 miljoen instructies plaatsvinden op de Arduino!
Elke regel code is niet noodzakelijkerwijs één instructie. In feite, is het zeer waarschijnlijk dat het meerdere instructies zijn. Maar toch, dat is relatief snel (uw computerprocessor draait waarschijnlijk op Gigahertz-snelheden… dat zijn er miljarden).
Zou je die lege schets dan als een strakke lus beschouwen? Zeker, dat is zo snel en strak als je een lus kunt maken. Omdat er niets binnen de lus is om uit te voeren, is de tijd die het kost om door de schets te gaan praktisch nihil. Anders gezegd, het interval van het begin van de lus tot het einde is kort (daarom is het snel, of “strak”).
Laten we wat regels code toevoegen. We zullen seriële communicatie starten, en vervolgens iets afdrukken naar het seriële monitorvenster.
Is dit een strakke lus? Dat wil zeggen, van het begin van de lus tot het einde van de lus, kost dat veel tijd? Het kost heel weinig tijd, dus dit is een snelle, strakke lus.
Het is echter de moeite waard op te merken dat deze lus niet zo strak is als het vorige voorbeeld. In het vorige voorbeeld, hadden we geen code. Dus het was gewoon racen door de lus. Nu we hier een functie hebben, seriële print, zal het (een klein) beetje tijd kosten om “Ice Ice Baby” af te drukken op de seriële monitor.
Maar dit is nog steeds een vrij snelle lus. Dus laten we een beetje meer code toevoegen. We laten het programma controleren of een knop is ingedrukt, en als dat zo is, laten we iets nieuws naar de seriële monitor sturen
Dus we hebben een knop gedeclareerd en een if-statement gebruikt om te controleren of de knop is ingedrukt. Is de spanning op pin vijf hoog? Zo ja, dan printen we iets anders naar het seriële monitorvenster.
Is dit een strakke lus? Dus, van het begin van de lus, tot het einde van de lus, is dat vrij snel?
Ja, het is nog steeds vrij snel. Dit is een vrij strakke lus. We hebben vier regels code. We printen naar de seriële monitor, en dan kijken we of er een knop wordt ingedrukt. En als dat zo is, drukken we iets af. Nog steeds strak, nog steeds snel.
Next laten we een vertraging toevoegen aan dit programma met behulp van de Arduino delay() functie. U kunt hieronder zien dat we een vertraging van duizend milliseconden (1 seconde) aan de lus hebben toegevoegd.
Is dit nog steeds een strakke lus? Is de tijd, van het begin van de lus tot het einde van de lus, veel tijd? Nee, dit is zeker geen strakke lus. De code begint snel, we doen de seriële afdruk, maar dan worden we gestopt daar bij de vertragingsfunctie.
Het hele programma komt tot stilstand terwijl we wachten op deze vertragingscode om te eindigen.
Wanneer de Arduino aan deze lijn van code komt, het is een beetje zoals naar de kruidenierswinkel gaan. U komt in de 12 items of minder lijn, maar dan trekt de kerel voor u zijn chequeboek, en begint een cheque uit te schrijven. Je weet dat je daar nog wel een minuutje zal staan. Hier is het net zo.
Dit is dus geen krappe lus. De tijd van het begin van de lus tot het einde van de lus is vrij aanzienlijk. Vooral vergeleken met de laatste paar programma’s. De orde van grootte van de tijd is enorm.
Nu wat we hier hebben geprobeerd aan te tonen is dat dit hele idee over strakke lussen relatief is. Het hangt allemaal af van je toepassing. Als je elke 10 miljoenste van een seconde de status van een sensor moet controleren, dan is een programma met drie regels code misschien niet strak genoeg, dat hangt er maar vanaf.
Een ander punt is dat niet alle regels code evenveel tijd nodig hebben om uit te voeren. Als je een functie aanroept die een heleboel dingen doet, zoals seriële print bijvoorbeeld, dan kan die ene regel code een stuk langer duren dan 10 andere regels code.
Dus de strakheid van een lus is een relatief idee.
Blocking Code
Wanneer een programma op een bepaald punt stopt, en enige tijd nodig heeft om bepaalde code uit te voeren, kan die code blocking code worden genoemd. Dit is een algemene term.
In ons programma hebben we de vertragingsfunctie die als blokkerende code fungeert. Geen van de code na de vertraging kan worden uitgevoerd totdat de vertraging voorbij is, dus het wordt geblokkeerd.
Blokkerende code is echter niet alleen wanneer we de delay() functie gebruiken.
Laten we ons programma nemen en ons ontdoen van de vertraging, maar we voegen een for-lus toe. Onze for-lus drukt getallen en tekst af naar de seriële poort.
Dus hoe lang loopt deze lus? Hij gaat een tijdje lopen omdat hij 100 iteraties moet doorlopen voordat hij stopt.
En hoe zit het met de code na deze for-lus? Is het in staat om te draaien? Nee, die moet wachten omdat hij geblokkeerd wordt door de for-lus.
Deze for-lus zit genest in de hoofdlus. Is de for-lus een “strakke” lus? Laten we, voordat je antwoordt, nog eens benadrukken hoe je erover moet denken: kost het veel tijd om deze for-lus van boven naar beneden te doorlopen?
Nou, eigenlijk niet. Het zijn maar twee regels code. Dus dit is een vrij krappe lus.
Maar het is een krappe lus die veel iteraties moet doorlopen voordat het programma bij de code eronder kan komen. Dus zelfs een strakke lus, als hij gedwongen wordt om meerdere iteraties te doorlopen, kan onze code blokkeren.
Meer over de delay() functie
Laten we het eens wat meer over deze vertragingsfunctie hebben. Wat hebben we tot nu toe vastgesteld?
Eerst hebben we gezegd dat de vertragingsfunctie de strakheid van een lus vermindert. Als je een strakke lus hebt en je voegt de vertragingsfunctie toe, dan gaat het meer tijd kosten en wordt de lus minder strak. Dat wil zeggen, de hoeveelheid tijd die nodig is om van het begin van de lus naar beneden te komen, zal toenemen met de vertragingsfunctie.
We weten ook dat de vertragingsfunctie code blokkeert. Dit gaat hand in hand met wat we zojuist zeiden. Als de vertragingsfunctie draait, blokkeert hij andere code terwijl hij vertraagt.
Je zou kunnen denken dat deze vertragingsfunctie een totale slappeling is! Het zal nooit iets betekenen in onze projecten. Maar voor veel eenvoudige programma’s die we schrijven, werkt de vertragingsfunctie fantastisch. Hij is eenvoudig te gebruiken, gemakkelijk te spellen en doet precies wat hij zegt.
Dus we moeten de delay()-functie niet per se uit onze programmeergereedschapskist verbannen. We moeten gewoon erkennen dat het een eenvoudige programmeerfunctie is die in veel gevallen kan werken.
Er is echter een moment waarop je tegen problemen aanloopt. Dat heeft te maken met het blokkerende effect dat de vertragingsfunctie in ons programma heeft.
In de volgende les in de serie, deel 3, gaan we vaststellen waar dit echt een probleem wordt. Je zult leren wanneer het zinvol is om de delay() functie in een programma te gebruiken, en wanneer het tijd is om over te schakelen op het gebruik van de millis() functie.
Review
Eerst hebben we het gehad over de strakheid van een lus. We zeiden dat de strakheid van een lus relatief is. Het hangt af van wat uw toepassing is.
Tweede, we spraken over blokkerende code, of code die blokkeert. In wezen is het een algemene term die we kunnen geven aan code die enige tijd gaat duren om uit te voeren, en het gaat andere delen van ons programma te stoppen met draaien terwijl het uitvoert.
We hopen dat genoten van deze les! In het volgende deel van deze serie, gaan we verder met onze reis te leren hoe de millis functie te gebruiken om getimede, repetitieve gebeurtenissen in onze Arduino-code te maken. Tot de volgende keer!