logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Zliczanie impulsów w przerwaniach - Arduino, błędne wartości impulsów

xyproxx 19 Paź 2017 22:31 2784 21
REKLAMA
  • #1 16767290
    xyproxx
    Poziom 11  
    Posty: 58
    Ocena: 3
    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 / C++
    Zaloguj się, aby zobaczyć kod



    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • REKLAMA
  • #2 16768438
    jaclew
    Poziom 18  
    Posty: 206
    Pomógł: 27
    Ocena: 210
    Zamieść schemat ponieważ aby odnieść się do kodu wypadałoby wiedzieć jak on wygląda.
    Szczególnie ciekawi mnie to:
    Kod: C / 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 / 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ś :-?
  • REKLAMA
  • #3 16768469
    xyproxx
    Poziom 11  
    Posty: 58
    Ocena: 3
    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.
  • REKLAMA
  • Pomocny post
    #4 16768626
    jaclew
    Poziom 18  
    Posty: 206
    Pomógł: 27
    Ocena: 210
    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 / C++
    Zaloguj się, aby zobaczyć kod

    albo krócej:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Drgania styków kontaktronu będą powodować chaotyczne naliczanie.
    Zrób debouncing programowo albo sprzętowo.
  • #5 16768902
    xyproxx
    Poziom 11  
    Posty: 58
    Ocena: 3
    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 / C++
    Zaloguj się, aby zobaczyć kod
  • #7 16770335
    xyproxx
    Poziom 11  
    Posty: 58
    Ocena: 3
    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?
  • #8 16770821
    BlueDraco
    Specjalista - Mikrokontrolery
    Posty: 6479
    Pomógł: 939
    Ocena: 421
    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.
  • REKLAMA
  • #9 16770861
    xyproxx
    Poziom 11  
    Posty: 58
    Ocena: 3
    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?
  • #10 16770950
    markolsrz
    Poziom 12  
    Posty: 40
    Pomógł: 3
    Ocena: 4
    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()
  • #11 16771151
    Mikroprocesorowiec
    Poziom 12  
    Posty: 71
    Ocena: 35
    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.
  • #12 16771279
    BlueDraco
    Specjalista - Mikrokontrolery
    Posty: 6479
    Pomógł: 939
    Ocena: 421
    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.
  • #13 16771291
    Mikroprocesorowiec
    Poziom 12  
    Posty: 71
    Ocena: 35
    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 / C++
    Zaloguj się, aby zobaczyć kod


    Oczywiscie jeszcze przerwanie od timera co 1ms gdzie:
    Kod: C / 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.
  • #14 16771612
    xyproxx
    Poziom 11  
    Posty: 58
    Ocena: 3
    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 / 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 / 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?
  • #15 16771625
    Mikroprocesorowiec
    Poziom 12  
    Posty: 71
    Ocena: 35
    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.
  • #16 16771674
    BlueDraco
    Specjalista - Mikrokontrolery
    Posty: 6479
    Pomógł: 939
    Ocena: 421
    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ą.
  • #17 16771742
    Mikroprocesorowiec
    Poziom 12  
    Posty: 71
    Ocena: 35
    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ę.
  • #18 16772204
    xyproxx
    Poziom 11  
    Posty: 58
    Ocena: 3
    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?
  • #19 16772340
    Konto nie istnieje
    Konto nie istnieje  
  • #20 16772511
    grko
    Poziom 33  
    Posty: 1386
    Pomógł: 247
    Ocena: 141
    R-MIK napisał:

    Kod: C / 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.
  • #21 16772534
    Konto nie istnieje
    Konto nie istnieje  
  • #22 16772881
    BlueDraco
    Specjalista - Mikrokontrolery
    Posty: 6479
    Pomógł: 939
    Ocena: 421
    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.

Podsumowanie tematu

LABEL_AI_GENERATED
Użytkownik napotkał problem z zliczaniem impulsów w projekcie Arduino, który wykorzystuje czujnik DHT-11 do pomiaru temperatury i wilgotności oraz kontaktron do zliczania impulsów. Zgłosił, że wartości impulsów są błędne, co może wynikać z drgań styków kontaktronu. Uczestnicy dyskusji zasugerowali zastosowanie debouncingu, zarówno programowego, jak i sprzętowego, oraz poprawne użycie przerwań. Wskazano, że funkcja obsługi przerwania nie powinna zwracać wartości ani przyjmować argumentów, a zmienne używane w przerwaniach powinny być oznaczone jako volatile. Użytkownik uzyskał pomoc w poprawie kodu, co pozwoliło na prawidłowe zliczanie impulsów.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA