Skocz do zawartości
Komentator

Kurs Arduino II - #9 - wielozadaniowość, opóźnienia z millis()

Pomocna odpowiedź

html_mig_img
Funkcja delay (do wprowadzania opóźnień) to jedna z pierwszych rzeczy, której uczymy się podczas poznawania Arduino. Jej działanie może jednak generować wiele kłopotów.Na szczęście z pomocą przychodzi nam bardziej rozbudowane rozwiązanie bazujące na funkcji millis. Dzięki niej Arduino może wykonywać kilka zadań "jednocześnie".

UWAGA, to tylko wstęp! Dalsza część artykułu dostępna jest na blogu.

Przeczytaj całość »

Poniżej znajdują się komentarze powiązane z tym wpisem.

Udostępnij ten post


Link to post
Share on other sites

Jeszcze raz przepraszam wszystkich, którzy musieli czekać na kolejną część tak długo. Niestety plany związane z tym artykułem miałem zupełnie inne i wszystko przeciągało się w nieskończoność. W końcu wpadłem w pułapkę, bo chciałem „za dobrze” i ciągle przekładałem ten artykuł. Ostatecznie postanowiłem zmienić tematykę na millis(). Myślę, że będzie to korzystne dla wszystkich, bo warto znać tę funkcję. W kolejnym artykule krótkie podsumowanie kursu + plany na przyszłość 🙂

Udostępnij ten post


Link to post
Share on other sites

Mam takie dwie drobne uwagi. Przełączanie zadań odbywa się raczej tysiące razy na sekundę, niż miliardy - to dość czasochłonny proces wbrew pozorom. Druga sprawa to animacja przy miganiu dwóch diodek (jedna 0,5s, druga 1s) - może warto byłoby zmienić długość filmu, bo teraz dioda migająca co 0,5s ma taki dziwny przeskok. Dość długo próbowałem zrozumieć skąd w tym programie to dodatkowe, szybki mrugnięcie 🙂

Udostępnij ten post


Link to post
Share on other sites

Teraz o millis?!?!? 😅😅 Trzeba było 2 miechy temu to by zaoszczędziło Elvisovi żmudnego uczenia mojej skromnej osoby 😋 .

Artykuł bardzo przydatny, choć dla mnie teraz już mniej(dzięki forum Forbota 😃 ).

Dziękuję za ogrom włożonej pracy.

Mała uwaga, uważam, że lepiej pisać takie artykuły z nauką podstaw i uczenia kodowania, podawanie niuansów i sztuczek, niż skupianie się na praktyce. Wydaje się, że powyższy artykuł jest idealnie wyważony, podaje to co ważne w teorii i jakiś przykład dla zrozumienia, ew. podpowiedzi co do innego wykorzystania zdobytej wiedzy niż tylko jak w przykładzie.

Do artykułu jak bym jeszcze dopisał sposób

millis()-poprzedniZapisanyMillis[zwał jak zwał]>2000

korzystamy tylko z jednej zmiennej co przynajmniej, jak dla mnie, znacznie upraszcza całą robotę 🙂

Pozdrawiam

Udostępnij ten post


Link to post
Share on other sites

Elvis, dzięki za uwagi! Już poprawione, faktycznie animacja była źle złożona.

SOYER, nie ukrywam, że Wasza dyskusja była też trochę inspiracją do tego artykułu. Dzięki za miłe słowa 😉 Jeśli chodzi o zaproponowane przez Ciebie rozwiązanie:

millis()-poprzedniZapisanyMillis > 2000

To mam mieszane uczucia. Na pewno jest to poprawne, gdy mamy jedno opóźnienie w programie i potrzebujemy sprawdzić aktualny czas tylko raz. Jeśli takich wątków będzie 50, to w Twojej metodzie wywołasz 50 razy funkcję millis(), a w mojej tylko raz. W tym przypadku nie zrobi to wielkiej różnicy, ale obawiam się, że takie podejście wykształca nawyk, aby za każdym razem wywoływać funkcję (nawet jeśli wynik będzie taki sam). Jeśli zamiast millisa mielibyśmy bardziej rozbudowaną funkcję, to lepiej byłoby wykonać ją raz, zapisać wynik do zmiennej i później się do niej odwoływać - zamiast 50x wywoływać funkcję, która może być "zasobożerna", a zawsze zwróci ten sam wynik (w danym obiegu pętli). Jednak tak jak mówię, to tylko mój nawyk i nie jest on związany bezpośrednio z millis.

Udostępnij ten post


Link to post
Share on other sites

Ja widzę co najmniej dwie zalety wersji opisanej w kursie. Po pierwsze, każde wywołanie millis() może zwrócić inną wartość. W opisywanym przypadku to nie problem, ale lepiej sobie wyrabiać "odruch" używania tej samej wartości.

Druga sprawa to typ ze znakiem. Kod w kursie używał typu unsigned long. Natomiast jeśli użyjemy odejmowania jak w kodzie zaproponowanym przez SOYER-a, kompilator ma pełne prawo użyć typu long. Niestety wtedy pojawią się problemy w przypadku przepełnienia typu (czyli po 50 dniach). Wesja z kursu działa za to poprawnie nawet wtedy.

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites
Ja widzę co najmniej dwie zalety wersji opisanej w kursie. Po pierwsze, każde wywołanie millis() może zwrócić inną wartość. W opisywanym przypadku to nie problem, ale lepiej sobie wyrabiać "odruch" używania tej samej wartości.

