fbpx

Serwomechanizmy modelarskie, czyli w skrócie serwa są bardzo ciekawym rodzajem urządzeń wykonawczych. Wykonuje on ruch w okół osi, podobnie jak zwykłe silniki, z tym że obrót nie jest ciągły. Mechanizm wykonuje go jedynie o “zadany kąt”. Inaczej mówiąc – w serwie ustawia się położenie, w jakim ma się znajdować. Do działania nie wystarczy jedynie napięcie. Serwo wymaga “specjalnego” sygnału.

Jak to ugryźć?

Czym jest ten tajemniczy sygnał? Otóż jest to nic innego jak sygnał PWM(Pulse Width Modulation). Układ sterujący w serwomechanizmie do poprawnego działania wymaga sygnału o częstotliwości 50 Hz, czyli o okresie 20 ms. Stan wysoki w owym sygnale powinien znajdować się w zakresie 1÷2 ms. Teoretycznie 1 ms odpowiada wychyleniu około -90°, 1,5 ms 0°, a 2 ms +90°. To w teorii. W praktyce okazuje się niestety różnie.

Tym razem na stół wziąłem serwo z rozmiaru mikro – Turnigy TG9e. Jest ono bardzo popularne, głównie ze względu na swoją niską cenę. Wiadomo – większe są droższe. Pacjent mój posiada malutkie, plastikowe zębatki, więc po tym już można wywnioskować, że raczej został stworzony do przenoszenia nie za wielkich sił. Specyfikacja podaje moment obrotowy na poziomie 1,5 kg/cm. Do poruszania cięgien w małym modelu RC wystarczy 🙂

Według specyfikacji, którą znalazłem w sieci, zasilanie powinno znajdować się w zakresie 4,8÷6 V. Zestawy Nucleo mają na szczęście piny zasilania 5 V. Co w takim razie z sygnałem? Specyfikacja niestety nie podaje, aby była możliwość sterowanie z 3,3 V 🙁 STM32 nie wysteruje z GPIO 5V, ale spróbować z 3,3 V zawsze warto 😉

Pierwszy tykacz

Do testów wziąłem tym razem Nucleo64 w wersji z L053R8. Schemat nie jest skomplikowany. Niestety serwa nie mają numerowanych pinów na swoim złączu. Należy przestrzegać kolorystyki. Czerwony jest zasilaniem, czarny, lub brązowy masą a pomarańczowy lub biały sygnałem sterującym. Zapewne są jeszcze inne kolory, ale zasada odcieni jest zawsze podobna. Ja akurat mam dwa modele serw czerwono-czarno-białe.

Połączenie jest. Teraz muszę skonfigurować Cube’a. W jaki sposób generować sygnał PWM? Można mordować się na GPIO i liczyć, kiedy należy zmianić stan pinu. Nie polecam tego sposobu i nawet nie próbuj tak działać! Do generacji sygnału PWM w głównej mierze używa się timerów. STM32 jak każdy szanujący się Pan mikrokontroler, ma takich timerów kilka. W przykładowym STM32L053R8 3 z 4 timerów posiada tryb PWM i łącznie posiadają 8 kanałów, których można użyć w tym celu. Ja obrałem sobie TIM2. Źródło taktowania timera wybrałem zewnętrzne, a na kanale pierwszym wybrałem generowanie sygnału PWM.

Należy jeszcze ustawić prescaler oraz ilość zliczanych impulsów. W moim przykładzie MCU jest taktowany 32 Mhz i taki też zegar dociera do timerów. Okres sygnału powinien wynosić 20 ms. Stąd chciałbym, aby licznik zliczał do wielokrotności tej liczby. 32 Mhz dzielone na 20 daje wynik 1,6 Mhz. Gdyby podnieść rozdzielczość zliczania o jeden rząd, uzyskując tick na poziomie 1 µs i okres zliczania 1,6 kHz. 1600 Hz / 50 Hz = 32, więc przez tyle jeszcze należy podzielić zegar główny. Zrobię to w prescaler’ze. Trzeba pamiętać, że w to miejsce wpisujemy wartość pomniejszoną o 1, czyli w tym wypadku 31. Można dopisać w polu Pulse wartośc odpowiadającą zakresowi sterowania serwem. Ja wpisałem 1000, czyli odpowiednik 1 ms dla powyższych ustawień timer’a. Wyliczenia te mogą wydawać się chaotyczne, lecz przy odrobinie wprawy idzie to w miarę gładko 🙂 W miarę zdobywania doświadczenia wiele działań związanych z liczeniem paramterów timer’ów zaczyna być widoczne na pierwszy rzut oka.

