- Co to jest wielozadaniowość?
- Dlaczego pomijać opóźnienie () w Arduino?
- Dlaczego używać millis ()?
- Wymagane składniki
- Schemat obwodu
- Programowanie Arduino UNO do wielozadaniowości
Wielozadaniowość doprowadziło komputery do rewolucji, gdzie jeden lub więcej programów można uruchomić jednocześnie, co zwiększa wydajność, elastyczność, zdolności adaptacyjnych i wydajności. W systemach wbudowanych mikrokontrolery mogą również obsługiwać wielozadaniowość i jednocześnie wykonywać dwa lub więcej zadań bez zatrzymywania bieżących instrukcji.
W tym samouczku dowiemy się, jak Arduino wykonuje wielozadaniowość z funkcją Arduino millis. Zasadniczo funkcja delay () jest używana w Arduino do wykonywania okresowych zadań, takich jak miganie diody LED, ale ta funkcja opóźnienia () zatrzymuje program na określony czas i nie pozwala na wykonywanie innych operacji. W tym artykule wyjaśniono, w jaki sposób możemy uniknąć używania funkcji delay () i zastąpić ją millis (), aby wykonywać więcej niż jedno zadanie jednocześnie i sprawić, że Arduino będzie kontrolerem wielozadaniowym. Zanim przejdziemy do szczegółów, zacznijmy od zaniżania wielozadaniowości.
Co to jest wielozadaniowość?
Wielozadaniowość oznacza po prostu jednoczesne wykonywanie więcej niż jednego zadania lub programu. Prawie wszystkie systemy operacyjne obsługują wielozadaniowość. Tego rodzaju systemy operacyjne są znane jako MOS (wielozadaniowy system operacyjny). MOS może być systemem operacyjnym dla komputerów przenośnych lub stacjonarnych. Dobrym przykładem wielozadaniowości na komputerach jest sytuacja, w której użytkownicy jednocześnie uruchamiają aplikację pocztową, przeglądarkę internetową, odtwarzacz multimediów, gry, a jeśli użytkownicy nie chcą korzystać z aplikacji, działa ona w tle, jeśli nie jest zamknięta. Użytkownik końcowy korzysta ze wszystkich tych aplikacji w tym samym czasie, ale system operacyjny traktuje tę koncepcję nieco inaczej. Omówmy, jak system operacyjny zarządza wielozadaniowością.
Jak widać na rysunku, procesor dzieli czas na trzy równe części i przypisuje każdą część do każdego zadania / aplikacji. Tak wygląda wielozadaniowość w większości systemów. Koncepcja będzie prawie taka sama dla Arduino Multitasking, z wyjątkiem tego, że rozkład czasu będzie nieco inny. Ponieważ Arduino działa z niską częstotliwością i pamięcią RAM w porównaniu do laptopa / telefonu komórkowego / komputera, czas poświęcony na każde zadanie również będzie inny. Arduino ma również szeroko stosowaną funkcję delay () . Ale zanim zaczniemy, omówmy, dlaczego nie powinniśmy używać funkcji delay () w żadnym projekcie.
Dlaczego pomijać opóźnienie () w Arduino?
Jeśli weźmie się pod uwagę dokumentację referencyjną Arduino, istnieją dwa rodzaje funkcji opóźniających, pierwsza to delay (), a druga to delayMicroseconds (). Obie funkcje są identyczne pod względem generowania opóźnienia. Jedyną różnicą jest to, że w funkcji delay () przekazywana liczba całkowita parametru jest wyrażona w milisekundach, tj. Jeśli napiszemy opóźnienie (1000), to opóźnienie wyniesie 1000 milisekund, czyli 1 sekundę. Podobnie w funkcji delayMicroseconds (), przekazywany parametr jest w mikrosekundach, tj. Jeśli napiszemy delayMicroseconds (1000), to opóźnienie wyniesie 1000 mikrosekund, czyli 1 milisekundę.
I tu chodzi o to, że obie funkcje wstrzymują program na czas, który upłynął w funkcji opóźnienia. Więc jeśli podajemy opóźnienie 1 sekundy, to procesor nie może przejść do następnej instrukcji, dopóki nie minie 1 sekunda. Podobnie, jeśli opóźnienie wynosi 10 sekund, program zatrzyma się na 10 sekund, a procesor nie pozwoli na przejście do następnych instrukcji przed upływem 10 sekund. Utrudnia to działanie mikrokontrolera pod względem szybkości i wykonywania instrukcji.
Najlepszym przykładem wyjaśnienia wady funkcji opóźnienia jest użycie dwóch przycisków. Rozważmy, że chcemy przełączać dwie diody LED za pomocą dwóch przycisków. Więc jeśli wciśnięty jest jeden przycisk, odpowiednia dioda LED powinna świecić przez 2 sekundy, podobnie, jeśli naciśnięty jest drugi, dioda LED powinna świecić przez 4 sekundy. Ale kiedy używamy delay (), jeśli użytkownik naciśnie pierwszy przycisk, program zatrzyma się na 2 sekundy, a jeśli użytkownik naciśnie drugi przycisk przed 2 sekundowym opóźnieniem, to mikrokontroler nie zaakceptuje wejścia tak, jak program jest w fazie zatrzymania.
Oficjalna dokumentacja Arduino wyraźnie wspomina o tym w opisie funkcji Uwagi i ostrzeżenia o opóźnieniu (). Możesz przejść i sprawdzić to, aby było bardziej zrozumiałe.
Dlaczego używać millis ()?
Aby przezwyciężyć problem spowodowany użyciem opóźnienia, programista powinien użyć funkcji millis (), która jest łatwa w użyciu, gdy nabierzesz nawyku i będzie wykorzystywać 100% wydajności procesora bez generowania żadnych opóźnień w wykonywaniu instrukcji. millis () to funkcja, która po prostu zwraca liczbę milisekund, które upłynęły od momentu, gdy płyta Arduino zaczęła uruchamiać bieżący program bez jego zawieszania. Ten numer czasowy przepełni się (tj. Powróci do zera) po około 50 dniach.
Podobnie jak Arduino ma delayMicroseconds (), ma również mikro wersję millis () jako micros (). Różnica między micros i millis polega na tym, że micros () przepełni się po około 70 minutach, w porównaniu do millis (), czyli 50 dni. W zależności od aplikacji możesz użyć funkcji millis () lub micros ().
Używanie millis () zamiast delay ():
Aby użyć funkcji millis () do pomiaru czasu i opóźnienia, musisz zarejestrować i zapisać czas, w którym miało miejsce działanie, aby rozpocząć odliczanie, a następnie sprawdzać w odstępach czasu, czy upłynął zdefiniowany czas. Jak już wspomniano, zapisz bieżący czas w zmiennej.
unsigned long currentMillis = millis ();
Potrzebujemy jeszcze dwóch zmiennych, aby dowiedzieć się, czy upłynął wymagany czas. Przechowaliśmy bieżący czas w zmiennej currentMillis , ale musimy również wiedzieć, kiedy rozpoczął się okres czasu i jak długi jest ten okres. Dlatego zadeklarowano Interval i previousMillis . Interwał poinformuje nas o opóźnieniu czasowym, a previosMillis zapisze ostatni czas wystąpienia zdarzenia.
unsigned long previousMillis; długi okres bez znaku = 1000;
Aby to zrozumieć, weźmy przykład prostej migającej diody LED. Okres = 1000 powie nam, że dioda LED będzie migać przez 1 sekundę lub 1000 ms.
const int ledPin = 4; // numer pinu LED podłączony int ledState = LOW; // używany do ustawiania stanu diody LED unsigned long previousMillis = 0; // zapisze czas ostatniego mignięcia diody const long period = 1000; // okres, w którym migać w ms void setup () { pinMode (ledPin, OUTPUT); // ustaw jako wyjście ledpin } void loop () { unsigned long currentMillis = millis (); // zapisz aktualny czas if (currentMillis - previousMillis> = period) {// sprawdź, czy minęło 1000 ms previousMillis = currentMillis; // zapisz ostatni raz, gdy mrugnąłeś diodą LED if (ledState == LOW) {// jeśli dioda LED jest wyłączona włącz ją i odwrotnie ledState = HIGH; } else { ledState = LOW; } digitalWrite (ledPin, ledState); // ustaw diodę LED z ledState, aby ponownie migała } }
Tutaj oświadczenie
Przerwania w Arduino działają tak samo jak w innych mikrokontrolerach. Płytka Arduino UNO ma dwa oddzielne piny do dołączania przerwań na pinach 2 i 3 GPIO. Omówiliśmy to szczegółowo w samouczku Arduino Interrupts, gdzie możesz dowiedzieć się więcej o przerwaniach i jak z nich korzystać.
Tutaj pokażemy wielozadaniowość Arduino, obsługując dwa zadania jednocześnie. Zadania będą obejmować miganie dwóch diod LED z różnym opóźnieniem wraz z przyciskiem, który będzie służył do sterowania stanem włączenia / wyłączenia diody LED. A więc trzy zadania będą wykonywane jednocześnie.
Wymagane składniki
- Arduino UNO
- Trzy diody LED (dowolny kolor)
- Odporności (470, 10k)
- Zworki
- Płytka prototypowa
Schemat obwodu
Schemat obwodu demonstrujący użycie funkcji Arduino Millis () jest bardzo łatwy i nie ma wielu elementów do podłączenia, jak pokazano poniżej.
Programowanie Arduino UNO do wielozadaniowości
Programowanie Arduino UNO do wielozadaniowości będzie wymagało tylko logiki stojącej za działaniem millis (), co zostało wyjaśnione powyżej. Przed rozpoczęciem programowania Arduino UNO do wielozadaniowości zaleca się przećwiczenie migania diod LED za pomocą milis, aby logika była jasna i czuła się komfortowo z millis (). W tym samouczku przerwanie jest używane jednocześnie z funkcją millis () do wykonywania wielu zadań jednocześnie. Przycisk będzie przerywał. Tak więc za każdym razem, gdy zostanie wygenerowane przerwanie, tj. Naciśnięty zostanie przycisk, dioda LED przełączy się w stan WŁ. Lub WYŁ.Programowanie rozpoczyna się od zadeklarowania numerów pinów, do których podłączone są diody LED i przycisk.
int led1 = 6; int led2 = 7; int toggleLed = 5; int pushButton = 2;
Następnie piszemy zmienną do przechowywania stanu diod LED do wykorzystania w przyszłości.
int ledState1 = LOW; int ledState2 = LOW;
Jak wyjaśniono powyżej w przykładzie migania, zadeklarowano, że zmienne dla okresu i poprzedniego millis porównują i generują opóźnienie dla diod LED. Pierwsza dioda LED miga co 1 sekundę, a druga dioda LED co 200 ms.
unsigned long previousMillis1 = 0; const long period1 = 1000; unsigned long previousMillis2 = 0; const long period2 = 200;
Kolejna funkcja milisek będzie używana do generowania opóźnienia odbicia, aby uniknąć wielokrotnego naciskania przycisku. Będzie podobne podejście jak powyżej.
int debouncePeriod = 20; int debounceMillis = 0;
Te trzy zmienne będą używane do przechowywania stanu przycisku jak przerwania, przełączanie stanu LED i przyciskiem.
bool buttonPushed = false; int ledChange = LOW; int lastState = HIGH;
Zdefiniuj działanie pinezki, która będzie działać jako INPUT lub OUTPUT.
pinMode (led1, WYJŚCIE); pinMode (led2, WYJŚCIE); pinMode (toggleLed, OUTPUT); pinMode (pushButton, INPUT);
Teraz zdefiniuj pin przerwania, dołączając przerwanie z definicją ISR i trybem przerwania. Zauważ, że zaleca się użycie digitalPinToInterrupt (numer_pinu) podczas deklarowania funkcji attachInterrupt (), aby przetłumaczyć rzeczywisty pin cyfrowy na określony numer przerwania.
attachInterrupt (digitalPinToInterrupt (pushButton), pushButton_ISR, CHANGE);
Podprogram przerwania zostanie zapisany i zmieni on tylko flagę buttonPushed. Zauważ, że podprogram przerwania powinien być możliwie najkrótszy, więc spróbuj go napisać i zminimalizuj dodatkowe instrukcje.
void pushButton_ISR () { buttonPushed = true; }
Pętla rozpoczyna się od zapisania wartości milisekund w zmiennej currentMillis, która będzie przechowywać wartość czasu, który upłynął za każdym razem, gdy pętla się iteruje.
unsigned long currentMillis = millis ();
W wielozadaniowości są w sumie trzy funkcje, miganie jednej diody LED co 1 sekundę, miganie drugiej diody LED co 200 ms, a jeśli przycisk jest wciśnięty, wyłącz / włącz diodę LED. Więc napiszemy trzy części, aby wykonać to zadanie.
Pierwszy jest przełącza stan LED po co 1 sekundę przez porównanie millis upłynął.
if (currentMillis - previousMillis1> = period1) { previousMillis1 = currentMillis; if (ledState1 == LOW) { ledState1 = HIGH; } else { ledState1 = LOW; } digitalWrite (led1, ledState1); }
Podobnie, po drugie, przełącza diodę LED co 200 ms, porównując upływające milisekundy. Wyjaśnienie zostało już wyjaśnione wcześniej w tym artykule.
if (currentMillis - previousMillis2> = period2) { previousMillis2 = currentMillis; if (ledState2 == LOW) { ledState2 = HIGH; } else { ledState2 = LOW; } digitalWrite (led2, ledState2); }
Wreszcie, buttonPushed flaga jest monitorowany i po wygenerowaniu zwłoce czasowej z 20ms po prostu przełącza stan odpowiada LED przycisku do załączonych jako przerwanie.
if (buttonPushed = true) // sprawdź, czy wywołano ISR { if ((currentMillis - debounceMillis)> debouncePeriod && buttonPushed) // wygeneruj opóźnienie odbicia 20 ms, aby uniknąć wielokrotnych naciśnięć { debounceMillis = currentMillis; // zapisz czas opóźnienia ostatniego odbicia if (digitalRead (pushButton) == LOW && lastState == HIGH) // zmień diodę po naciśnięciu przycisku { ledChange =! ledChange; digitalWrite (toggleLed, ledChange); lastState = LOW; } else if (digitalRead (pushButton) == HIGH && lastState == LOW) { lastState = HIGH; } buttonPushed = false; } }
To kończy samouczek Arduino millis (). Zauważ, że aby uzyskać nawyk z millis (), po prostu przećwicz implementację tej logiki w niektórych innych aplikacjach. Można go również rozszerzyć o silniki, serwomotory, czujnik i inne urządzenia peryferyjne. W razie wątpliwości napisz na nasze forum lub skomentuj poniżej.
Pełny kod i wideo do demonstracji użycia funkcji milisek w Arduino znajduje się poniżej.