Šimon Mik, UREL, FEEC, VUT Brno
xmiksi00
stud.feec.vutbr.cz
Miloš Juhás, UREL, FEEC, VUT Brno
xjuhas01
stud.feec.vutbr.cz
Servo je zařízení které umožňuje otáčet hřídelí v rozsahu zpravidla od -90° do +90°. Na vstupu serva jsou tři vodiče: zem, napájení a řídící napětí, které nastavuje polohu serva. Servo obsahuje stejnosměrný motor, převodovku a výstupní hřídel spraženou s potenciometrem pro detekci úhlu natočení [3].
Cílem projektu je ovládání dvou modelářských mikroserv Hitec HS-422 (obrázek 1) pomocí mikrokontroléru. První servo se bude natáčet v rozsahu -90° až +90°, druhé servo v rozsahu 0° až +90°. Serva se ovládají pomocí pulzně šířkové modulace (PWM) s periodou 20ms a šířkou pulzů přibližně 1 až 2ms. Šířka pulzu přímo úměrně odpovídá natočení výstupní hřídele. Pulzy s šířkou 1,5ms nastaví servo do střední polohy.
V laboratoři je k dispozici vývojová deska s mikrokontrolérem Atmel ATmega 16, rovněž nám byla zapůjčena konstrukce se dvěma servy, která je plánovaná k využití natáčení web kamery. Bylo nutno tedy ještě vyrobit napájecí obvody. Proudový odběr serva HS-422 udává výrobce na 10mA v klidu a 280mA při pohybu při napětí 5V. Ve skutečnosti, však podle [4] může proudový odběr při rychlém střídání krajních poloh, kdy servo ještě nedosáhlo požadované krajní polohy být až 700mA. Pro napájení dvou serv je potřeba hodnotu vynásobit dvěmi, tedy maximální proudový odběr může dosáhnout hodnoty až 1,4A.
Bylo navrženo zapojení (obrázek 2) Do napájecího konektoru J1 je přivedeno nestabilizované napětí (max 30V) a na výstupu stabilizátoru 78S05 dostáváme stabilizované napětí +5V. Kondenzátory C2 a C4 slouží jako filtrační. Na svorkovnici X2 budou přivedeny z portu mikrokontroléru řídící signály pro obě serva. Odpory R4 a R5 nastavují proud do optočlenů na 20mA. Odpory R6 a R7 jsou trvale připojeny k napájecímu napětí (pull-up rezistory). Aby na řídícím výstupu SV2 a SV3 bylo nulové napětí, musí být optron zapnutý (na výstupu mikrokontroléru log. 1). Impulsy na řídícím výstupu SV2 a SV3 jsou generovány stažením výstupních signálů mikrokontroléru na log. 1.
![]() |
![]() |
![]() |
![]() |
Part | Value | Package |
---|---|---|
R1 | 200 | R1206 |
R2 | 200 | R1206 |
R3 | 4k7 | R1206 |
R4 | 4k7 | R1206 |
C1 | 22u | RM2,5-5 |
C2 | 330n | RM5 |
IC1 | 78S05 | TO220V |
J1 | SPC4077 | |
OK1 | PC 817 | SMD4 |
OK2 | PC 817 | SMD4 |
SV1 | MA03-1 | |
SV2 | MA03-1 | |
X1 | AK500/4 |
Na ovládanie horizontálnej a vertikálnej polohy serv bola napísaná knižnica ServoHV.h, obsahujúca všetky potrebné funkcie. Táto knižnica je určená pre mikroprocesor ATmega16 pracujúcim na kmitočte 16 MHz. Využíva 16 bitový Timer/Counter1 v režime CTC, ktorý umožňuje presné nastavenie polohy. Tá je nastaviteľná s presnosťou na jeden stupeň, keďže funkcie používajú celočíselné premenné. Polohu je možné nastaviť buď absolútne alebo relatívne. Absolútna poloha sa zadáva v kladných stupňoch, zatiaľ čo relatívna v stupňoch so znamienkom. Pre oba spôsoby platí, že pokiaľ je požadovaná hodnota mimo rozsah obmädzený krajnými polohami serva, funkcia vráti 1 a polohu nebude nastavovať. V opačnom prípade sa nastaví poloha a návratová hodnota bude 0. Vzhľadom na dlhšiu dobu nastavenia požadovanej polohy okolo 800 ms, je vhodné relatívnu polohu meniť s hrubším krokom a až potom prípadne doladiť jemnejšie.
Názov | Defaultná hodnota | Rozsah hodnôt | Popis |
---|---|---|---|
V_PORT | PORTD | PORTn | Port, ku ktorému je pripojené vertikálne servo |
H_PORT | PORTD | PORTn | Port, ku ktorému je pripojené horizontálne servo |
V_PIN | 6 | 0 - 7 | Číslo pinu na danom porte, ku ktorému je pripojené vertikálne servo |
H_PIN | 7 | 0 - 7 | Číslo pinu na danom porte, ku ktoremu je pripojené horizontálne servo |
uiBaseOcr | 2048 | 0 - 65535 | Východzia hodnota registra OCRB, dĺžka jedného cyklu (2048 ~ 1 ms @16MHz) |
PERIOD_CYCLES | 20 | 1 - 255 | Počet cyklov v jednej perióde (20*uiBaseOcr ~ 20 ms) |
ucSetCycles | 40 | 1 - 255 | Počet periód potrebných k nastaveniu požadovanej polohy (40*uiBaseOcr ~ 800 ms) |
uiTopOcrV | 1200 | 0 - 65535 | Šírka pulzu pre hornú krajnú polohu vertikálneho serva. (1200=560 us @16MHz) |
uiBottomOcrV | 3000 | 0 - 65535 | Šírka pulzu pre spodnú krajnú polohu vertikálneho serva. (3000=1430 us @16MHz |
uiLastPosV | 2000 | 1 - 65535 | Šírka pulzu pre východziu polohu vertikálneho serva. |
uiLeftOcrH | 1200 | 1 - 65535 | Šírka pulzu pre lavú krajnú polohu horizontálneho serva. (1200=560 us @16MHz |
uiRightOcrH | 4700 | 1 - 65535 | Šírka pulzu pre pravú krajnú polohu horizontálneho serva. (4700=2300 us @16MHz |
uiLastPosH | 2000 | 1 - 65535 | Šírka pulzu pre východiu polohu horizontálneho serva. |
ucTopDegV | 90 | 1 - 255 | Horná krajná poloha vertikálneho serva v stupňoch |
ucBottomDegV | 0 | 1 - 255 | Dolná krajná poloha vertikálneho serva v stupňoch |
ucLeftDegH | 180 | 0 - 255 | Lavá krajná poloha horizontálneho serva v stupňoch |
ucRightDegH | 0 | 0 - 255 | Pravá krajná poloha horizontálneho serva v stupňoch |
void
_servo_init(
void
);
_get_cal()
a povoliť tak automatické načítanie nastavenych parametrov z EEPROM. V tomto prípade je ale dôležité aby boli premenné v EEPROM na daných adresách skutočne uložené.
void
_set_cal(
char
write);
uiTopOcrV, uiBottomOcrV, ucTopDegV, ucBottomDegV, uiLeftOcrH, uiRightOcrH, ucLeftDegH, ucRightDegH,uiBaseOcr, ucSetCycles.
Funkciu je možné použiť na kalibráciu alebo pri zmene kmitočtu oscilátora.
write = 0
// uloží tiež aktuálnu polohu, tj. premenné
uiLastPosV, uiLastPosH
write != 0
// neukladá premenné
uiLastV, uiLastH.
void
_get_cal(
char
read);
_set_cal().
read = 0
// načíta tiež aktuálnu polohu, tj. premenné
uiLastPosV, uiLastPosH
read != 0
// nenačíta premenné
uiLastV, uiLastH.
unsigned
char
_abs_pos(
unsigned
char
setPosH,
unsigned
char
setPosV);
setPosH
// celočíselná hodnota horizontálneho natočenia v stupňoch, z nastaveného rozsahu,
SetPosV
// celočíselná hodnota vertikálneho natočenia v stupňoch, z nastaveného rozsahu.
unsigned
char
_rel_pos(
char
setOffsetH,
char
setOffsetV);
setOffsetH
// celočíselná znamienková hodnota uhľa, o ktorý sa natočí horizontálne servo voči aktuálnej polohe, pričom nesmie byť prekročená medzná hodnota
SetOffsetV
// celočíselná znamienková hodnota uhľa, o ktorý sa natočí vertikálne servo vočí aktuálnej polohe, pričom nesmie byť prekročená medzná hodnota.
unsigned
char
_center_pos(
void
);
void
_start_intro_1(
void
)
h_center_pos(); h_right_pos(); h_left_pos(); h_center_pos(); v_center_pos(); v_top_pos(); v_bottom_pos(); v_center_pos();
Tá môže slúžiť buď na otestovanie funkcií alebo ako intro po zapnutí.
void
_start_intro_2(
void
)
_abs_pos(90,0); h_abs_pos(180); h_abs_pos(0); h_abs_pos(135); h_abs_pos(45); h_abs_pos(90); v_abs_pos(90); v_abs_pos(45); v_abs_pos(0); v_abs_pos(90); v_abs_pos(0);
Tá môže slúžiť buď na otestovanie funkcií alebo ako intro po zapnutí.
void
v_set_const(
void
);
uiConstV,
ktorá sa používa pri prepočte stupňov na šírku pulzu pre vertikálne servo. Volá ju tiež
_servo_init(),
takže ju nie je nutné volať, pokiaľ nedôjde k zmenám parametrov počas behu programu (napr. pri kalibrácii).
unsigned
char
v_abs_pos(
unsigned
char
setPosV);
SetPosV
// celočíselná hodnota vertikálneho natočenia v stupňoch, z nastaveného rozsahu.
unsigned
char
v_top_pos(
void
);
unsigned
char
v_center_pos(
void
);
unsigned
char
v_bottom_pos(
void
);
unsigned
char
v_rel_pos(
char
setOffsetV);
SetOffsetV
// celočíselná znamienková hodnota uhľa, o ktorý sa natočí vertikálne servo vočí aktuálnej polohe, pričom nesmie byť prekročená medzná hodnota.
unsigned
int
v_get_deg(
void
);
uiLastPosV
prepočítaná na stupňe.
void
h_set_const(
void
);
uiConstH,
ktorá sa používa pri prepočte stupňov na šírku pulzu pre horizontálne servo. Volá ju tiež
_servo_init(),
takže ju nie je nutné volať, pokiaľ nedôjde k zmenám parametrov počas behu programu (napr. pri kalibrácii).
unsigned
char
h_abs_pos(
unsigned
char
setPosH);
SetPosH
// celočíselná hodnota horizontálneho natočenia v stupňoch, z nastaveného rozsahu.
unsigned
char
h_right_pos(
void
);
unsigned
char
h_center_pos(
void
);
unsigned
char
h_left_pos(
void
);
unsigned
char
h_rel_pos(
char
setOffsetH);
setOffsetH
// celočíselná znamienková hodnota uhľa, o ktorý sa natočí horizontálne servo voči aktuálnej polohe, pričom nesmie byť prekročená medzná hodnota
unsigned
int
h_get_deg(
void
);
uiLastPosH
prepočítaná na stupňe.
void _set_uiBaseOcr(unsigned int val);
void _set_ucSetCycles(unsigned char val);
void _set_uiTopOcrV(unsigned int val);
void _set_uiBottomOcrV(unsigned int val);
void _set_uiLastPosV(unsigned int val);
void _set_uiLeftOcrH(unsigned int val);
void _set_uiRightOcrH(unsigned int val);
void _set_uiLastPosH(unsigned int val);
void _set_ucTopDegV(unsigned char val);
void _set_ucBottomDegV(unsigned char val);
void _set_ucLeftDegH(unsigned char val);
void _set_ucRightDegH(unsigned char val);
unsigned int _get_uiBaseOcr(void);
unsigned char _get_ucSetCycles(void);
unsigned int _get_uiTopOcrV(void);
unsigned int _get_uiBottomOcrV(void);
unsigned int _get_uiLastPosV(void);
unsigned int _get_uiLeftOcrH(void);
unsigned int _get_uiRightOcrH(void);
unsigned int _get_uiLastPosH(void);
unsigned char _get_ucTopDegV(void);
unsigned char _get_ucBottomDegV(void);
unsigned char _get_ucLeftDegH(void);
unsigned char _get_ucRightDegH(void);
_set_
nastavujú hodnotu premennej a funkcie s prefixom
_get_
naopak hodnotu premennej vracajú. Názov funkcie za prefixom je totožný s názvom premennej, s ktorou sa pracuje. Tieto funkcie sú určené na kalibráciu a je vhodné ich použiť s funkciami
_get_cal()
a
_set_cal().
_servo_init()
a potom už len využívať jednotlivé funkcie na nastavenie požadovanej polohy. Príkladom je nasledujúci výpis zdrojového kódu:
int main(void)
{
_servo_init();
if(!_abs_pos(90,0))
error();
while(1);
return 0;
}
_set_cal().
Na uloženie hodnôt do premenních slúžia funkcie označené prefixom
_set_,
za ktorým nasleduje názov premennej. Spôsob akým je kalibrácia vykonaná je tak ponechaný na samotný program. Funkcie
_set_cal()
aj
_get_cal()
vypínajú globálne prerušenie. Preto je nutné v prípade potreby následne globálne prerušenie povoliť. Naopak funkcia
_servo_init()
globálne prerušenie povoluje preto na posledných dvoch výpisoch už znova povolované nie je. Príklad práce s kalibračnými funkciami ukazujú nasledujúce výpisy:
// Uloženie parametrov
int ulozenie(void)
{
_set_LastPosV(3000);
_set_cal(0);
sei();
while(1);
return 0;
}
// Načítanie uložených parametrov
int nacitanie(void)
{
_get_cal(0);
_servo_init();
while(1);
return 0;
}
// Obnovenie pôvodných parametrov
int obnovenie(void)
{
_set_cal(0);
_servo_init();
while(1);
return 0;
}
ucSetCycles,
ktorá určuje dobu po ktorú sa poloha mení. Tá bola určená tak, aby sa stihla nastaviť správna poloha serva aj pri prechode z jednej krajnej polohy na druhú. Preto je táto doba v prípade malej zmeny polohy (o niekoľko stuňov) podstatne dlhšia ako je skutočne potrebné. Jednoduchá by bola napríklad implementácia pri zmene relatívnej polohy, kde je priamo zadaná hodnota, o ktorú sa bude poloha meniť. Druhou možnosťou je implementovať toto vylepšenie do funkcií pre absolútne nastavenie polohy, ktoré sú používané všetkými ostatnými funkciami a zmena by sa tak prejavila globálne.