Tak ustawiony Cube jest w stanie wygenerować szkielet kodu, który posłuży mi do obsługi serwa. BIblioteczka serwa, którą napisałem jest dosyć mała. W zasadzie serwomechanizmowi zadajemy jedynie kąt wychylenia. Cała biblioteka to trzy funkcje. Inicjalizacja, do której przekazać należy strukturę utworzoną dla każdego serwa z osobna, wskaźnik na timer odpowiadający serwu oraz kanał PWM tegoż timer’a.

void Servo_Init(servo_t *servo, TIM_HandleTypeDef *_htim, uint32_t _channel);

Dla serwa w powyższej konfiguracji Cube’a inicjalizacja wygląda następująco:

Servo_Init(&servo1, &htim2, TIM_CHANNEL_1);

Pozostałe dwie funkcje to ustawienie serwa w zadanej pozycji. Jedna dla kątów całkowitych, druga dla ułamków.

void Servo_SetAngle(servo_t *servo, uint16_t angle);
void Servo_SetAngleFine(servo_t *servo, float angle);

Na początku pliku nagłówkowego umieściłem definicje użytego przeze mnie serwa Turnigy TG9e. Powiązane z nią są maksymalne wartości odchyleń oraz wartości timera im odpowiadające. Każdy model serwomechanizmu ma inne maksymalne możliwości, a parametry te dobrałem empirycznie. Nie są one wyznaczone z chirurgiczną precyzją.

Jak wygląda przebieg dla serwomechanizmu? Klasycznie jak PWM. Wychylenia serwa odpowiednio: 0°, 90° i 180°:

Jak podłączyć drugie serwo?

Przewodem, a jak inaczej? Najwygodniej skorzystać z już skonfigurowanego kanału, który wystawia maksymalnie cztery kanały PWM. Ja użyłem kanału drugiego dla kolejnego bzyczka. Jeśli byłaby potrzeba użycia innego timer’a, niestety trzeba skonfigurować ten timer w podobny sposób. Konfiguracja drugiego kanału dla serwa jest banalnie prosta. Wystarczy ustawić pin i aktywować Channel2  w drzewie opcji TIM2.

W kodzie trzeba zadeklarować kolejną zmienną dla serwa oraz zainicjować je.

Servo_Init(&servo1, &htim2, TIM_CHANNEL_1);
Servo_Init(&servo2, &htim2, TIM_CHANNEL_2);

Kompiluję i heja banana! Działa 🙂

 

PWM dla dwóch serw wygląda tak:

Wychylenia są niezależne. Jedyne co jest wspólne to punkt początkowy stanu wysokiego. Wszystkie PWM’y oparte o jeden timer będą miały tą wspólną cechę.

A więcej serw?

Zwiększanie liczby serw w układzie może być kuszące na przykład budując jakiegoś robota pająka. Podłączanie każdego pojedynczego serwa do wyjść PWM timerów może nie być wtedy najlepszym rozwiązaniem. Co wzamian?

Jedną z propozycji może być układ PCA9685. Domyślnie jest to sterownik PWM dla diod LED, ale w odpowiednim układzie i konfiguracji spokojnie poradzi sobie z serwami. Nasi chińscy przyjaciele produkują takie płytki dedykowane dla serw i tylko tym aspektem się zajmę. Co potrafią te moduły?

