Kurs AVR-GCC. Serwomechanizm modelarski
To będzie łatwy aczkolwiek poruszający:) temat - sterowanie serwomechanizmem modelarskim. Ze względu na mnogość możliwych zastosowań serwo modelarskie może być potężną bronią w arsenale (w szufladzie) każdego domorosłego konstruktora. Na początku, dla tych, którzy jeszcze nie mieli styczności z serwomechanizmami modelarskimi, napisałem kilka zdań wprowadzenia. Dalej tłumaczę krótko jak sterować serwem analogowym i wreszcie uruchamiamy przykładowy programik - będzie to sterownik dwunastu serw na uC atmega8.
Serwo modelarskie
Serwomechanizm modelarski to elektromechaniczne urządzenie wykonawcze, montowane w kierowanych radiem modelach samolotów, pojazdów, łódek itd.
W modelach serwomechanizmy, zwykle z pomocą różnych dźwigni i popychaczy, poruszają powierzchniami sterowymi, kołami lub innymi ruchomymi elementami modelu. Każdy serwomechanizm zamontowany w modelu podłączony jest przewodem sygnałowym do odbiornika radiowego zdalnego sterowania.
We wnętrzu obudowy serwomechanizmu modelarskiego znajdują się: silniczek DC; przekładnia redukująca obroty(wzmacniająca moment siły) z wałkiem wyjściowym; potencjometr obrotowy oraz płytka z elektroniką. Wałek wyjściowy sprzęgnięty jest z wałkiem potencjometru, dodatkowo mechaniczna blokada ogranicza kąt obrotu wałka ( 120°-150°). Elektronika serwomechanizmu stale mierzy napięcie na potencjometrze i tak steruje kierunkiem obrotów oraz mocą silnika, żeby jak najszybciej uzyskać pożądany kąt wychylenia na wałku wyjściowym. Czyli jest to układ ze sprzężeniem zwrotnym.
Ze względu na rozmiary i siłę serwa modelarskie dzielą się na: serwa micro, standard i giga, a ze względu na zasadę działania na serwa analogowe i cyfrowe, produkowane są też specjalne serwa dla entuzjastów robotyki. Jest kilka "parametrów technicznych", które rozważa się przy ocenie serw modelarskich, ja nie będę tutaj o nich pisał, w internecie można znaleźć mnóstwo informacji na ten temat.
Sterowanie serwem
Z obudowy serwomechanizmu wyprowadzony jest kabel, trzy żyły: czarny lub brąz - masa, czerwony- zasilanie (4,8-7,2V), biały lub żółty - sygnał sterujący. Na wejście serwomechanizmu podaje się przebieg prostokąty o częstotliwości 40-60Hz, kąt wychylenia wałka serwa związany jest z szerokością impulsu, patrz rysunek poniżej. Szerokość impulsu zmienia się w zakresie od ok 0,3ms do 2,3 ms. Środkowy (centralny) kąt wychylenia wałka serwo przyjmuje dla impulsu o długości 1,5ms , dla impulsu o szerokości 1ms wałek serwa obrócony jest o ok 45° w w prawo, a dla 2ms - o 45° w lewo.
Sygnał PWM sterujący serwem możemy generować z pomącą tajmerów(peryferyjnych układów czasowy) AVRa pracujących w trybie PWM. Atmega8 ma tylko trzy kanały PWM, atmega16(32) - cztery, atmega128 i atmega88 - 6. Jak skonfigurować tajmery AVRa do sterowania serwem, znajdziesz w internecie wiele przykładów - jeśli nie, zapytaj na forum. Natomiast jeśli chcemy sterować większą liczbą serw, to pozostaje generować sygnał PWM programowo, tzn. nie timer, a program bezpośrednio steruje wyjściami PWM. Dalej w artykule uruchomimy przykładowy program dla atmega8, w którym sygnały PWM sterujące dwunastoma serwami, będą tworzone programowo.
Przykład programu sterownika dwunastu serw
Przykładowy program przeznaczony jest dla AVRa atmega8 taktowanego zegarem 16MHz - maksymalna prędkość atemaga8 jest raczej niezbędna przy programowym generowaniu sygnału PWM. Aby zmienić częstotliwość taktowania uC atmeag8 z fabrycznie ustawionego 1MHz na 16MHz, należy do atmega8 dołączyć rezonator kwarcowy 16MHz - schemat poniżej.
W miejsce "usbasp" podajemy nazwę własnego programatora. Przy zmianie fuse-bitów trzeba uważać, bo drobna pomyłka może skutkować zablokowaniem układu.
Dwanaście serw zostało podłączonych do portów we/wy AVRa w następujący sposób: serwa s1-s6 - porty PD2..PD7, a serwa s7-s12 - porty PC0..PC5. Do zasilania dwunastu serw potrzeby jest odpowiedniej mocy zasilacz lub akumulator (4,8-7,2V), polecam zasilacz od komputera PC.
Przygotowałem dwie wersje programu sterownika serw. W pierwszej wersji programu, co sekundę, sterownik zmienia kąt wychylenie orczyka serw s0,s2,s4..s10 w następujący sposób: położenie centrum (1,5ms), centrum+45°, centrum-45°, centrum; zaś serw s1,s3,s5..s11 w przeciwnym kierunku. W drugiej wersji programu będziemy sterować zdalnie dwunastoma serwami z komputera PC, połączymy uC atmega8 z komputerem poprzez złącze szeregowe RS232C.
Przykładowy program sterownika działa w następujący sposób: Na początku głównej pętli programu wszystkie dwanaście wyjść, do których przyłączone są serwomechanizmy, zostają ustawione są w stan wyskoki - początek impulsu na wszystkich wyjściach. Następnie, po odpowiednim opóźnieniu, program ustawia poszczególne wyjścia w stan niski; dziej się to w pierwszej zagnieżdżonej pętli for. Czas trwania impulsu na poszczególnych wyjściach uzależniony jest od wartości odczytanej z tablicy "SERVOS". Tablica "SERVOS" zawiera dwanaście bajtów mówiących o kącie wychyleniu poszczególnych serw, wartość 0x8B odpowiada położeniu centralnemu (1,5ms), a zmiana wartości bajtu jeden powoduje obrót serwa w przybliżeniu o 1 stopień. Szerokość impulsu na wyjściach zmienia się w zakresie 0-2,4ms. Dalej w kolejnej zagnieżdżonej pętli while umieszczona jest reszta kodu programu (nie związana z generowaniem sygnału pwm), czas wykonania tego fragmentu powinien być mniejszy niż 18ms. Cały kod zawarty w głównej pętli programu wykonuje się w czasie ok 20ms - pięćdziesiąt razy na sekundę.
Opóźnienia w programie odmierzane są z użyciem 16-bitowego tajmera1, Timer1 konfigurujemy i włączamy ustawiając odpowiednie bity w rejestrze TCCR1B. Tajmer1 zostaje włączony przed główną pętlą programu, tak, że zlicza on co ósmy cykl zegara(16MHz) taktującego mikrokontroler - wybrano opcję preskaler 8. Stan timera1 odczytuje się z 16-bitowego rejestru TCNT1, a zeruje się tajmer1 zapisując wartość zero do rejestru TCNT1.
Poniżej zamieściłem pełny listing pierwszej wersji programu sterownika serw, dalej można pobrać spakowany katalog projektu z plikiem makefile.
/* ABXYZ KURS AVR-GCC, SERWO MODELARSKIE Steruje 12 serwami atmega8 (16MHz) */ #include <avr/io.h> // Wychylenie serw unsigned char servos[12] ; int main(void) { volatile unsigned char n,p_c,p_d,p_cc,p_dd; unsigned char t; // Początkowe wychylenie 12 serw // centrum 1,5ms for(n=0; n<12; n++) servos[n] = 0x8B; // Wyjścia PC0..PC5 i PD2..PD7 - 12 serw DDRC = 0x3f; // wyjścia PC0..PC5 - s6..s11 DDRD = 0xfc; // wyjścia PD2..PD7 - s0..s5 // Start Timer1, preskaler 8 // Tajmer1 zlicza co ósmy cykl zegara // taktuącego uC (16MHz), czyli co 0,5us TCCR1B = (1<<CS11); // GŁÓWNA PĘTLA PROGRAMU for(t=0;;t++) { p_c = 0; p_d = 0; // Start impulsu - wszystkie wyjścia // w stan wysoki PORTC |= 0x3f; PORTD |= 0xfc; // Czas wykonania pętli 241*9,5us 2,3ms for(n=0; n<241; n++) { TCNT1 = 0; // Zeruje Timer1 SFIOR = 1; // Resetuje preskaler if(n>servos[0]) p_d |= (1<<2); if(n>servos[1]) p_d |= (1<<3); if(n>servos[2]) p_d |= (1<<4); if(n>servos[3]) p_d |= (1<<5); if(n>servos[4]) p_d |= (1<<6); if(n>servos[5]) p_d |= (1<<7); if(n>servos[6]) p_c |= (1<<0); if(n>servos[7]) p_c |= (1<<1); if(n>servos[8]) p_c |= (1<<2); if(n>servos[9]) p_c |= (1<<3); if(n>servos[10]) p_c |= (1<<4); if(n>servos[11]) p_c |= (1<<5); p_cc = ~p_c; p_dd = ~p_d; // Koniec impulsu PORTD &= p_dd; PORTC &= p_cc; // Czeka, jeśli zawartość tajmera1 // jest mniejsza niż 19, czyli 9,5us od // momentu zerowania tajmera1 while(TCNT1<19 ); // 0,5 * 19 = 9,5us } TCNT1 = 0; // Zeruje Timer1 SFIOR = 1; // Resetuje preskaler // Pętla wykonuje się dopóki zawartość tajmera1 // jest mniejsza niż 35100, czyli // ok 18ms od momentu zerowania tajmera while(TCNT1<35100) // 0,5*3510 = 1,7550 ms { // Tutaj, w pętli, reszta programu, // powinien się wykonać w czasie <18ms. if(t==1) { servos[0] = 0x5e; servos[1] = 0xb8; servos[2] = 0x5e; servos[3] = 0xb8; servos[4] = 0x5e; servos[5] = 0xb8; servos[6] = 0x5e; servos[7] = 0xb8; servos[8] = 0x5e; servos[9] = 0xb8; servos[10] = 0x5e; servos[11] = 0xb8; } else if(t==50) { servos[0] = 0x8B; servos[1] = 0x8B; servos[2] = 0x8B; servos[3] = 0x8B; servos[4] = 0x8B; servos[5] = 0x8B; servos[6] = 0x8B; servos[7] = 0x8B; servos[8] = 0x8B; servos[9] = 0x8B; servos[10] = 0x8B; servos[11] = 0x8B; } else if(t==100) { servos[0] = 0xB8; servos[1] = 0x5e; servos[2] = 0xB8; servos[3] = 0x5e; servos[4] = 0xB8; servos[5] = 0x5e; servos[6] = 0xB8; servos[7] = 0x5e; servos[8] = 0xB8; servos[9] = 0x5e; servos[10] = 0xB8; servos[11] = 0x5e; } else if(t==150) { t=0; } } } return 0; }
Druga wersja programu sterownika serw
W drugiej wersji programu sterownika serw dodałem obsługę portu szeregowego. Łączymy mikrokontroler atmega8 kablem RS232C do komputera PC i sterujemy zdalnie dwunastoma serwami z pomocą specjalnego programu uruchamianego w systemie windows. Schemat poniżej pokazuje sposób połączenia portu szeregowego atmega8 z interfejsem RS232C komputera PC. Jeśli w komputerze brak portu RS232C, to zamiast układu MAX232, stosujemy konwerter USB-RS232/TTL - kosztuje jakieś tam kilkanaście zł.
Stworzyłem niewielki program "serwo-demo" dla systemu Windows, w jego okienku, poruszając kontrolkami-suwakami, możemy zdalnie sterować dwunastoma serwomechanizmami, zrzut ekranu poniżej.
Obsługa programu "serwo-demo" jest oczywista. Zaczynamy od wybrania właściwego portu COMx i kliknięcia przycisku "Open". Po otwarciu portu COMx, każda ruch kontrolki-suwaka skutkuje wysłaniem do portu ramki danych, zawierającej informacje o wychyleniu dwunastu serwomechanizmów. Ostatnia wysłana portem szeregowym ramka danych pokazana jest w lewym dolnym rogu okienka programu.
Jednorazowo wysyłanych jest 13 bajtów danych, 12 bajtów to informacja o kącie wychylenia 12 serw, a trzynasty bajt to suma kontrolna. Ramka danych składa się wyłącznie z wybranych kodów ASCII, tj. z cyfr hex(0-9,A-F),ze znaku dwukropka(:) i znaku końca linii. Dane w ramce są zapisane w systemie szesnastkowym, dwa znaki (cyfry hex 0-9,A-F) na jeden bajt danych. Ramka danych rozpoczyna się od znaku dwukropka ':', następnie występuje 24 cyfry hex - wychylenie dwunastu serw, następnie dwie cyfry hex - suma kontrolna i zamykający ramkę znak końca linii.
Programik "serwo-demo" został stworzony z pomocą darmowego środowiska programistycznego "Visual C++ Express Edition 2008", jest prościuchny:) i służy jedynie do demonstracji działanie naszego sterownika serw. "Visual C++" daje możliwość szybkiego tworzenia aplikacji okienkowych dla Windows metodą wizualną, tzn. klikania, kopiowania i wklejania. Każdy, kto potrafi cokolwiek programować w C/C++, może pobrać za free "Visual C++" i w kilka chwil stworzyć program do zdalnego sterowania serwami według swoich potrzeb.
Kilka wskazówek
Programowe generowanie sygnałów PWM ma tę zaletę, że możemy do AVRa przyłączyć dowolną ilość serw - tyle, ile wolnych jest portów we/wy. W przypadku korzystaniu z tajmerów uC ATmega, możemy sterować tylko kilkoma serwami - dwa kanały PWM na jeden tajmer. Ale programowe tworzenie sygnału sterującego dla wielu serw w znacznym stopniu zajmuje mikrokontroler i może mu braknąć czasu na realizowanie innych zadań. Tak więc, jeśli mamy tylko kilka serw, to lepiej korzystać z tajmerów. Kolejną opcją jest wybór innego, lepiej wyposażonego w peryferia mikrokontrolera inż atmega. Ja polecam popularne ostatnio 32-bitowe mikrokontrolery stm32. Kolejna rzecz. My tu bawimy się w programowanie głównie w języku C, w praktyce tego rodzaju programy jak nasz sterownik serw dobrze jest napisać od początku do końca w asemblerze.
Katalog z plikami źródłowymi przykładów można pobrać klikając w link serwo_modelarskie.zip.