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

[Atmega8][C] Sterowanie serwem - programowy PPM - obsługa przycisku

cassius17 18 Maj 2012 13:32 7016 34
REKLAMA
  • #1 10909921
    cassius17
    Poziom 17  
    Witam,

    Na wstępie napiszę, że jestem bardzo początkującym.

    Napisałem program do obsługi 5 serw modelarskich.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Eliminację drgań switch-a mam z tej strony:
    Link - Dzięki.

    Przerwanie działa dobrze, serwa się ustawiają. Nie potrafię jednak zmieniać zmiennej pwm1(przykładowo) aby sterować opóźnieniem(zmianą pozycji serwa).
    Przycisk podłączony pod PB1, serwo pod PB0. W tej chwili serwo ustawia się w pozycji 1.5ms(15*0.1), nie drga.

    Czy program jest źle napisany, jak napisać obsługę tego przycisku i zmianę wartości zmiennej pwmx?
  • REKLAMA
  • Pomocny post
    #2 10909983
    tmf
    VIP Zasłużony dla elektroda
    Program masz całkowicie źle napisany. Zacznij od sterowania serwami, ale tak, żeby nie używać delay. Czyli całe sterowanie musisz zrobić w przerwaniach. Najlepiej tak, że w procedurze obsługi przerwania inkrementujesz jakąś zmienną i porównujesz ją ze zmiennymi określającymi szerokość impulsu dla konkretnego serwa (skoro masz 10 to będziesz miał tablicę 10 elementową). Porównanie fałszywe - przechodzisz do kolejnego serwa, prawdziwe - wyłączasz impuls. Dzięki temu pomiędzy impulsami masz stałą szerokość wymaganą przez serwa. W twoim programie tak nie jest, w efekcie serwo nie jest sterowane co 20ms, lecz czas ten zależy od stanu innych serw. Jak już to zrobisz to wrócimy do przycisków :)
  • REKLAMA
  • #3 10910254
    cassius17
    Poziom 17  
    Inkrementację zmiennej i jej porównanie z inną - nie ma problemu. Ale nie potrafię sobie wyobrazić reszty.

    Załóżmy, że będę inkrementować zmienną w przerwaniu o 0.1, w pętli while(1) porównuję tę zmienną z długością impulsu(np. 1.5). Co będzie mi odmierzać okres 20ms? Z jaką częstotliwością musiałbym zrobić przerwanie?
  • #4 10910375
    tmf
    VIP Zasłużony dla elektroda
    Zanim przejdziemy dalej ustalmy kilka szczegółów. Jaką rozdzielczość serwa chcesz uzyskać? Od tego zależy sposób realizacji. Druga sprawa - z jaką częstotliwością taktujesz M8? Może być 16 MHz? I ostatnia - M8 to wybór ostateczny, czy możesz go zmienić? Najłatwiej będzie wykorzystać procesor, który ma 10 kanałów PWM. Jeśli mógłbyś zastosować XMEGA, np. XMEGA16/32 to byłoby super, bo całość to byłoby dosłownie kilka linii kodu. Jeśli musi być M8, to popracujemy nad implementacją softwarową, jednak musisz się liczyć z tym, że 10 serw trochę obciąży procesor, a i super rozdzielczości nie uzyskasz.
  • #5 10910498
    cassius17
    Poziom 17  
    Jest 5 serw.
    M8 mam i wolałbym uniknąć kupna innego. Taktowana wewnętrznym oscylatorem 8MHz.
    Nie jest potrzebna super wysoka rozdzielczość. Chcę wykorzystać to do prostego symulowania ruchów palców: Link
  • #6 10910579
    tmf
    VIP Zasłużony dla elektroda
    Ok. Zrób tak. Jeden timer przepełnia się co 20ms - generuje odstępy pomiędzy impulsami sterującymi serwem. Po każdym przepełnieniu odpalasz drugi timer który przepełnia się co czas równy rozdzielczości sterowania serwem i wysterowujesz wszystkie linie sterujące serwami na 1. W timerzeod rozdzielczości inkrementujesz jakąś zmienną pomocniczą, np. licznik. W funkcji obsługi przerwania przepełnienia tego timera porównujesz licznik z wartością odpowiadającą wybranemu serwomechanizmowi. Jeśli licznik<sierwo[n] to nic nie robisz, jeśli >= to zerujesz odpowiedni pin. Jeśli licznik>od wartość maksymalna to blokujesz timer. Ponownie zostanie odblokowany w przerwaniu timera odmierzającego odstępy. I to tyle. Pytaj śmiało jeśli czegoś nie rozumiesz.

    Dodano po 1 [minuty]:

    Aha, dla formalności jest jeszcze inne rozwiązanie - wykorzystujące zewnętrzny licznik do generowania ppm, ale to rozwiązanie częściowo hardwarowe, wymagane jeśli ma być duża rozdzielczość i brak jitteru.
  • #7 10916550
    cassius17
    Poziom 17  
    Wymyśliłem takie coś:

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


    Serwo się ustawia, ale strasznie wibruje. Wibracje wysokiej częstotliwości. Gdzie jeszcze mam błędy?
  • #8 10916577
    dondu
    Moderator na urlopie...
    j222 napisał:
    Serwo się ustawia, ale strasznie wibruje. Wibracje wysokiej częstotliwości. Gdzie jeszcze mam błędy?

    Masz jakiś oscyloskop lub analizator stanów logicznych, by zobaczyć przebiegi?
  • REKLAMA
  • #9 10916594
    cassius17
    Poziom 17  
    Właśnie szukam elementów, żeby zrobić sondę do karty dźwiękowej.
  • #11 10916656
    cassius17
    Poziom 17  
    Niestety nie - laptop.

    Edit:
    Taki mam przebieg:

    [Atmega8][C] Sterowanie serwem - programowy PPM - obsługa przycisku


    Jeżeli dobrze odczytuję(5ms/div) to okres wynosi ok. 25ms a czas stanu wysokiego to ok. 1,5ms i tak mam ustawione w programie.

    Serwo ustawia się w pozycji maksymalnej i chce dalej się obracać, co skutkuje jego zablokowaniem.

    Aktualnie program wygląda tak:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Nie mam pojęcia w czym rzecz. Wydaje mi się, że timer2 jest dobrze dobrany dla 50Hz.
  • Pomocny post
    #12 10916891
    janbernat
    Poziom 38  
    Na razie masz impulsy o długości 920us co 2.46ms.
    A powinny być 1-2ms co 20ms.
    Poza tym te impulsy są niestabilne.
    Znierzone oscyloskopem- widzisz jakie długie mam sondy...

    A teraz masz 142us co 2.64ms.
    Dalej źle.

    No tak-oba timery ustawiają PORTB.0.
  • #13 10916927
    cassius17
    Poziom 17  
    janbernat napisał:


    A teraz masz 142us co 2.64ms.
    Dalej źle.

    No tak-oba timery ustawiają PORTB.0.


    Symulujesz program?

    Starałem się to napisać wg instrukcji tmf'a.

    W obsłudze przerwania timera1 mam usunąć w instrukcji if część z else?
  • #14 10916938
    janbernat
    Poziom 38  
    Nie symuluję- wgrałem i mierzę oscyloskopem.
    Błąd w rozumowaniu masz chyba taki- jeden timer ma odmierzać 20ms.
    Wtedy uruchamia się drugi timer odmierzający czas impulsu.
    Ten pierwszy nie może nic ustawiać na wyjściu- ma tylko uruchomić drugi timer na określony czas.
  • #15 10916982
    cassius17
    Poziom 17  
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    No to musiałoby to być tak jak powyżej.

    Mógłbyś spojrzeć na to w jaki sposób mam ustawione timery? Czy dobrze policzyłem: Timer2 - 50Hz, timer1 : 100kHz.
  • #16 10917056
    janbernat
    Poziom 38  
    Na pewno masz źle ustawiony timer2- powinien mieć prascaler 1024 i OCR 256-99.
    Da to ok 20ms.
    Impuls z timer1 ma 146us- za mało.
    Na resztę jeszcze patrzę- nie podoba mi się zezwolenie na przerwanie od timera z przerwania od innego timera- ale nie wiem- tylko mi się nie podoba.
  • #17 10917106
    cassius17
    Poziom 17  
    W tej chwili mam to tak. Przebieg przypomina sygnał prostokątny.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #18 10917135
    janbernat
    Poziom 38  
    Sprawdzam:
    Impulsy są co 23.8ms- może być.
    Długość impulsów -15.8ms- nie do przyjęcia.
    Szybko zmieniasz kod a ja jestem stary/początkujący i nie nadążam z analizą.
  • #19 10917188
    sulfur
    Poziom 24  
    janbernat jakiś czas temu popełnił bardzo dobry kod programowego PWMa na podstawie jakiejś tam noty atmela. Może spróbuj skorzystać z tego Link.
  • #20 10917201
    cassius17
    Poziom 17  
    Widziałem program, lecz staram się sam coś wymyślić. Wiem, nie powinno się wyważać otwartych drzwi, ale trzeba się też uczyć.

    Teraz już tylko dobrać się do timera1 odpowiedzialnego za długość impulsów i powinno być dobrze.
  • #21 10917288
    sulfur
    Poziom 24  
    Rozumiem. Liczysz więc na uwagi, niż na efekt końcowy.

    Wklejam linijki kodu, które są źle.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

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

    Dodatkowo po zatrzymaniu timera nie zerujesz/ustawiasz rejestru jego licznika. Musisz się wtedy liczyć z pewną (małą) - ale w przyszłości pewnie zmienną - dokładnością.

    Timer 1 jest w trybie CTC oraz korzystasz z przerwania od porównania B, ale nigdzie nie modyfikujesz tej wartości. Pomyśl, co to oznacza.
  • #22 10917546
    cassius17
    Poziom 17  
    sulfur napisał:

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

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




    Błędy dotyczą składni, czy uruchomienia i zatrzymania timera(wpisów do rejestru)? Jeżeli dobrze ustawiłem, to timer1 pracuje w trybie CTC z preskalerem równym 1, czyli błąd będzie dotyczył jego uruchomienia, ale nie mam pojęcia jaki to może być. Ciężko się zastanawiałem w jaki sposób uruchomić timer przerwaniem lub jakąś zmienną, ale do niczego innego nie doszedłem.


    Cytat:
    Dodatkowo po zatrzymaniu timera nie zerujesz/ustawiasz rejestru jego licznika. Musisz się wtedy liczyć z pewną (małą) - ale w przyszłości pewnie zmienną - dokładnością.

    Timer 1 jest w trybie CTC oraz korzystasz z przerwania od porównania B, ale nigdzie nie modyfikujesz tej wartości. Pomyśl, co to oznacza.

    Czyli muszę wpisać wartość do rejestru OCR1B. Po zatrzymaniu powinienem ustawić wszystkie wartości rejestru licznika na 0?
  • #23 10917707
    sulfur
    Poziom 24  
    j222 napisał:
    Błędy dotyczą składni, czy uruchomienia i zatrzymania timera(wpisów do rejestru)?
    Błąd składni to nie jest, bo nie skompilował by Ci się kod. To są błędy logiczne.
    Pierwszy dotyczy braku nawiasów klamrowych. Przy takim zapisie (statystycznie rzecz ujmując) zgubisz od czasu do czasu fakt ustawienia flagi na jeden.
    Drugi dotyczy braku nawiasów () w połączeniu z priorytetami operatorów. W tym wypadku oczywiście kompletnie się nic nie zmieni, ale jest to przypadek. Jeśli o tym wiesz, to musisz to zaznaczyć w komentarzu, że taka sytuacja jest pożądana.
    j222 napisał:
    Czyli muszę wpisać wartość do rejestru OCR1B. Po zatrzymaniu powinienem ustawić wszystkie wartości rejestru licznika na 0?
    No to już zależy od Ciebie. Napisz, jak według Ciebie powinno to wyglądać teoretycznie, to będę mógł się do tego odnieść - czyli co chcesz zrobić, a co robisz na prawdę. Zauważ, że teraz generujesz PWM na jednym pinie, a docelowo chcesz chyba 5. Jak to sobie wyobrażasz z takim kodem ?
  • REKLAMA
  • #24 10917760
    cassius17
    Poziom 17  
    sulfur napisał:

    j222 napisał:
    Czyli muszę wpisać wartość do rejestru OCR1B. Po zatrzymaniu powinienem ustawić wszystkie wartości rejestru licznika na 0?
    No to już zależy od Ciebie. Napisz, jak według Ciebie powinno to wyglądać teoretycznie, to będę mógł się do tego odnieść - czyli co chcesz zrobić, a co robisz na prawdę. Zauważ, że teraz generujesz PWM na jednym pinie, a docelowo chcesz chyba 5. Jak to sobie wyobrażasz z takim kodem ?


    Narazie nie wrzucę żadnego nowego kodu, ale generację PPM na 5 pinach miałem zamiar wykonać w sposób podany prze tmf'a, tzn. timer1 przy każdym przerwaniu inkrementuje zmienną(czas), z którą porównuję zmienne dla każdego serwa(zmienna pwm). Przypadku czas>=pwm zmieniam stan na niski, a w przypadku czas<pwm nie robię nic tzn. pin ma stan wysoki.
  • #25 10917792
    sulfur
    Poziom 24  
    No właśnie. Wynika z tego, że przy preskalerze 1 będziesz takie sprawdzenie musiał wykonać co takt zegara. Samo sprawdzenie trwa wielokrotnie dłużej.
  • #26 10917811
    cassius17
    Poziom 17  
    Chciałem ustawić tak, aby przerwanie z timera1 było co 0,1ms. Chyba tu jest błąd. Wartość rejestru OCR1B powinna wynosić 399.

    Edit:

    Aktualny kod. Przebieg prostokątny. Serwo ustawia się w zadanej pozycji, choć nie tak jak sobie to liczyłem, tzn. zmienna pwm=15*0,1ms=1,5ms.

    Po wpisaniu wartości do rejestru OCR1B nic się nie dzieje. Wg noty katalogowej dla trybu CTC używa się rejestru OCR1A.

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #27 10920880
    janbernat
    Poziom 38  
    W tej chwili mamy tak- długość impulsu 1.150ms.
    Czyli prawie na max w lewo.
    Okres impulsu- 19.3 ms- czyli prawie dobrze.
    Okres impulsu- lepiej raczej nie będzie.
    Zastanawiam się jak ustawiać timer1 aby po 20ms wysłał 5 impulsów o różnej długości dp pięciu serw.
    I po następnych 20ms- następny zestaw.
  • #28 10921066
    cassius17
    Poziom 17  
    Chyba było ustawione max w lewo - muszę poekperymentować z minimalnymi/maksymalnymi nastawami zmiennych.

    janbernat napisał:
    Zastanawiam się jak ustawiać timer1 aby po 20ms wysłał 5 impulsów o różnej długości dp pięciu serw.
    I po następnych 20ms- następny zestaw.



    Już wyjaśniam. Poniżej jest kawałek kodu za to odpowiedzialny. Przy każdym przerwaniu timera1(0,1ms) sprawdzam warunek czy inkrementowana zmienna cnt>= pwm[i](każde z pięciu serw ma swoją zmienną). Jeżeli warunek jest spełniony to na wyjście zostaje ustawiony stan niski. Jeżeli nie jest spełniony na wyjściu jest stan wysoki. Sprawdzamy tak każde serwo.
    Na koniec, jeżeli zmienna cnt>45(muszę dobrać) - blokuję timer i zeruję zmienną cnt.
    Timer1 jest ponownie włączany przy przerwaniu z timera2(co 20ms).

    Teoretycznie mnożąc zmienną razy ilość przerwań mamy długość impulsu, ale zauważyłem już, że muszę wpisać do zmiennej więcej niż np. 10 co równało by się 1ms. Zapewne jest to związane z samą obsługą przerwania i sprawdzania warunku.

    Mam nadzieję, że zrozumiesz co mam na myśli.

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #29 10921245
    janbernat
    Poziom 38  
    No właśnie- o wyższszości Świąt Boźego Narodzenia nad Świętami Wielkiej Nocy.
    Co ok. 20ms musi wystąpić przerwanie od timer8bit.
    To daje nam ok 20ms.
    To przerwanie powinno nam wystartować timer16bit.
    Timer16bit powinien nam ustawić 16 wyjść w stan 1.
    Potem powinien mieć 16 przerwań sprzętowych- tyle nie ma- albo programowych aby ustawić 16 serw w stan 0.
    Każde o innej długości.
    Dlaczego 16- bo łatwiej liczyć.
    To się chyba da zrobić w głównej pętli- porównania.
    Dla ośmiu serw wystarczy ten program który znasz.
    Bo wykorzystuje tylko jeden timer.
    Ale nie bardzo mi się chce sprawdzać dla 16 serw- drogo.
    Wystarczyło mi kupić 8 serw aby sprawdzić.
  • #30 10921344
    sulfur
    Poziom 24  
    Pod względem szybkości działania kod, którego autorem jest janbernat na podstawie noty atmela uważam za optymalny przy obsłudze sprzętowego wielokanałowego PWM. Ale skoro chcesz mieć swoją inną wersję, to moim zdaniem przy twoich założeniach uzasadnione jest stworzenie struktury, w której będziesz trzymał numer kanału oraz jego wypełnienie. Posortowanie tej tablicy pod kątem wypełnienia pozwoli uniknąć Ci wielokrotnego sprawdzania całej tablicy. Jest to najrozsądniejsze rozwiązanie, które przychodzi mi na myśl.
REKLAMA