Układ PCA9685 według noty katalogowej( PCA9685 Datasheet ) posiada 16 kanałów PWM. Może on być zasilany w szerokim zakresie napięcia 2,3 ÷ 5,5 V, co sprawia, że nadaje się do zastosowania z STM32. Każde z wyjść na diody może być obciążone 25 miliamperami przy napięciu 5.5V.

Kontroler ten posiada programowalną częstotliwość PWM w zakresie od 24 do 1526 Hz przy czym każde z wyjść będzie posiadało tą samą częstotliwość PWM. Wypełnienie może być kontrolowane w zakresie 0÷100% w rozdzielczości 12 bitów, co daje 4096 kroków.

PCA9685 komunikuje się przez interfejs I²C. Ciekawe jest to, że scalak ma wbudowany układ przeciwdziałaniu zakłóceniom na liniach SDA i SCL. Maksymalna częstotliwość zegara SCL to 1 MHz. Dodatkowo układ udostępnia 6 bitów konfiguracji adresu. Dzięki temu można podłączyć 64 takie układy, co daje możliwość kontroli aż 1024 serwa!

Po więcej szczegółów dotyczących samego układu zapraszam do noty katalogowej.

Płytka, którą dostajemy dostosowana jest do sterowania serwami. Dla każdego serwa położone jest 3-pinowe złącze w dodatku oznaczone odpowiednio kolorami dla zasilania i sygnału. Zasilanie dla serw jest oddzielone od zasilania układu scalonego i dostarczane jest przez złącze śrubowe. Dzięki temu logikę możemy puścić na 3,3 V a serwa na 5V. Jako, że serw może być aż 16 i to całkiem spore, należy użyć zewnętrznego zasilacza.

Schemat i konfiguracja Cube’a

Tym razem użyję Nucleo z STM32F401RE. Od jakiegoś czasu CubeMX generuje jednakowy zestaw nagłówków dla wszystkich rodzin mikrokontrolerów, więc nie ma problemów z przenoszeniem między poszczególnymi MCU. Podłączenie jest banalne.

W Cube aktywuj I2C1 na prędkość jaką chcesz. Tyle wystarczy.

Kod PCA9685

Układ PCA9685 jest dosyć rozbudowany. Pozwala m. in. ustawić różną częstotliwość PWM, Należy pamiętać, że częstotliwość ta jest identyczna dla każdego z 16 kanałów. Można natomiast dla każdego z kanału ustawić inny punkt startu i końca impulsu, czyli można np. przesunąć część wyjść.

Jednak zacznę od początku. Pierwszą rzeczą, którą należy zrobić chcąc sterować serwami, to  trzeba odkometować definicje z nagłówka.

#define PCA9685_SERVO_MODE

Jest to takie drobne ułatwienie, które konfiguruje PWM na odpowiednią wartość oraz udostępnia funkcje serwa, blokując jednocześnie dostęp do funkcji zmiany częstotliwości PWM.

Klasycznie pierwszą funkcją, którą trzeba wywołać w mainie to inicjalizacja. Resetuje ona scalak, ustawia PWM oraz włącza auto inkrementacje adresów rejestrów dzięki której łatwo wpisuje się dane do PCA9685.

PCA9685_STATUS PCA9685_Init(I2C_HandleTypeDef *hi2c);

W pozycji używania układu z serwami interesować nas będą dwie funkcje. Pierwsza to

PCA9685_STATUS PCA9685_SetPin(uint8_t Channel, uint16_t Value, uint8_t Invert);

Ustawia sygnał PWM o zadanej długości. Można również odwrócić PWM, ale w przypadku serwa jest to nieco nienaturalne. W celu użycia tej funkcji należy znać wartości, które trzeba wpisać. Układ przyjmuje maksymalnie wartość 4096, lecz zakres użyteczny dla serwa to ok 110÷500 zdefiniowane jako SERVO_MINSERVO_MAX dla testowych Turingy TG9e. Teoretycznie zakres ten umożliwia ruch w zakresie 0÷180º(a właściwie to -90º do +90º). W praktyce zauważyłem, że kaze z serw ma niewiele różny zakres.

