Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Arduino UNO - Jak migać diodą pin 13 niezależnie od pętli void loop?

15 Sty 2017 01:42 3615 22
  • Poziom 8  
    Witam, jestem początkującym użytkownikiem arduino, zatem mam pytanie.

    Chciałbym aby dioda na płytce migała sobie niezależnie od pracy programu.
    Jednak jeśli po drodze pojawia się komenda delay program zwleka z wykonaniem czynności (digitalRead)
    i przycisk trzeba przytrzymywać dłużej, aby wywołać funkcję. Czy da się w funkcji loop wykonać 2 oddzielne
    niezależne pętle tak aby komenda delay nie powodowała opóźnienia w odczycie przycisku?
    Kod wygląda tak:
    Kod: c
    Zaloguj się, aby zobaczyć kod
  • Pomocny post
    Poziom 37  
    Witam,
    w rzeczy samej, funkcje typu "delay" odbierają sterowanie na czas opóźnienia, nic się wówczas nie dzieje.
    W prosty sposób można jednak zrealizować taką "kooperacyjną" wielozadaniowość.
    Należy zrezygnować w ogóle z funkcji "delay", zamiast nich wywoływać w kółko w głównej pętli (czyli loop) funkcje, które będą odmierzały czas od jakiegoś momentu startowego (za pomocą "millis", tudzież "micros") i porównywały go z żądanym opóźnieniem. Jeżeli czas opóźnienia upłynął, zwracają wartość "upłynął", jeżeli nie upłynął to "nie upłynął". Generalna zasada jest w przykładzie pod tytułem "blink without delay".
    Trochę wyższą szkołą jazdy jest zagnieżdżenie odpowiedniego kodu w przerwaniach timerów.

    Pozdrawiam
  • Pomocny post
    Poziom 36  
    Przeanalizuj(zastosuj) przykładowy szkic "blink without delay". Zrezygnuj ze stosowania funkcji delay na rzecz millis , micros.
  • Poziom 8  
    Przy zastosowaniu "blink without delay" reakcja na przycisk jest natychmiastowa, niezależnie od stanu diody. Jednak podczas wykonywania programu w pętli dioda przestaje migać. Czy da się tak zrobić, żeby dioda migała niezależnie od wykonywanego programu w pętli? Czy arduino po prostu wykonuje po kolei wiersze i nie ma możliwości wykonywania dwóch czynności jednocześnie?
  • Poziom 32  
  • Poziom 8  
    Program obecnie wygląda tak:
    Kod: c
    Zaloguj się, aby zobaczyć kod
  • Poziom 32  
  • Poziom 8  
    Czyli przełączanie ledów pin12 - pin8 też muszę zrobić na millis() ?
  • Poziom 37  
    Witam,
    najlepiej będzie zrealizować tę sekwencję za pomocą automatu, który będzie się przełączał pomiędzy poszczególnymi stanami (statemachine). Instrukcje realizujące automat wykonują się bardzo szybko, a czasy opóźnień upływają sobie "w tle". Dzięki temu procesy, które co prawda nie wykonują się jednocześnie, są na tyle szybko przełączane, że sprawiają wrażenie jednoczesności.

    Pozdrawiam
  • Użytkownik usunął konto  
  • Pomocny post
    Poziom 36  
    orzatko napisał:

    (1). Czy da się tak zrobić, żeby dioda migała niezależnie od wykonywanego programu w pętli?
    (2). Czy arduino po prostu wykonuje po kolei wiersze i nie ma możliwości wykonywania dwóch czynności jednocześnie?

    Ad(1) – krótka odpowiedź jest Tak, da się zrobic. Jak? – odpowiedź będzie niżej
    Ad(2) – na to pytanie odpowiedzią jest też – absolutnie tak(!), chyba że interrupt przerwie tą sekwencje ze swoją listą zadań i po wykonaniu tej listy, powróci aby kontynuowac tą sekwencję.

    Z dyskusji wynika że masz na myśli didę na pinie 13. Dlaczego akutat 13?
    Dlaczego na płytce arduino wybrano pin 13 i pod niego podpięto diodę z rezystorem, przeznaczoną do wykorzystania w programach testowych jako pewnego rodzaju sygnalizacja (?). Bootloader też ją wykorzystuje podczas wpisywania nowego programu.
    Analizując pinmapping Arduino, okazuje się że pin 13 jest to PB5 processora – jest to pin najmniej urzyteczny w całym systemie AVR, bez dodatkowych alternatywnych funkcji.
    Jezeli chcesz realizowac błyskanie diodą absolutnie niezaleznie od przebiegu programu i masz na myśli akurat diodę 13 to takiej możliwości nie ma(!). Owszem, możesz wykorzystac któryś z timerów i w jego przerwaniu obsługiwac tę diodę, lecz to pociąga za sobą zależnośc udziału processora, a ty tego nie chcesz.
    Istnieje oczywiście taka możliwoś (bez udziału processora) lecz na innym porcie – nie 13!
    Możesz skonfigurowac dowolny timer do pracy na przykład w trybie CTC i skojarzony z tym timerem ‘jego’ port wyjściowy będzie zmieniał swój stan niezależnie od czynności processora.
    Nie mozesz kompletnie uśpic processora, bo wymagany jest oscylator. Poszukaj w tabeli który tryb ‘SLEEP’ na to pozwala.
    Jeżeli wykorzystujesz w programie funkcję millis() to dla błyskania diodą nie możesz wykorzystac timera0 bo zajmuje go właśnie ta funkcja. Masz jeszcze dwa inne; Timer1 i Timer2.

    Kilka słów odnośnie funkcji millis(). Czas odmierzany tą funkcją jest bardzo dokładny bo jest to pochodna od wewnętrznej w arduino funkcji Timer0_millis(). Problem z dokładnością jej stosowania wynika ze sposobu odczytu tego czasu i jego interpretacji.
    Dla przykładu: możesz miec super dokładny zegar (oparty na wnętrzu atomowym!) lecz dowiesz sie jaką godzinę wskazuje tylko wtedy gdy go czytasz,. - za chwilę już nie wiesz jaka jest godzina, bo nie wiesz jak długo trwała ta chwila. – musisz znów odczytac. Tak samo jest z millis – odczytujesz stan tego unikalnego zegara tylko wtedy gdy na to pozwoli sekwencja programu. Z tego właśnie powodu w twojej funkcji porównana odczytu millis() z zadanym interwałem:

    if (currentMillis - previousMillis >= interval) {

    masz zależnośc (>=) co właśnie powoduje pewną niedokładnośc; ubezpieczenie że gdy spóźnię się z odczytem to wartośc (>) większa jest tez akceptowana bez wnikania o ile jest ona większa.

    e marcus
  • Poziom 8  
    Dzięki za odpowiedzi.

    e marcus, wybrałem diodę wbudowaną, bo jest ona na płytce i sygnalizuję mi pracę programu (wyszło to przypadkiem, bo gdy nie jest wykonywana żadna funkcja dioda miga szybko, natomiast podczas wykonywania operacji przestaje migać) Proces ten jest realizowany za pośrednictwem millis() (jeszcze się tego nie nauczyłem - skopiowałem gotowca; arduino posiadam kilka dni i dopiero się uczę) Język programowania przypomina mi częściowo basica, którego dawno temu uczyłem się w podstawówce, ale komendy nie te same niestety.
    Wracając do tematu, wbudowany led ma służyć tylko do informacji, że arduino działa. (taki niby bajer) Ale na moim poziomie programowania (czyli żadnym) informuje mnie czy wykonywane są operacje. Fajnie gdyby dioda zamiast gaśnięcia przy wykonywaniu operacji migała wolniej zamiast gasnąć. Nie chcę wykorzystywać jej do zaawansowanych operacji, tylko do tej jednej, ale jeśli to w czymś przeszkadza, to zrezygnuję z tego.
    emarcus napisał:
    chyba że interrupt przerwie tą sekwencje ze swoją listą zadań i po wykonaniu tej listy, powróci aby kontynuowac tą sekwencję.


    To jest coś co będzie mnie interesowało w przyszłości, dlatego od razu zadam pytanie?
    funkcji delay zakładam, że nie da się przerwać (poza przyciskiem resetu na płytce) ale jeśli wykonywana jest pętla FOR albo proces millis() czyli miganie diodą jak u mnie to da się go przerwać?

    Wyjaśnię jeszcze skąd moje pierwotne pytania:

    Chciałbym zrobić sterowanie dwóch drzwi przesuwnych sterowanych silnikami DC (a dokładniej sterowane są podnośnikami szyb samochodowych, które posiadają oddzielne podłączenie zasilania 12V i sterowania "góra" , "dół" - sterowanie po podaniu +12V oraz posiadają wbudowane wyłączniki nadprądowe).
    Aby zamknąć jedne drzwi potrzebuję około 5 sek. Nie mogę stosować komendy delay, bo w tym czasie zamknięcie drugich drzwi było by niemożliwe. Dlatego moje pytanie o pominięcie DELAY i ewentualne przerwanie wykonywanej funkcji (jeżeli podczas zamykania drzwi zechcę je otworzyć)
    Nie proszę o gotowy kod - bo to mnie niczego nie nauczy, ale o wskazanie czym się kierować i ewentualną pomoc w wyjaśnieniu na jakiej zasadzie to działa. (przy millis trochę się już nauczyłem ale dalej nie rozumiem całości)
    Zakładam, że jeśli nauczę się migać oddzielnie wbudowaną diodą i wykonywać oddzielne operacje będzie to jakiś sukces.
  • Poziom 37  
    Witam,
    poruszanie wspomnianymi drzwiami w oparciu o upływ czasu nie jest zbyt dobrym rozwiązaniem. Faktyczne czasy zamykania i otwierania mogą się nieco zmieniać, poza tym przy zmianach decyzji (otwieranie podczas zamykania lub odwrotnie) możesz pogubić się z wyznaczeniem ostatecznego czasu ruchu tych drzwi.

    Lepiej będzie zastosować czujniki zamknięcia / otwarcia i użyć ich sygnałów do zbadania w jakiej pozycji są te drzwi. Ewentualnie, żeby uniknąć interferencji działania czujników i wyłącznika prądowego można te czujniki umieścić "nieco wcześniej" mechanicznie, po pojawieniu się odpowiedniego sygnału z czujnika, można wysterowanie silnika przedłużyć, powiedzmy o 200 ms, żeby być pewnym, że drzwi otworzyły się lub zamknęły całkowicie. Wskazane jest również zastosowanie jakiegoś timeout'u, jeżeli drzwi nie osiągną żądanej pozycji w ustalonym czasie, zapewne wystąpił jakiś błąd, tak czy owak należy wyłączyć sterowanie i zasygnalizować awarię.

    Program, w którym różne akcje dzieją się pozornie jednocześnie można napisać według dość prostych zasad, nawet bez użycia przerwań, umieszczając wszystko w głównej pętli.

    Funkcja "loop" kręci się w kółko "sama z siebie", jeżeli nie jest to absolutnie konieczne należy unikać zagnieżdżania w niej dodatkowych pętli, które mogą przejmować sterowanie na zbyt długi czas i w konsekwencji spowalniać działanie innych akcji.
    W przypadku sterowania obiektami mechanicznymi (typu drzwi) procesor Arduino, mimo tego, że nie jest jakimś demonem wydajności, jest od tych obiektów wielokrotnie szybszy, podczas ruchu tych drzwi funkcja "loop" (o ile nie jest nadmiernie przeładowana) wykona się setki lub tysiące razy.
    Poszczególne akcje należy kodować za pomocą możliwie małej liczby instrukcji, to co już zrobiłeś na podstawie przykładu (czyli miganie diodą) zawiera raptem kilka instrukcji, które procesor wykonuje w stosunkowo krótkim czasie. Przy okazji, zmianę częstotliwości migania można zrealizować zastępując stałą "interval' odpowiednią zmienną, częstotliwość migania będzie zależała od wartości liczbowej przechowywanej aktualnie przez tę zmienną, kod jest cały czas ten sam.

    Jeżeli jeszcze nie czytałeś, zapoznaj się z tym materiałem:
    http://playground.arduino.cc/Code/TimingRollover
    Opisane są zasady, którymi należy się kierować aby uniknąć niekorzystnego działania kodu wynikającego ze zjawiska "timer rollover".

    Ponadto, używając mechanicznych przełączników, powinieneś zapoznać się z zasadami maskowania niekorzystnych zjawisk wynikających z odbijania się styków tych przełączników (debouncing), szeroko opisane, również w zasobach Arduino.

    Pozdrawiam
  • Poziom 36  
    orzatko napisał:

    . Fajnie gdyby dioda zamiast gaśnięcia przy wykonywaniu operacji migała wolniej zamiast gasnąć. Nie chcę wykorzystywać jej do zaawansowanych operacji, tylko do tej jednej, ale jeśli to w czymś przeszkadza, to zrezygnuję z tego.

    Popatrz na to bardziej realnie:Aby migac diodą, coś musi to robic w sposób raczej dedykowany. Może to byc processor, lub jak wcześniej wspomniałem alternatywnie 'zatrudnic dowolny' timer , który będzie to robił na swoim zintegrowanym wyjściu.
    Innej możliwości nie ma!!!

    Jeżeli robi to processor i pośrednio wykorzystuje timer, co masz w przypadku stosowania millis(), to procedura stosowana w millis ‘uwalnia’ processor od ciągłej obserwacji upływu czasu na okres określony w interval (załóżmy 200 milisec ). Ponieważ procedura ta nie ma ‘specjalnych przywilejów’ i jest realizowana/czytana w kolejności jak każde inne wg listy, to po wydaniu decyzji na przykład: zapalic diodę, processor zapomina o tym (szczegóły patrz niżej) i wykonuje kojene zadane funkcje do końca pętli, a następnie zaczyna od początku aż do miejsca sprawdzenia stanu zegara millis(). Jeżeli postawiony tam warunek nie jest spełniony (chocby brakowało 1 milisec) to program biegnie dalej o klejny obrót pętli, w którym byc może warunek jest spełniony, lecz moze byc grubo przekroczony (z powodu czasu zajętego przez instrukcje/kommendy i funkcie zawarte w pętli), też jest akceptowany, a wtedy może ją zgasic jeżeli program taką decyzję przewiduje.
    Aby sygnalizowac aktywnosc wystarczyłoby tylko zapalic tą diodę (na czas powiedzmy aktywności silnika otwierania/zamykania bramy); przed rozpoczęciem processu i zgasic ją po zakończeniu, wtedy odwrócisz od niej uwagę processora.

    orzatko napisał:

    dlatego od razu zadam pytanie?
    funkcji delay zakładam, że nie da się przerwać (poza przyciskiem resetu na płytce) ale jeśli wykonywana jest pętla FOR albo proces millis() czyli miganie diodą jak u mnie to da się go przerwać?

    Jest to błędne założenie!!!
    Zacznij na tym etapie od założenia, że przycisk RESET jest z grubsza funkcjonalnie równoznaczny z wyłączeniem i załączeniem processora.
    Wszystkie funkcje, pętle i processy włącznie z delay mogą byc i są przerywane przez każdy dowolny interrupt (Reset to tez interrupt ze specjalną funkcją i priorytetem).
    Wyjątkiem jest funkcja millis() - żaden interrupt jej nie przerywa i nie resetuje za wyjątkiem Reset.
    Tu nie nalezy mylic processu pomiaru czasu wykorzystując tę funkcję. Proces pomiaru może byc przerwany jak każde inne procesy, natomiast sama funkcja millis() nie da się ani przerwac, ani wyzerowac!!! Programista też nie ma na to wpływu.

    Róznica w sposobie pomiaru czasu przez delay a stosowaniu funkcji millis() polega na tym że:
    Podczas delay(); - kolejne pozycje programu nie są realizowane, -inaczej mówiąc, program jest zatrzymany do czasu odliczenia delay. Naturalnie kazdy interrupt może to liczenie przerwac i processor będzie realizował listę zawartą w jego vektorze, i dopiero po jej skończeniu co oczywiście zabiera czas powraca do miejsca gdzie mu przerwano i kontynuuje do zakończenia liczenie zadanej wartości delay().
    Dla odmiany w metodzie z użyciem millis(), program zapamiętuje tylko bieżący stan tego zegara traktując go umownie jako ‘poprzedni’ i kontynuuje dalszą pracę programu, jednak za jakiś czas musi powrócic i odczytac ponownie aktualny stan millis() porównując z poprzednim zaczym podejmie decyzję.
    Jezeli cokolwiek zostało przerwane przez interrupt to czas wykonania tej funkcji jest wydłużony o czas wykonania wszystkiego co ten interrupt ze sobą niesie !

    Oczywiście, przy sterowanu ruchu jakichkolwiek objektów; tu u ciebie drzwi (bramy, cokolwiek) stosuj metodę zaproponowaną przez kol. w poście wyżej.
    Wydaj polecenie 'otwieraj -załącz silnik' i przechodż do kolejnych pozycji w pętli. Jeżeli przyjdzie potrzeba zatrzymania otwierania, to wyłącznik krańcowy (mechaniczny, optyczny, magnetyczny etc.) ustawiony w tej pozycji da sygnał na wejście z interruptem, w którego obsłudze będzie 'zatrzymaj silnik'.
    W praktyce stosuje się bardziej skomplikowane obwody i oprogramowanie przewidujące srodki bezpieczeństwa nie tylko w obawie uszkodzenia sprzętu , ale także i to głównie z uwagi na bezpieczeństwo ludzi.

    e marcus
  • Użytkownik usunął konto  
  • Poziom 33  
    @Piotrus_999 Ano jest co przerywać. Na avr odczyt zmiennych 16/32 bitowych nie jest atomowy. Z tego co wiem to taki odczyt realizuje funkcja millis. Więc jak już chcesz sie do czegoś przyczepić to źle wybrałeś. To nie nukleło i stm32.
  • Poziom 36  
    Piotrus_999 napisał:
    emarcus napisał:
    Wyjątkiem jest funkcja millis()


    A co tu przerywać? Odczyt jednej zmiennej?


    Albo nie doczytałeś, albo (jak zwykle(!)) - nie zrozumiałes całości fragmentu...
    Przeciez pisałem w twoim języku, ze nie chodzi o sam odczyt, lecz o funkcję millis().

    e marcus
  • Użytkownik usunął konto  
  • Użytkownik usunął konto  
  • Poziom 8  
    Dziękuję wszystkim za odpowiedź. Bardzo mi to pomogło - Temat zamykam :)