Czy istnieje sposób, w jaki mogę mieć wiele części programu działających razem bez robienia wielu rzeczy w tym samym bloku kodu?
Jeden wątek czeka na urządzenie zewnętrzne, jednocześnie mrugając diodą LED w innym wątku.
arduino-uno
threads
Bja
źródło
źródło
Odpowiedzi:
W Arduino nie ma obsługi wieloprocesowej ani wielowątkowej. Możesz jednak zrobić coś blisko wielu wątków za pomocą oprogramowania.
Chcesz spojrzeć na Protothreads :
Oczywiście istnieje tutaj przykład Arduino z przykładowym kodem . To pytanie SO może być również przydatne.
ArduinoThread też jest dobry.
źródło
Arduino oparte na AVR nie obsługuje wątków (sprzętowych), nie znam Arduino opartych na ARM. Jednym ze sposobów obejścia tego ograniczenia jest użycie przerwań, zwłaszcza przerwania czasowego. Możesz zaprogramować timer, aby przerywał główną procedurę co tyle mikrosekund, aby uruchomić określoną inną procedurę.
http://arduino.cc/en/Reference/Interrupts
źródło
Możliwe jest wykonywanie wielowątkowości po stronie oprogramowania na Uno. Gwintowanie na poziomie sprzętu nie jest obsługiwane.
Aby osiągnąć wielowątkowość, konieczne będzie wdrożenie podstawowego harmonogramu i utrzymanie listy procesów lub zadań w celu śledzenia różnych zadań, które należy uruchomić.
Struktura bardzo prostego harmonogramu nieprzekazującego byłaby następująca:
Tutaj
tasklist
może być tablica wskaźników funkcji.Z każdą funkcją formularza:
Każda funkcja może wykonywać osobne zadanie, takie jak
function1
wykonywanie manipulacji diodami LED ifunction2
wykonywanie obliczeń zmiennoprzecinkowych. Każde zadanie (funkcja) będzie odpowiedzialne za przestrzeganie przydzielonego mu czasu.Mam nadzieję, że to powinno wystarczyć, aby zacząć.
źródło
Zgodnie z opisem twoich wymagań:
Wygląda na to, że możesz użyć jednego przerwania Arduino dla pierwszego „wątku” (w rzeczywistości wolałbym to nazwać „zadaniem”).
Przerwania Arduino mogą wywoływać jedną funkcję (Twój kod) na podstawie zdarzenia zewnętrznego (poziom napięcia lub zmiana poziomu na cyfrowym pinie wejściowym), które natychmiast uruchomią twoją funkcję.
Jednak jedną ważną rzeczą, o której należy pamiętać w przypadku przerwań, jest to, że wywoływana funkcja powinna być tak szybka, jak to możliwe (zazwyczaj nie powinno być żadnego
delay()
wywołania ani żadnego innego interfejsu API, który byłby zależnydelay()
).Jeśli musisz aktywować długie zadanie po wyzwoleniu zdarzenia zewnętrznego, możesz potencjalnie użyć harmonogramu kooperacyjnego i dodać do niego nowe zadanie z funkcji przerwania.
Drugą ważną kwestią dotyczącą przerwań jest to, że ich liczba jest ograniczona (np. Tylko 2 w UNO). Jeśli więc zaczniesz mieć więcej zdarzeń zewnętrznych, będziesz musiał zaimplementować pewnego rodzaju multipleksowanie wszystkich wejść w jednym, a funkcja przerwania określi, który zmultipleksowany inut był rzeczywistym wyzwalaczem.
źródło
Prostym rozwiązaniem jest użycie harmonogramu . Istnieje kilka implementacji. W skrócie opisano tę, która jest dostępna dla płyt opartych na AVR i SAM. Zasadniczo pojedyncze połączenie rozpocznie zadanie; „szkic w szkicu”.
Scheduler.start () doda nowe zadanie, które uruchomi polecenie taskSetup, a następnie wielokrotnie wywoła taskLoop, tak jak działa szkic Arduino. Zadanie ma swój własny stos. Rozmiar stosu jest parametrem opcjonalnym. Domyślny rozmiar stosu to 128 bajtów.
Aby umożliwić przełączanie kontekstu, zadania muszą wywoływać fed () lub delay () . Istnieje również makro obsługi oczekiwania na warunek.
Makro to cukier składniowy dla następujących elementów:
Oczekiwania można także użyć do synchronizacji zadań. Poniżej znajduje się przykładowy fragment:
Aby uzyskać więcej informacji, zobacz przykłady . Istnieją przykłady od wielokrotnego mrugnięcia diody LED do przycisku odbicia i prostej powłoki z odczytem nieblokującego wiersza poleceń. Szablony i przestrzenie nazw mogą służyć do uporządkowania i ograniczenia kodu źródłowego. Poniższy szkic pokazuje, jak używać funkcji szablonu do wielokrotnego mrugania. Wystarczy 64 bajty na stos.
Istnieje również punkt odniesienia, aby dać wyobrażenie o wydajności, tj. Czas na rozpoczęcie zadania, zmianę kontekstu itp.
Na koniec istnieje kilka klas wsparcia dla synchronizacji i komunikacji na poziomie zadań; Kolejka i semafor .
źródło
Z poprzedniej inkantacji tego forum następujące pytanie / odpowiedź zostało przeniesione do inżynierii elektrycznej. Posiada przykładowy kod arduino do mrugania diody LED za pomocą przerwania timera podczas używania głównej pętli do szeregowego we / wy.
https://electronics.stackexchange.com/questions/67089/how-can-i-control-things-without-using-delay/67091#67091
Repost:
Przerwania są powszechnym sposobem wykonywania zadań, gdy dzieje się coś innego. W poniższym przykładzie dioda LED miga bez użycia
delay()
. Przy każdymTimer1
uruchomieniuisrBlinker()
wywoływana jest procedura obsługi przerwań (ISR) . Włącza / wyłącza diodę LED.Aby pokazać, że mogą się zdarzyć inne rzeczy,
loop()
wielokrotnie zapisuje foo / bar do portu szeregowego niezależnie od migania diody LED.To jest bardzo proste demo. ISR mogą być znacznie bardziej złożone i mogą być wyzwalane przez timery i zdarzenia zewnętrzne (piny). Wiele wspólnych bibliotek jest implementowanych przy użyciu ISR.
źródło
Do tego tematu doszedłem również przy wdrażaniu matrycowego wyświetlacza LED.
Jednym słowem, możesz zbudować harmonogram odpytywania za pomocą funkcji millis () i przerwania timera w Arduino.
Proponuję następujące artykuły Billa Earla:
https://learn.adafruit.com/multi-tasking-the-arduino-part-1/overview
https://learn.adafruit.com/multi-tasking-the-arduino-part-2/overview
https://learn.adafruit.com/multi-tasking-the-arduino-part-3/overview
źródło
Możesz także wypróbować moją bibliotekę ThreadHandler
https://bitbucket.org/adamb3_14/threadhandler/src/master/
Używa harmonogramu przerywającego, aby umożliwić przełączanie kontekstu bez przekazywania wydajności () lub opóźnienia ().
Stworzyłem bibliotekę, ponieważ potrzebowałem trzech wątków i potrzebowałem dwóch z nich, aby działały dokładnie w tym samym czasie, bez względu na to, co robili inni. Pierwszy wątek obsługiwał komunikację szeregową. Drugim działał filtr Kalmana wykorzystujący mnożenie macierzy zmiennoprzecinkowej za pomocą biblioteki Eigen. Trzeci to szybka nić pętli sterującej, która musiała być w stanie przerwać obliczenia macierzy.
Jak to działa
Każdy cykliczny wątek ma priorytet i kropkę. Jeśli wątek o wyższym priorytecie niż bieżący wątek osiągnie swój następny czas wykonania, program planujący wstrzyma bieżący wątek i przełączy się na wyższy. Po zakończeniu wykonywania wątku o wysokim priorytecie program planujący powraca do poprzedniego wątku.
Zasady planowania
Schemat szeregowania biblioteki ThreadHandler jest następujący:
Jak używać
Wątki można tworzyć poprzez dziedziczenie c ++
Lub poprzez createThread i funkcję lambda
Obiekty wątków automatycznie łączą się z uchwytem wątków podczas ich tworzenia.
Aby rozpocząć wykonywanie utworzonych obiektów wątków, wywołaj:
źródło
A oto kolejna biblioteka wielozadaniowości współpracującej z mikroprocesorem - PQRST: kolejka priorytetowa do wykonywania prostych zadań.
W tym modelu wątek jest zaimplementowany jako podklasa a
Task
, która jest planowana na pewien czas w przyszłości (i być może planowana w regularnych odstępach czasu, jeśli, jak to zwykle bywa, podklasyLoopTask
).run()
Metoda obiektu jest wywoływana, gdy zadanie staje się wymagalne.run()
Metoda ma pewne należytą pracę, a następnie zwraca (jest to spółdzielnia bit); zazwyczaj będzie utrzymywał maszynę stanu do zarządzania swoimi działaniami przy kolejnych wywołaniach (trywialnym przykładem jestlight_on_p_
zmienna w przykładzie poniżej). Wymaga to drobnego przemyślenia sposobu organizacji kodu, ale okazało się bardzo elastyczne i niezawodne przy dość intensywnym użyciu.Jest agnostyczny w stosunku do jednostek czasu, więc jest równie szczęśliwy, biegając w jednostkach
millis()
comicros()
, lub w dowolnym innym dogodnym tiku.Oto program „blink” zaimplementowany przy użyciu tej biblioteki. Pokazuje to tylko jedno uruchomione zadanie: inne zadania byłyby zazwyczaj tworzone i uruchamiane w ramach
setup()
.źródło
run()
wywołaniu metody nie jest ona przerywana, więc jest odpowiedzialna za jej szybkie zakończenie. Zazwyczaj jednak wykona swoją pracę, a następnie przełoży się (ewentualnie automatycznie, w przypadku podklasyLoopTask
) na pewien czas w przyszłości. Powszechnym wzorem dla zadania jest utrzymanie pewnej wewnętrznej maszyny stanów (trywialnym przykładem jestlight_on_p_
powyższy stan), aby zachowywała się odpowiednio, gdy będzie następna.run()
. Jest to w przeciwieństwie do wątków kooperacyjnych, które mogą uzyskać wydajność procesora, np. Przez wywołanieyield()
lubdelay()
. Lub wątki zapobiegawcze, które można zaplanować w dowolnym momencie. Uważam, że to rozróżnienie jest ważne, ponieważ zauważyłem, że wiele osób, które przychodzą tutaj w poszukiwaniu wątków, robi to, ponieważ wolą pisać kod blokujący niż maszyny stanowe. Blokowanie prawdziwych wątków, które dają procesor, jest w porządku. Blokowanie zadań RtC nie jest.