Niestety trzeba się trochę nagłówkować jakie wychylenie odpowiada konkretnej wartości PWM. Dużo łatwiej będzie operować w wartościach kątowych wychylenia. Służy do tego odpowiednia funkcja.

PCA9685_STATUS PCA9685_SetServoAngle(uint8_t Channel, float Angle);

Jest ona dużo prostsza w użyciu. Kąt podawany w argumencie nie musi być pełny, dlatego postawiłem tam float. Chcąc używać tej funkcji należy mieć prawidłowo zdefiniowane definicje zakresów dla użytych serw oraz kątów, które im odpowiadają.

Nic nie stoi na przeszkodzie, aby wykorzystać moją bibliotekę do kontrolowania LEDów. Wystarczy odkomentować PCA9685_SERVO_MODE, a nawet można na nim działać, jeśli PWM o częstotliwości 50 Hz będzie odpowiedni. Na koniec jeszcze krókie demo z działania PCA9685.

 

Podsumowanie

Jak widać serwomechanizmy są bardzo łatwe w użyciu. Z użyciem dedukowanego modułu PWM jeszcze łatwiejsze. Zastosowania? Wszędzie tam, gdzie trzeba czymś ruszyć 😀 W modelarstwie są to np stery samolotu, skręcanie. Można spróbować zrobić blokadę drzwi czy nawet zegar na serwach. Możesz podzielić się pomysłem w komentarzu.

Od zawsze chodzi mi po głowie zbudowanie manipulatora na serwach. Takiego z zaszytą kinematyką robota, a nie na milionach potencjometrów jak projekty na Arduino. Co Ty na to?

Kody testowe wraz z bibliotekami znajdziesz na moim GitHUB’ie: PWM, PCA9685

Na koniec zachęcam do dyskusji w komentarzach na temat obsługi wyświetlacza LCD na STM32. Uważasz, że popełniłem gdzieś błąd? Masz jakiś ciekawy pomysł co można ulepszyć? Podziel się tym w komentarzu! Pamiętaj, że dyskusja ma być kulturalna i zgodna z zasadami języka polskiego.

kurs stm32

 

4.5/5 - (6 votes)

Podobne artykuły

.

4 komentarze

SaS · 03/12/2019 o 22:31

Pojawił się kilka opisów komunikacji po I2C ale nigdzie nie znalazłem informacji o tym, że niektóre serie F1 (miałem nieprzyjemność się na taką natknąć jakieś 2 lata temu przy okazji projektu “Domofonu GSM” ) mają błąd. I2C często po kilku poprawnych cyklach komunikacji zawiesza się. HAL zwraca BUSY. Kilka linijek kodu wystarczy aby rozwiązać problem.
Warto też wiedzieć, że przerwanie komunikacji ze slave w złym momencie (np reset uC), może zablokowacI2C (trzymanie linii SDA w stanie L). Jeśli slave nie ma linii reset to pozostaje wyłączenie zasilania. Jest jeszcze jeden sposób, wyłączyć I2C, ustawić GPIO linii SDA i SCL na wyjścia OD, na pinie SCK wygenerować 9 impulsów i warunek STOP. Po tym włączyć I2C i wszystko śmiga.
Sprawdzone na STM32 i AVR.

    Mateusz Salamon · 08/12/2019 o 15:12

    Ojej też kiedyś trafiłem na ten problem z zawieszającym się I2C na L152… Pamiętam, że to była straszna rzecz do zdebugowania i faktycznie 2-3 linijki kodu załatwiały sprawę 🙂 Ten błąd był tylko w F1 czy znasz jeszcze jakieś przypadki?

      Faustyna · 14/02/2022 o 05:47

      Witam mam pytanie czy kod bardzo się będzie różnił gdy będę chciała robić na płytce nucleo f103RB? Jeśli tak to jak by to wyglądało ?
      Pozdrawiam

      Mateusz Salamon · 15/02/2022 o 09:27

      Będzie w zasadzie tak samo. Wystarczy wygenerować projekt z włączonym I2C 🙂

Dodaj komentarz

Avatar placeholder

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *