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

Jak skonfigurować przerwania zewnętrzne do sterowania silnikiem w AVR?

Jakub17 20 Gru 2016 20:36 1245 27
REKLAMA
  • #1 16139837
    Jakub17
    Poziom 6  
    Witam.

    Po raz kolejny wracam do tematu przerwań:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Gdy naciskam przycisk i trzymam przyciśnięty, w przerwaniu najpierw zwiększa się zmienna k i wypełnienie sygnału rośnie, silnik przyspiesza. Gdy puszczę przycisk wypełnienie zostaje zapamiętane i silnik kręci się ze stałą prędkością. Gdy dochodzę do maksymalnego wypełnienia k=255 i chcę by w następnej kolejności silnik powoli zwalniał (ciągle trzymam przycisk) to on kręci się cały czas na maksymalnych obrotach. Gdzie popełniam błąd w kodzie?
    Oczywiście zaraz dostanę pewnie reprymendę że używam delay w przerwaniu ale przy narastaniu prędkości wszystko działa, to dlaczego miałoby nie działać przy jej zmniejszaniu?
  • REKLAMA
  • #2 16139865
    Konto nie istnieje
    Konto nie istnieje  
  • #3 16139882
    jaccuzi2
    Poziom 12  
    Aby wejść do drugiego warunku i musi przyjąć wartość co najmniej 256, co jest nie możliwe, bo przy i == 255 program nie wejdzie do pierwszego warunku w którym jest inkrementacja. W drugim warunku z kolei nie osiągnie wartości potrzebnej do wyzerowania i. Czemu nie nie zastosujesz if()...else?
  • #4 16140178
    dondu
    Moderator na urlopie...
    Styki drgają. Skoro używasz przerwania INTx do obsługi przycisku z drgającymi stykami, to przy pierwszym wykrytym zboczu (przy naciskaniu) w funkcji przerwania go obsługującej musisz wyłączyć to przerwanie, a włączyć je po jakimś czasie np. 20ms. Od tego zacznij.

    Pytanie: Jaki masz powód, by przyciski obsługiwać przerwaniem?
  • #5 16140471
    Jakub17
    Poziom 6  
    jaccuzi2 napisał:
    Aby wejść do drugiego warunku i musi przyjąć wartość co najmniej 256, co jest nie możliwe, bo przy i == 255 program nie wejdzie do pierwszego warunku w którym jest inkrementacja. W drugim warunku z kolei nie osiągnie wartości potrzebnej do wyzerowania i. Czemu nie nie zastosujesz if()...else?


    Faktycznie, niedopatrzenie. Poprawiłem błędy ale i tak działa nie do końca. Kiedy licznik wejdzie w zakres i>255 && i<=512 (co sygnalizuje zapalenie drugiej diody:PORTA |= (1<<PA5);) to zamiast sukcesywnie zmniejszać wartość z k=255 poprzez k-- to silnik kręci się na maks obrotach jak przy k=255 a dopiero gdy k==512 to zmienna i jest zerowana i zaczyna się od początku narastanie prędkości.

    Dodano po 6 [minuty]:

    dondu napisał:
    Styki drgają. Skoro używasz przerwania INTx do obsługi przycisku z drgającymi stykami, to przy pierwszym wykrytym zboczu (przy naciskaniu) w funkcji przerwania go obsługującej musisz wyłączyć to przerwanie, a włączyć je po jakimś czasie np. 20ms. Od tego zacznij.

    Pytanie: Jaki masz powód, by przyciski obsługiwać przerwaniem?


    Tak wiem. Chociaż podciągnałem pin PD2 (INT0) do Vcc i zastosowałem równoległy kondensator co chociaż w części zniweluje drgania. Przerwanie jest wymuszane stanem niskim, nie zboczem. Teoretycznie wpadłem jeszcze na pomysł by wykorzystać coś takiego że timer zlicza i w trybie compare match rozpoczyna się przerwanie w który jest sprawdzanie czy przycisk jest na stanie wysokim czy na zwarciu do masy, jeżeli jest to po powrocie do pętli głownej ma się wykonać konkretne zadanie. Być może poprawie to jak zasadnicze założenia programu zostaną spełnione. W skali makro dla mojego oka wszystko działa okej na razie. Nawet jeżeli występują jakieś niekontrolowane przerwania, to w tak prostym programie (który de facto dla mnie nie jest taki prosty jeszcze jak widać) nie uważam za potrzebne za bardzo się na tym roztkliwiać.

    Dodano po 16 [minuty]:

    dondu napisał:
    [..]to przy pierwszym wykrytym zboczu (przy naciskaniu) w funkcji przerwania go obsługującej musisz wyłączyć to przerwanie, a włączyć je po jakimś czasie np. 20ms. Od tego zacznij.



    Mam zrobić coś takiego?
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Czytałem gdzieś że ogólnie należałoby się wystrzegać praktyk włączania/wyłączania przerwań w ISR ale może mam za mało doświadczenia jeszcze. Jeżeli tak to ma wyglądać, to wprowadzę to.

    Dodano po 7 [minuty]:

    Piotrus_999 napisał:

    2. Skoro wiesz zeby ne stosować delay to po co to robisz?


    Gdybym nie zastosował delay no to silnik za szybko by przyspieszał i zwalniał. Jak inaczej można to zrobić?
  • Pomocny post
    #6 16140508
    dondu
    Moderator na urlopie...
    Przecież wiesz, że delay w przerwaniu nie powinieneś stosować to dlaczego ponownie je tam wstawiasz?
    Przyjmij wreszcie postawę, że delay jest be i przestań go używać, oraz że do odliczania odcinków czasowych należy wykorzystywać timery.

    Patrząc na to co zaproponowałeś powyżej nie wiesz natomiast, że:
    - wchodząc w przerwanie przerwania są blokowane (flaga globalne), stąd nie musisz ich blokować na początku funkcji przerwania za pomocą cli(),
    - wychodząc z funkcji przerwania przerwania globalne są automatycznie odblokowywane, nie musisz więc ich odblokowywać. za pomocą sei().

    Jak już pisałem, w przerwaniu masz zablokować:

    dondu napisał:
    Styki drgają. Skoro używasz przerwania INTx do obsługi przycisku z drgającymi stykami, to przy pierwszym wykrytym zboczu (przy naciskaniu) w funkcji przerwania go obsługującej musisz wyłączyć to przerwanie, a włączyć je po jakimś czasie np. 20ms.


    Innymi słowy masz wykonać:


    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    a opóźnienie odlicz w inny sposób np. timerem - można to zrobić na 1001 sposobów.

    Możesz na przykład w kolejnej linii przerwania INT0 włączyć preskaler timera0 by zaczął liczyć, a przerwanie przepełnienia timera0 ma:
    - wyłączyć preskaler timera0,
    - wyzerować timer0 (TCNT0),
    - wyzerować preskaler timer0,
    - ponownie włączyć przerwania INT0.

    ... i tyle.
  • REKLAMA
  • #7 16140599
    Jakub17
    Poziom 6  
    Wielkie dzięki za podpowiedzi:) Dziś jak wrócę do domu zobaczę co uda mi się zdziałać.

    Chociaż nadal pozostaje nierozwiązana ta kwestia o której pisałem wcześniej:

    Kiedy licznik wejdzie w zakres i>255 && i<=512 (co sygnalizuje zapalenie drugiej diody:PORTA |= (1<<PA5);) to zamiast sukcesywnie zmniejszać wartość z k=255 poprzez k-- to silnik kręci się na maks obrotach jak przy k=255 a dopiero gdy k==512 to zmienna "i" jest zerowana i zaczyna się od początku narastanie prędkości.
  • Pomocny post
    #8 16141237
    jaccuzi2
    Poziom 12  
    Kolejne "niedopatrzenie". W drugim warunku do OCR0 wpisujesz zawsze taką samą wartość. Przeanalizuj, co się w tym warunku dzieje ze zmienną k. Jeżeli coś nie działa, sprawdzaj linia po linii, przynajmniej ze trzy razy (jeżeli za pierwszym razem nie znajdziesz), a z pewnością wyeliminujesz "niedopatrzenia".
  • #9 16141844
    Jakub17
    Poziom 6  
    dondu napisał:


    Możesz na przykład w kolejnej linii przerwania INT0 włączyć preskaler timera0 by zaczął liczyć, a przerwanie przepełnienia timera0 ma:
    - wyłączyć preskaler timera0,
    - wyzerować timer0 (TCNT0),
    - wyzerować preskaler timer0,
    - ponownie włączyć przerwania INT0.

    ... i tyle.


    Czyli, z tego co zrozumiałem, przerwanie ma się pojawić w przerwaniu?
  • REKLAMA
  • #11 16142040
    Jakub17
    Poziom 6  
    A da się na tej samej zasadzie wprowadzić opóźnienia za pomocą timera żeby prędkość powoli narastała i opadała? (to co powodował delay w każdej z pętli if w przerwaniu)
  • #12 16142258
    kamyczek
    Poziom 38  
    Jakub17 napisał:
    Czyli, z tego co zrozumiałem, przerwanie ma się pojawić w przerwaniu?


    Kolego styki drgają i zobaczysz to na oscyloskopie , efektem jest wielokrotne wyzwolenie przerwania od przycisku przy jednokrotnym jego naciśnięciu aby tego uniknąć stosuje się różne zabiegi np. sprawdzenie czy przycisk został puszczony po określonym czasie ale to zajmuje czas i nie może być zrealizowane w przerwaniu int0 bo w tym czasie nie obsłużysz przerwań np. od tim0 i pwm0A .Gdybyś chciał to zrobić w taki sposób trzeba to zrealizować inaczej i w przerwaniu jedynie ustawiać flagę naciśnięcia przycisku a obsługiwać ją w programie głównym co określony czas . W takim rozwiązaniu po prostu przerwanie ustawi flagę i bez względu na ilość wystąpień nie zmieni nic do czasu skasowania jej przez program główny obsługujący zliczanie przyciśnięć przycisku i kasujący jej stan .
  • #13 16142453
    dondu
    Moderator na urlopie...
    kamyczek napisał:
    ... sprawdzenie czy przycisk został puszczony po określonym czasie ale to zajmuje czas i nie może być zrealizowane w przerwaniu int0 ...

    Ależ oczywiście, że można. To kwestia podejścia do problemu i odpowiedniego napisania programu w zależności od konkretnego projektu.

    kamyczek napisał:
    Gdybyś chciał to zrobić w taki sposób trzeba to zrealizować inaczej i w przerwaniu jedynie ustawiać flagę naciśnięcia przycisku a obsługiwać ją w programie głównym co określony czas . W takim rozwiązaniu po prostu przerwanie ustawi flagę i bez względu na ilość wystąpień nie zmieni nic do czasu skasowania jej przez program główny obsługujący zliczanie przyciśnięć przycisku i kasujący jej stan .

    To nie załatwia problemu, ponieważ między pierwszym i ostatnim drganiem przyciskanego przycisku flaga może zostać obsłużona przez program główny.

    Jakub17 napisał:
    A da się na tej samej zasadzie wprowadzić opóźnienia za pomocą timera żeby prędkość powoli narastała i opadała? (to co powodował delay w każdej z pętli if w przerwaniu)

    Tak, to właściwa droga. :)
    Do przerwania przepełnienia timera możesz dodać zmienną (zmienne) które będą odliczać jakieś inne odcinki czasowe.

    Innymi słowy za pomocą jednego timera można w ten sposób obsługiwać wiele procesów uzależnionych od upływu jakichś konkretnych odcinków czasu.


    http://mikrokontrolery.blogspot.com/2011/04/przycisk-drgania-stykow-debouncing.html



  • #14 16142746
    Jakub17
    Poziom 6  
    @dondu spróbowałem zrobić tak jak mówiłeś ale wyszło z miernym skutkiem...

    To znaczy do końca nie rozumiem jak to przebiega wszystko...
    Na filmiku pokazuje jak to działa.

    Co jest źle i czego nie rozumiem:

    --Silnik po puszczeniu przycisku cały czas kręci się na max obrotach.

    --Dioda PA0 w trakcie przerwania TIMER0 powinna mrugać i mruga ale ledwo zauważalnie w każdym razie w innym tempie niż dioda PA6 co się zgadza. Dioda PA5 w ogole się nie pali - program tam nie dochodzi.

    --Silnik nie zwalnia tzn. tak jak wyżej, program tam nie dochodzi bo i dioda syganlizująca PA5 tez nie mryga - tak mi się wydaje przynajmniej.

    --Dlaczego silnik ma taką szarpaną prace? Tak jakby w momencie gdy licznik przestaje liczyć i następuje powrót do przerwania INT0 od razu została załadowana wartość i=255

    Narazie dodałem licznik tylko w obszarze pierwszego warunku if.




    Tutaj rozrysowałem sobie algorytm jak działają te przerwania. Czy to co napisałem w kodzie jest adekwatne do mojego wyobrażenia o tym?
    Jak skonfigurować przerwania zewnętrzne do sterowania silnikiem w AVR?

    No i filmik:

    https://www.youtube.com/watch?v=QTtnqu1QoLo
    Reset tutaj cos nawala ale jest to tylko widoczne przy tym programie. Normalnie zawsze dziala za pierwszym razem
  • Pomocny post
    #15 16142870
    dondu
    Moderator na urlopie...
    Generalnie na pierwszy rzut oka program jest prawidłowy.

    Nie zauważyłem jednak wcześniej, że używasz timera0 do generowania PWM, więc nie możesz go zatrzymywać tak jak sugerowałem.
    Niewskazane jest także resetowanie preskalera, choć dla silnika to nie ma znaczenia.
    Jeśli masz wolny timer 1 lub timer2 możesz to zrobić na nim według wcześniej przedstawionego algorytmu.

    Możesz także skorzystać z timer0 i przerwań od przepełnień, ale nie wyłączać timera0, czyli nie zerować bitów preskalera. Tutaj będzie potrzebna niewielka zmiana algorytmu.
  • #16 16142880
    Konto nie istnieje
    Konto nie istnieje  
  • #17 16142893
    dondu
    Moderator na urlopie...
    Piotrus_999 napisał:
    dondu napisał:
    Możesz także skorzystać z timer0 i przerwań od przepełnień, ale nie wyłączać timera0, czyli nie zerować bitów preskalera
    I w tym przerwaniu sprawdzać i debouncowac klawisz a o int0 zapomniec

    Oczywiście, ale autor chce na INT0, więc niech się nauczy.
  • #18 16142947
    Konto nie istnieje
    Konto nie istnieje  
  • #20 16142962
    dondu
    Moderator na urlopie...
    Piotrus_999 napisał:
    dondu napisał:
    Oczywiście, ale autor chce na INT0, więc niech się nauczy.
    Nauczy się złych nawyków. INT się nie nadaje (bez specjalnych cyrków i wygibasów programistycznych) do rzeczy które drgaja.
    I kolega jako początkujący powinien (jeżeli nie potrafi tego zrozumieć) przyjąć na wiarę.

    Po raz n-ty powtarzam i powtarzać będę, że:
    - każde rozwiązanie jest dobre jeśli działa tak jak zaplanował projektant,
    - są sytuacje, w których przycisk musi być podłączony pod INTx np. wybudzenie z niektórych trybów,
    - każda nabyta umiejętność się przydaje w późniejszych projektach i dlatego warto nauczyć się także tego.

    A to, że w 99% przypadków deboucing nie realizuje się na przerwaniach INTx, czy Pin Change, to oczywiście prawda.
    Dlatego teraz w ramach ćwiczeń powinien zrobić deboucing w oparciu o wykorzystany do PWM timer0 bez przerwań INTx i timera2.
  • REKLAMA
  • #21 16142978
    Jakub17
    Poziom 6  
    Tylko jest jedna rzecz nie do końca fajnie...
    Trzymam cały czas przycisk. Silnik rozpędza się a następnie zwalnia. Gdy zwolni już na tyle że znowu zacznie się pierwszy warunek if(i<=255) to silnik dostaje takiego kopa z prędkością na chwile i a później znowu zaczyna wszystko fajnie działać. Problem w tym że jak tak trzymać przycisk przez cały czas przez kilka cykli przyspieszania i zwalniania to z każdym nowym cyklem ten "kop" dla silnika staje się coraz większy co później całkowicie zaburza rozpędzanie i zwalnianie.

    Zaraz wrzucę filmik

    Z czego to wynika i jak to wyeliminować? Czy to efekt tego że przerwania trwają za długo i zaczynają na siebie nachodzić?

    Program:
  • #22 16142992
    Konto nie istnieje
    Konto nie istnieje  
  • #23 16142997
    dondu
    Moderator na urlopie...
    Piotrus_999 napisał:
    dondu napisał:
    - są sytuacje, w których przycisk musi być podłączony pod INTx np. wybudzenie z niektórych trybów,
    Myślę że to jeszcze dalece nie ten etap. A jak ten czas nadejdzie to będzie wiedział o co chodzi.

    Ja uważam, że nie ma utartej ścieżki poznawania mikrokontrolera dlatego zawsze sugeruję, by zaczynając określili sobie od razu jakiś projekt, który chcą wykonać i zależnie od niego uczyli się tego co w danym momencie im potrzeba. Inaczej bowiem, trzeba spędzić wiele miesięcy na wkuwaniu C oraz poznawaniu mikrokontrolera, zanim zrobi się coś konkretnego.
  • #24 16143003
    Konto nie istnieje
    Konto nie istnieje  
  • #25 16143024
    dondu
    Moderator na urlopie...
    Zasugerowałem co powinien zrobić:
    dondu napisał:
    Dlatego teraz w ramach ćwiczeń powinien zrobić deboucing w oparciu o wykorzystany do PWM timer0 bez przerwań INTx i timera2.


    ale z tym się nie zgadzam i nigdy nie zgodzę:

    Piotrus_999 napisał:
    Należy na początku uczyć metod poprawnych...

    bowiem jak już napisałem wcześniej każda metoda jest poprawna jeśli działa tak jak założył projektant.

    Kończymy off-top.
  • #26 16143026
    Jakub17
    Poziom 6  
    A co z tym problemem który opisałem wyżej? Na końcu jest drugiego warunku if(i==512) i=0; co powoduje przejście do pierwszego warunku. Nie wiem skąd bierze się ten nagły skok prędkości który urasta z każdą iteracją rozpędź-zwolnij do ogromnych rozmiarów tzn. czas jaki temu towarzyszy. Wydaje mi się że to przez to że coś jest nie do końca z czasami tych przerwań? Czy to jest przyczyna? Czy muszę jakoś skrócić kod w przerwaniu czy są inne metody?
  • #27 16143035
    dondu
    Moderator na urlopie...
    Zacznijmy od tego, że w dwóch miejscach ustawiasz OCR0 - zdecyduj się na jedno.

    Poza tym, jak ma działać Twój przycisk?
    Ma zmieniać o określoną ilość prędkość silnika, za każdym przyciśnięciem, czy także gdy go przytrzymujesz?
  • #28 16143041
    Jakub17
    Poziom 6  
    dondu napisał:
    Zacznijmy od tego, że w dwóch miejscach ustawiasz OCR0 - zdecyduj się na jedno.

    Poza tym, jak ma działać Twój przycisk?
    Ma zmieniać o określoną ilość prędkość silnika, za każdym przyciśnięciem, czy także gdy go przytrzymujesz?


    Gdy przyciskam prędkość ma narastać (do pewnego momentu określonego w programie) a następnie zwalniać. Gdy przycisk jest puszczony silnik utrzymuje poprzednio zapamiętaną wartość prędkości (gdy przycisk był wciśnięty)

    Dodano po 3 [minuty]:

    dondu napisał:
    Zacznijmy od tego, że w dwóch miejscach ustawiasz OCR0 - zdecyduj się na jedno.


    No tak w dwóch miejscach. W przerwaniu do narastania i zmniejszania prędkośći, i w pętli głownej while do utrzymywania "ostatniej" wartości prędkości tuż przed puszczeniem przycisku, gdy następuje powrót programu do pętli głownej.


    A o to jak wygląda problem:
    https://www.youtube.com/watch?v=te9TawAdtV0

    A tak to wygląda po kilku takich iteracjach "rozpędź-zwolnij" przy nieustannym trzymaniu przycisku (wywołaniu przerwań INT0). Na filmiku zwarłem przewody zamiast ciagle trzymać przycisk.

    https://www.youtube.com/watch?v=TDfhCK4xAUY

    Na filmiku cały czas trzymam przycisk więc program jest w przerwaniu. Napjpierw prędkosć narasta, później maleje następnie znowu ma narastać (przycisku nie puściłem) ale zanim zacznie się ponownie narastanie następuje ten niechciany chwilowy "kop" z prędkością dla silnika.
REKLAMA