Elektroda.pl
Elektroda.pl
X

Wyszukiwarki naszych partnerów

Wyszukaj w ofercie 200 tys. produktów TME
Europejski lider sprzedaży techniki i elektroniki.
Proszę, dodaj wyjątek elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

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

cassius17 18 Maj 2012 13:32 5816 34
  • #1 18 Maj 2012 13:32
    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
    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?

  • Pomocny post
    #2 18 Maj 2012 13:49
    tmf
    Moderator Mikrokontrolery Projektowanie

    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 :)

  • #3 18 Maj 2012 15:30
    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 18 Maj 2012 16:11
    tmf
    Moderator Mikrokontrolery Projektowanie

    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 18 Maj 2012 16:45
    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 18 Maj 2012 17:15
    tmf
    Moderator Mikrokontrolery Projektowanie

    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 20 Maj 2012 15:32
    cassius17
    Poziom 17  

    Wymyśliłem takie coś:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Serwo się ustawia, ale strasznie wibruje. Wibracje wysokiej częstotliwości. Gdzie jeszcze mam błędy?

  • #9 20 Maj 2012 15:48
    cassius17
    Poziom 17  

    Właśnie szukam elementów, żeby zrobić sondę do karty dźwiękowej.

  • #11 20 Maj 2012 16:10
    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
    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 20 Maj 2012 17:42
    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 20 Maj 2012 17:58
    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 20 Maj 2012 18:02
    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 20 Maj 2012 18:17
    cassius17
    Poziom 17  

    Kod: 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 20 Maj 2012 18:39
    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 20 Maj 2012 18:51
    cassius17
    Poziom 17  

    W tej chwili mam to tak. Przebieg przypomina sygnał prostokątny.

    Kod: c
    Zaloguj się, aby zobaczyć kod

  • #18 20 Maj 2012 18:59
    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 20 Maj 2012 19:13
    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 20 Maj 2012 19:18
    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 20 Maj 2012 19:39
    sulfur
    Poziom 24  

    Rozumiem. Liczysz więc na uwagi, niż na efekt końcowy.

    Wklejam linijki kodu, które są źle.

    Kod: c
    Zaloguj się, aby zobaczyć kod

    Kod: 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 20 Maj 2012 20:49
    cassius17
    Poziom 17  

    sulfur napisał:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    Kod: 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 20 Maj 2012 21:23
    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 ?

  • #24 20 Maj 2012 21:33
    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 20 Maj 2012 21:39
    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 20 Maj 2012 21:43
    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
    Zaloguj się, aby zobaczyć kod

  • #27 21 Maj 2012 20:24
    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 21 Maj 2012 21:07
    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
    Zaloguj się, aby zobaczyć kod

  • #29 21 Maj 2012 21:46
    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 21 Maj 2012 22:04
    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.

 Szukaj w ofercie
Zamknij 
Wyszukaj w ofercie 200 tys. produktów TME