Druga sprawa to typ ze znakiem. Kod w kursie używał typu unsigned long. Natomiast jeśli użyjemy odejmowania jak w kodzie zaproponowanym przez SOYER-a, kompilator ma pełne prawo użyć typu long. Niestety wtedy pojawią się problemy w przypadku przepełnienia typu (czyli po 50 dniach). Wesja z kursu działa za to poprawnie nawet wtedy.

Ja kiedyś wielowątkowość na AVR rozwiązałem za pośrednictwen zadeklarowanego rejestru flagowego sterowanego w przerwaniu z timera1, z flagami 1ms,10ms,100ms,1 ... , potem w pętli głównej podpinałem funkcje które mały być wykonywane w odpowiednich odstępach czasowych.

Pozdrawiam i Zdrowego Sylwestra 😉

Udostępnij ten post


Link to post
Share on other sites

Ok, czyli twierdzicie, że lepiej jak wrócę do currentMillis i previousMillis, w sumie macie rację, tylko, że wtedy trzeba pamiętać o aktualizowaniu w odpowiednim momencie obu zmiennych, co wiem, że początkującemu sprawia trochę problemu w momencie kiedy tych stoperów jest więcej niż dwa, trzy jednocześnie. Wtedy łatwiej jest stosować pojedynczą zmienną i traktować ją jako start odliczania.

Ale oczywiście zgadzam się, że nadpisana currentMillis jest stała i kropka. A millis() nawet wywołana jedną linijkę kodu niżej będzie się różnić od tej linijkę wyżej... bo chyba o to wam chodziło tak?

Udostępnij ten post


Link to post
Share on other sites

Wielu początkującym będzie sprawiać trudność zapanowanie nad zmiennymi określonymi przez millis() i dlatego w/g mnie szkoda, że nie wspomniano w artykule o bibliotekach ułatwiających "wielozadaniowość" w Arduino jak np. Timers czy leOS. Korzystając np. z leOS wystarczy raz zaplanować przedziały czasowe wykonywanych zadań (wątków) i tyle. W sumie artykuł ciekawy i powinien być obowiązkową lekturą, każdego rozpoczynającego przygodę z Arduino.

Pozdrawiam,

Udostępnij ten post


Link to post
Share on other sites

Nadmiar bibliotek bywa problematyczny, co już na tym forum się przewijało: biblioteka X działa pięknie ale nic mi nie działa jak dodam bibliotekę Y...

Jeśli Timers to ta biblioteka, którą znalazłem, to w niej właściwie nie ma kodu - taki cienki wrapper w C++ do wywołania millis(). Moim zdaniem lepiej zrozumieć jak millis() działa.

Natomiast IeOS to jakiś koszmarek. Wywoływanie funkcji z poziomu przerwań powinno być karane. Lepiej już uruchmić "prawdziwy" RTOS, chociażby FreeRTOS działa na Arduino.

Udostępnij ten post


Link to post
Share on other sites

Tak w kwestii formalnej:

Oba rozwiązania porównania - zarówno SOYERa:

   millis()-poprzedniZapisanyMillis > 2000

jak i użyte w kursie:

   roznicaCzasu > 1000

są co najmniej nieprawidłowe (a przynajmniej kompilator swoje zdanie na ten temat powinien wypluć - chyba że ktoś ma wyłączone wyświetlanie ostrzeżeń).

Prawidłowo w kursie powinno być:

   roznicaCzasu > 1000UL
  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites
Nadmiar bibliotek bywa problematyczny, co już na tym forum się przewijało: biblioteka X działa pięknie ale nic mi nie działa jak dodam bibliotekę Y...

Święta racja, sam się o tym przekonałem i super przydatnym byłoby zestawienie timerów Arduino wykorzystywanych przez popularne biblioteki. Szukałem i ... nie znalazłem, ale szukam takiego zestawienia dalej.

Natomiast IeOS to jakiś koszmarek. Wywoływanie funkcji z poziomu przerwań powinno być karane.

Z pewnością masz rację. Należy pamiętać, że procedury obsługi przerwań nie mogą być czasochłonne, ale uważam, że dla prostych zastosowań leOS nie jest zły, a może wiele uprościć. No ale to moje zdanie - początkującego.

Pozdrawiam,

Udostępnij ten post


Link to post
Share on other sites

Belferek, specjalnie nie zajmowałem się tutaj bibliotekami, bo obawiam się, że część osób od razu ominęłaby "ręczne eksperymenty" z millis(). Bez zrozumienia tego tematu nie ma sensu brać się za jakieś gotowce. Może w przyszłości zrobię takie zestawienie - zobaczymy 😉

ethanak, słusznie, już dopisuję! Ciężko mi teraz stwierdzić jak dokładnie radzi sobie z tym tematem Arduino, bo wygląda na to, że wszystko działa bez dopisku UL poprawnie, ale dla formalności zaraz dodam taką informację. Dzięki!

Udostępnij ten post


Link to post
Share on other sites

W reference znajdziemy:

Notes and Warnings

U & L formatters:

By default, an integer constant is treated as an int with the attendant limitations in values. To specify an integer constant with another data type, follow it with:

a 'u' or 'U' to force the constant into an unsigned data format. Example: 33u

a 'l' or 'L' to force the constant into a long data format. Example: 100000L

a 'ul' or 'UL' to force the constant into an unsigned long constant. Example: 32767ul

Więc pisząc np. 1000UL odwołujemy się do wartości unsigned long, a takiego typu wartość (unsigned long) zwraca millis()

Returns

Number of milliseconds since the program started (unsigned long)

Pozdrawiam,

Udostępnij ten post


Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Gość
Napisz odpowiedź...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.


×
×
  • Utwórz nowe...