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

zliczanie impulsów w przerwaniach - Arduino

xyproxx 19 Paź 2017 22:31 1245 21
  • #1 19 Paź 2017 22:31
    xyproxx
    Poziom 10  

    Witam wszystkich!

    Mam problem, otóż stworzyłem prosty projekt, którego zadaniem jest wyświetlanie temperatury/wilgotności z DHT-11 na wyświetlaczu i zliczanie impulsów. Niestety, coś jest nie tak - zliczane są jakieś dziwne wartości, przypuszczam, że błąd wynika z mojej niewiedzy, lub z jakiegoś błędu w składni. Przerwanie działa poprawnie - zapala się dioda. Gdy dodawanie (wynik=wynik+1) zastąpiłem po prostu przypisaniem liczby, "wydostaje" się ona poprawnie poza funkcję (stykx=1). Jednak, gdy próbuję cokolwiek dodawać, wprowadzać wartość do wnętrza funkcji, nie działa. Próbowałem również z boolean i wykonywać obliczenia poza interruptem - działa, ale nie o to chodzi... bardzo proszę o pomoc, ten problem jest dość irytujący :( Wstawiam dwa kody: jeden uproszczony, z najważniejszymi fragmentami i drugi pełny.

    Kod: c
    Zaloguj się, aby zobaczyć kod



    Kod: c
    Zaloguj się, aby zobaczyć kod

    0 21
  • #2 20 Paź 2017 16:34
    jaclew
    Poziom 16  

    Zamieść schemat ponieważ aby odnieść się do kodu wypadałoby wiedzieć jak on wygląda.
    Szczególnie ciekawi mnie to:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    Co to za kontaktron, że wymaga zasilania?
    oraz jak pin cyforowy nr 3 Arduino został podłączony do tegoż kontaktronu?
    Jakie Arduino wykorzystujesz?
    Co ma robić w funkcji przerwania takie działanie:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    będzie to liczyło przyrost zmiennej stykx ?

    xyproxx napisał:
    Próbowałem również z boolean

    a coś bliżej?
    Poza tym - prostą rzecz jak zliczanie impulsów w przerwaniu, że tak powiem - strasznie zabałaganiłeś :-?

    0
  • #3 20 Paź 2017 16:52
    xyproxx
    Poziom 10  

    Ogromne dzięki za zaangażowanie!

    Jeżeli chodzi o schemat, to nie ma nic szczególnego. Wyświetlacz na I2C (A4, A5), DHT-11 podłączony do D2 + zasilania i masy.
    Do D3 kontaktron i opornik 1k. Druga nóżka opornika do GND (żeby wejście nie łapało zakłóceń), druga nóżka kontaktronu do D12, na który podaję stan wysoki i tym samym "zasilam go". Kontaktron najzwyklejszy ze szklaną bańką, typ MDSR-7-15-20, lub podobny. Same przerwania działają, bo zapala się dioda.

    Problem pojawia się, kiedy próbuję w obrębie funkcji wyzwalanej przerwaniem wykonać operację dodawania i wyciągnąć wynik. Odnoszę wrażenie, że albo coś ze składnią jest nie tak, albo o czymś nie wiem i funkcja wykonywana przez przerwanie nie potrafi pobrać z LOOP-a wartości, którą ma powiększyć.

    Dokładnie tak, jak napisałeś, chcę po prostu zliczać ilość przerwań.
    Odnośnie boolean - ten fragment kodu jest zostawiony, tylko zakomentowałem fragment "stykx=stykx+1;", aby dwie różne funkcje nie miały tego samego zadania. Chodzi o to, że jeżeli w obrębie przerwania przypisywałem zmiennej boolean określony stan, to działało, tzn. w głównym loop-ie miałem ten stan zmieniony, więc na siłę można było dodać warunek if... i wtedy zliczać. Natomiast z tego, co pamiętam, gdy próbowałem w obrębie przerwania odczytać stan bool'a i zmienić go (!stan), to już nie działało.

    Przepraszam, że chaotyczny kod, walczyłem z tym dość długo na różne sposoby i w przypływie desperacji trochę wyczyściłem i wrzuciłem na forum. Stąd też pierwszy jest mocno wyczyszczony, żeby nie trzeba było czytać niepotrzebnych fragmentów.

    0
  • Pomocny post
    #4 20 Paź 2017 18:35
    jaclew
    Poziom 16  

    xyproxx napisał:
    Problem pojawia się, kiedy próbuję w obrębie funkcji wyzwalanej przerwaniem wykonać operację dodawania i wyciągnąć wynik.

    napisz w przerwaniu tak:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    albo krócej:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Drgania styków kontaktronu będą powodować chaotyczne naliczanie.
    Zrób debouncing programowo albo sprzętowo.

    0
  • #5 20 Paź 2017 21:06
    xyproxx
    Poziom 10  

    Działa, dzięki ogromne!

    Debouncing zrobię, celowo go nie dodawałem na tym etapie, bo nie wiedziałem, czy będzie potrzebny, a nie chciałem komplikować kodu. Faktycznie czasem zlicza kilkukrotnie, delay(20) powinno załatwić sprawę.

    Pytanie: dlaczego pierwsza linijka jest niepoprawna? Czy w funkcji nie trzeba podać argumentu? Jeżeli stykx zastąpię styk, lub w ogóle zostawię pusty nawias,, albo też dam void(stykx) to zliczanie następuje bez problemów. Przecież ta funkcja pobiera dane z zewnątrz, więc dlaczego to działa?

    Kod: c
    Zaloguj się, aby zobaczyć kod

    0
  • #7 21 Paź 2017 19:00
    xyproxx
    Poziom 10  

    kompilator nie zgłaszał zastrzeżeń, ale poprawiłem i bez zmian.

    Dałem zaraz za NoInterrupts delay 50 a nawet 250, ale często zlicza podwójnie, potrójnie, tak, jakby nie zdążył wyłączyć przerwań. Bez delaya zlicza i po kilkanaście, ale z delay-em również często zbyt dużo.

    Bardzo bym również prosił o pomoc przy wyjaśnieniu tego zachowania funkcji - czy może nie trzeba deklarować zmiennych, bo wywołanie przerwań znajduje się w setup, czyli de facto, skoro przerwania są aktywne cały czas, to ten kod jest gdzieś "ukryty" w LOOP-ie?

    0
  • #8 21 Paź 2017 22:43
    BlueDraco
    Specjalista - Mikrokontrolery

    To nie będzie działać. Zacznij od określenia maksymalnej częstotliwości impulsów i minimalnego czasu trwania impulsu. Zaprogramuj timer na zgłaszanie przerwań z okresem nieco krótszym niż minimalna szerokość impulsu. W przerwaniu timer sprawdzaj stan wejścia i porównuj z poprzednim stanem. Przy wykryciu zmiany z 1 na 0 zlicz impuls. Nie używaj przerwań od portów przy współpracy ze stykami.

    0
  • #9 21 Paź 2017 23:04
    xyproxx
    Poziom 10  

    Dziękuję za wyjaśnienie - co prawda dodałem "sprzętowy" debouncing i sytuacja się poprawiła, ale metoda zaproponowana przez Ciebie wydaje się być bardziej poprawna.

    Czy mogę jeszcze prosić o wyjaśnienie tej kwestii związanej z działaniem funkcji?

    0
  • #10 22 Paź 2017 00:07
    markolsrz
    Poziom 10  

    Przerwania to sprzętowe funkcje mikrokontrolera (Atmega328p w większości Arduino).
    Polecenie attachInterrupt() ustawia odpowiednie rejestry tak by odpowiedni bodziec (u Ciebie zmiana 0->1 na pinie 3) uruchomił przerwanie, a to uruchomiło funkcję jego obsługi (u Ciebie count()).
    Inne zadania zostają na ten czas wstrzymane, a kolejne przerwania czekają na swoją kolej do zakończenia wykonywania tej funkcji.
    Z tego wynika, że funkcja obsługi przerwania nie powinna pobierać, ani zwracać żadnych wartości, a funkcje noInterrupts() i interrupts() wewnątrz są niepotrzebne.
    Pętla loop może tylko operować na tych samych zmiennych globalnych co funkcja obsługi przerwania.
    Funkcja delay() sama opiera się na przerwaniach, więc gdy są wyłączone nie zadziała jak należy.

    Druga rzecz to kwestia dostępu do zmiennej styk wewnątrz loop.
    Ponieważ jest to zmienna wielobajtowa musisz przepisać ją do jakiejś innej przy wyłączonych przerwaniach zanim zaczniesz coś robić z tą wartością. (np. noInterrupts();i=styk;interrupts(); )
    Szersze omówienie tego problemu i inne rozwiązania znajdziesz tutaj (topic3229081), ale moje początkowe propozycje z tego wątku są przekombinowane :| .

    Sprawdź też: Arduino - AttachInterrupt()

    0
  • #11 22 Paź 2017 09:30
    Mikroprocesorowiec
    Poziom 11  

    Nie wiem jaka częstotliwość impulsów bedzie chciał mierzyć. Nie znam też czasu trwania imulsu. Rozwiązań jest kilka.
    - użycie wejścia Tx timera. Możesz zliczać Fmax = Fcpu / 2.
    - w przerwaniu od timera sprawdzasz czy nastąpiła zmiana stanu piny. Tak zmierzysz do kilku kHz
    - impulsy doprowadzasz do wejścia INTx. Tak zmierzysz kilkadziesiat kHz.

    Najlepsza jest metoda taktowania timera wejściem Tx. Szybka, bardzo mało obciąża procesor.

    0
  • #12 22 Paź 2017 10:55
    BlueDraco
    Specjalista - Mikrokontrolery

    Całkiem błędna sugestia - sygnał pochodzi ze styków, a ATmega nie ma sprzętowego filtrowania wejść timera. Tu sprawdzi się tylko zliczanie programowe w przerwaniu timera. Proponuję na początek częstotliwość ok. 50..100 Hz - powinieneś w ten sposób bezpiecznie ominąć drgania styków.

    0
  • #13 22 Paź 2017 10:59
    Mikroprocesorowiec
    Poziom 11  

    BlueDraco napisał:
    Całkiem błędna sugestia - sygnał pochodzi ze styków, a ATmega nie ma sprzętowego filtrowania wejść timera. Tu sprawdzi się tylko zliczanie programowe w przerwaniu timera. Proponuję na początek częstotliwość ok. 50..100 Hz - powinieneś w ten sposób bezpiecznie ominąć drgania styków.

    Jeśli styki to ja bym zrobił na przerwaniu INT a w nim:
    Kod: c
    Zaloguj się, aby zobaczyć kod


    Oczywiscie jeszcze przerwanie od timera co 1ms gdzie:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Naturalnie zmienne volatile.

    Teraz doczytałem, ze to kontaktron. Kontaktrony maja dość krótki drżenie styków, mozna wię zmienic" Timer = 30;" na "Timer = 10;". Naturalnie najlepiej sprawdzic w nocie lub sprawdzic oscyloskopem jak długo drżą styki.

    0
  • #14 22 Paź 2017 13:04
    xyproxx
    Poziom 10  

    Dziękuję za wszystkie odpowiedzi!

    Jednak nadal nie rozumiem jednej rzeczy: na chwilę obecną fragment kodu wygląda w następujący sposób:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    I pomimo użycia void, działa poprawnie. Podobnie w podanym w linku powyżej przykładzie zaczerpniętym wprost ze strony Arduino:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    I właśnie tego nie rozumiem, z tego, co wiem do poprawnego działania funkcji trzeba podać jej parametr, czyli np. int, a nie void i wskazać w nawiasie zmienne, którymi będziemy operować. Dlaczego więc te przykłady działają na void-ach?

    0
  • #15 22 Paź 2017 13:12
    Mikroprocesorowiec
    Poziom 11  

    Cytat:
    podać jej parametr, czyli np. int, a nie void

    Zmienna owszem, może być typu void, ale robi się to niezwykle rzadko. Parametr int, void? Wydaje mi się, że coś ci się myli. Napisz dokładniej czego nie rozumiesz.

    0
  • #16 22 Paź 2017 13:31
    BlueDraco
    Specjalista - Mikrokontrolery

    Mikro:
    Pomysł, żeby zamiast jednego przerwania użyć dwóch, jest dla mnie ciut kontrowersyjny. Zalet nie widzę żadnych, wad parę by się znalazło - np. zbędne obciążanie procesora przerwaniami od styków.
    A atrybut volatile tu akurat jest zbędny.

    xyproxx:
    Podstawy języka C się kłaniają. Jakie argumenty ma mieć Twoja funkcja? Jaką wartość ma zwracać? Moim zdaniem odpowie brzmi: żadne, żadną.

    0
  • #17 22 Paź 2017 14:02
    Mikroprocesorowiec
    Poziom 11  

    BlueDraco napisał:
    Mikro:
    Pomysł, żeby zamiast jednego przerwania użyć dwóch, jest dla mnie ciut kontrowersyjny.

    Dałem gotowe rozwiazanie rozwiązujące problem drżenia styków. Niech kolega zaproponuje inne lepsze rozwiązanie. Naturalnie monża zaangażować dodatkowy timer zamiast programowego zaproponowane prze mnie. Niestery, AVR sa raczej ubogie w timery, a poświęcanie sprzętu na tak banalny cel wydaje mi sie głupotą. Czekam więc na lepsze rozwiązanie.

    BlueDraco napisał:

    Zalet nie widzę żadnych, wad parę by się znalazło - np. zbędne obciążanie procesora przerwaniami od styków.
    A atrybut volatile tu akurat jest zbędny.

    Czyli w/g kolegi, jak w przerwaniu używam zmiennej , to volatile jest niepotrzbny. Mam pokazać jak w ASM bedzie wyglądał kod i, ze przeważnie nigdy warunken takiej zmiennej gdy = 0 nie bedzie spełniony? Cos sie źle dogadujemy, bo po postach kolegi widzę, że wie kiedy volatile jest potrzebny.

    BlueDraco napisał:

    xyproxx:
    Podstawy języka C się kłaniają. Jakie argumenty ma mieć Twoja funkcja? Jaką wartość ma zwracać? Moim zdaniem odpowie brzmi: żadne, żadną.

    Czasem void uzywa sie przy przekazywaniu argumentu do funkcji. Zwracać void to powiem szczerze nie wiem czy można (pewnie tak) ale to bedzie (pewnie wskaźnik). Tak z ciekawości to sprawdzę.

    0
  • #18 22 Paź 2017 17:10
    xyproxx
    Poziom 10  

    BlueDraco napisał:
    Mikro:
    Pomysł, żeby zamiast jednego przerwania użyć dwóch, jest dla mnie ciut kontrowersyjny. Zalet nie widzę żadnych, wad parę by się znalazło - np. zbędne obciążanie procesora przerwaniami od styków.
    A atrybut volatile tu akurat jest zbędny.

    xyproxx:
    Podstawy języka C się kłaniają. Jakie argumenty ma mieć Twoja funkcja? Jaką wartość ma zwracać? Moim zdaniem odpowie brzmi: żadne, żadną.


    Jeszcze się uczę :)

    Czyli o ile nie mam wyraźnie funkcji typu z=mojafunkcja(x,y), a zmieniam wartości będące gdzieś w kodzie, to mogę operować VOID-em, czy dobrze zrozumiałem?

    0
  • #19 22 Paź 2017 18:01
    22053
    Użytkownik usunął konto  
  • #20 22 Paź 2017 19:01
    grko
    Poziom 33  

    R-MIK napisał:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    To bedzie ona zwracała int a przymowała każdy typ danych (char, int, struktura wskaźnik, idp).


    No nie za bardzo. To co zaproponowałeś to nawet się nie kompiluje. Nie czepiałbym się jakby to była literówka. Jednak z kontekstu wypowiedzi wynika, że kolega naprawdę uważa to za poprawną konstrukcję. Jak na eksperta od kompilatorów to wygląda to bardzo słabo.

    0
  • #21 22 Paź 2017 19:09
    22053
    Użytkownik usunął konto  
  • #22 22 Paź 2017 21:28
    BlueDraco
    Specjalista - Mikrokontrolery

    Kłania się pojęcie funkcji w języku C. setup() i loop() to też są funkcje.

    Do testowania stanu styków potrzeba tylko jednego przerwania timera, nic więcej. Po co mnożyć przerwania, czy to od zmiany stanu linii, czy to od dodatkowych timerów?

    Przy jednopoziomowym systemie przerwań, takim, jaki ma ATmega, o ile programista nie udziwnia nic na siłę, zmienna używana tylko w procedurach obsługi przerwań nie musi mieć atrybutu volatile.

    0
  Szukaj w 5mln produktów