Stránka 1 z 1
Problem riadenie serva
Napsal: 07 lis 2011, 12:38
od pokrivnik
Zdravim Vás, mám taký menší problém. Rozhodol som sa riadiť servá z PC pomocou mojej Atmegy8. No problém je v tom, že keď servo pripojím, tak prvý príkaz mi vykoná a natočí sa na požadovanú pomohu, no to je všetko. Na ďalšie príkazy nereaguje. Kód prikladam. Servá neriadim cez ORC port, ale jednotlivé piny. PWM je tvorené softvérovo cez CTC. V prerušení počítam 20ms. Malo by to byť všetko OK, osciloskop bohužiaľ nemám, takže neviem zistiť či to je na 100% tak.
Tak Vás týmto prosím o pomoc. Či som náhodou niekde niečo neprehliadol, stať sa môže všetko

Napsal: 07 lis 2011, 16:11
od pokrivnik
problem vyrieseny. napisal som si to odznova a chyba bola v navratovej hodnote funkcie.
Ale chcel by som sa opýtať na Váš názor. Či mi zvládne procesor ešte niekoľko operácií, ako nejaké AD prevody + odosielanie všetkých dát do PC, bez ovplyvnenia chodu serv? Alebo by bolo idálnejšie ísť s frekvenciou ešte vyššie pre istotu? Teraz tam mam 8MHz
Napsal: 07 lis 2011, 17:17
od Zaky
Čekat 20 ms v přerušení je špatně. V přerušení jen nastav timer a vrať se do programu. Po uplynutí času přijde přerušení od timeru, obsloužíš ho, znovu nastavíš timer a tak stále dokola a máš téměř veškerý výpočetní čas k dispozici.
Michal
Napsal: 07 lis 2011, 18:06
od pokrivnik
ono je to cez prerusenie, ibaze prerusenie nastava kazdu 1ms, a jeden cyklus je konecny ked prejde 20 preruseni. Iny sposob na ovladanie dvoch serv ma ani nenapada.
mna by len zaujimalo, ci sa da nejak spocitat, ako dlho trva to jedno prerusenie. respektive ci by to niekto vedel. mam taky pocit ze by sa to malo dat cez pocet instrukcii ktore sa vykonavaju nie?
Napsal: 07 lis 2011, 18:50
od mtajovsky
Vidím tam vícero věcí. Například se funkce smer() volá jak z main(), tak z rutiny přerušení. Přitom smer() pracuje s globálními proměnnými slovo1 a pohyb. To není dobrý postup. Například na řádku
else if(!strcmp(slovo1,dozae)
v rutime smer()
se slovo 1 otestuje, a než se stačí provést kód na podmínce závislý, tak se může stát, že rutina
ISR(USART_RXC_vect)
to slovo1 přepíše:
slovo1[x]=recchar();
Takže rutina smer() okracuje s hlediska podmínky s vadnými daty.
Obdobně pro data pohyb. Navíc, proč smer() vrací hodnotu pohyb, když tuto hodnotu rovnou nastavuje?
Napsal: 08 lis 2011, 07:51
od AB1
Tvůj kód není moc dobrý.
Při předděliči 1024 trvá jeden krok čítače 128 us.
Pro OCR2 = 8 vychází impuls 8*128 = 1024 us.
Pro OCR2 = 16 vychází impuls 16*128 = 2048 us.
Takže můžeme nastavit jenom 8 nebo 9 poloh serva.
Hodnota OCR2 = 20 v kódu je už mimo rozsah serva.
Nejlépe je použít osvědčený režim Fast pwm a čítač1.
Mimo jiné pak nepotřebujeme přerušení.
Kód: Vybrat vše
// servo2.c
// Řízení dvou serv, připojených k PB1, PB2
// Atmega8, F_CPU = 8 MHz
// Avr-gcc
#include <avr/io.h>
#include <util/delay.h>
//---------------------------------------------------------------------
int main (void)
{
// fast pwm, mode 14 , TOP=ICR1
TCCR1A = (1<<WGM11);
TCCR1B = (1<<WGM12) | (1<<WGM13);
TCCR1B |= (1<<CS11); //prescale 8, step = 1 us
// clear OC1A, OC1B on compare
TCCR1A |= (1<<COM1A1) | (1<<COM1B1);
// periode = 20 ms (50 Hz)
ICR1 = 20000;
OCR1A=1500;// 1.5 ms (center)
OCR1B=1500;// 1.5 ms
DDRB |= (1<<PB1) | (1<<PB2); // OC1A, OC1B output
for(;;) //test servo1
{
OCR1A = 1000; // 1000 us - left
_delay_ms(1500);
OCR1A = 1500; // 1500 us - center
_delay_ms(1500);
OCR1A = 2000; // 2000 us - right
_delay_ms(1500);
}//for
}//main
//---------------------------------------------------------------------
Napsal: 08 lis 2011, 17:39
od pokrivnik
mtajovsky:
Takže ak som pochopil dobre, je lepšie hodnotu premennej slovo1 najprv zapísať vo funkcii smer(); do nejakej premenej, napr. temp, definovanej v tejto funkcii a potom túto porovnávať v if?
A to, že vracia funkcia smer(); pohyb je už opravené.
AB1:
Preddeličku mám ale nastavenú na 32. Tak jedno prerušenie pri hodnote OCR=250 je presne 1ms.
V tomto prípade sa strednej hodnote serva rovná OCR 125, a u krajných hodnôt je to 0 a 250. Tam je podľa môjho názoru clekom dostačujúci krok.
Tvoj príklad ma tiež napadol, no vylučuje použitie ďalších serv. No na riadenie auta je určite výhodnejší.
Napsal: 08 lis 2011, 19:10
od mtajovsky
pokrivnik píše:mtajovsky:
Takže ak som pochopil dobre, je lepšie hodnotu premennej slovo1 najprv zapísať vo funkcii smer(); do nejakej premenej, napr. temp, definovanej v tejto funkcii a potom túto porovnávať v if?
To by mohlo být lepší. Musíte ale předpokládat, že přerušení může přijít kdykoliv. Ten problém je obecnější a chce to trochu teorie. V podstatě jde o to, že pokud existuje více úseků programu, které pracují s stejnými proměnnými a mohou být vykonávány asynchronně (z hlediska programu), třeba na základě asynchronního přerušení, tak je potřeba přístup k těm proměnným nějak ošetřit - serializovat. V principu byste měl na dobu, co trvá jak otestování proměnné, tak vykonávání kódu, který je na této proměnné závislý, zakázat přerušení té rutině, která by chtěla proměnnu měnit. Jelikož je nanejvýš žádoucí zakazovat přerušení na nejkratší možnou dobu, je celý program třeba navrhovat s ohledem na tuto potřebu. Vazeb mezi rutinu přerušení a kódem na popředí by mělo být co nejméně, například rutina příjmu znaku by mohla někde uložit znak nebo řetězec a jen signalizovat událost příchodu dat pro běh na popředí, který by měl data co nejrychleji někam převzít a po tuto dobu zakázat přerušení, aby se mu data nezměnila pod rukama. Složitější možnost je jen signalizovat, že si přebírám data, ale potom musí být driver schopen odložit rozpracovanou IO operaci do fronty a ve spolupráci s během na popředí se k ní vrátit po uvolnění dat. Tahle technika byla poprvé uvedena na OS RSX11 firmou DEC a dočtete se o ní na
http://en.wikipedia.org/wiki/Talk%3ARSX-11
jako o "fork queue" nebo i jinde při hledání hesla "RSX11 fork".
Sežeňte si něco o multiprogramování, o threadech a jejich synchronizaci, pokud tomu chcete porozumět.
Napsal: 08 lis 2011, 22:07
od AB1
pokrivnik:
Pardon, špatně jsem přečetl předdělič.
Několik poznámek:
1. Proměnné, které jsou použity v hlavním programu i v přerušení musí být deklarovány jako volatile.
2. Kód přerušení má být co nejkratší, mimo jiné v něm nemá být volání funkcí.
3. V přerušení COMP nemusíš dorovnávat čas impulsů do 2 ms. Servu je jedno, jestli je mezera mezi impulsy 18, 20 nebo 22 ms.
4. Jestli dobře rozumím, tak z PC chceš jenom testovat pohyb serva doleva nebo doprava.
Potom je lepší posílat servu jednobajtové příkazy, ne stringy které pak musíš pracně dekódovat.
Vyzkoušel jsem úspěšně následující kód
Kód: Vybrat vše
//Atmega8 8 MHz
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "uart.h"
#define SERVO1_ON PORTB |= (1<<PB0);
#define SERVO1_OFF PORTB &= ~(1<<PB0);
#define SERVO2_ON PORTB |= (1<<PB1);
#define SERVO2_OFF PORTB &= ~(1<<PB1);
volatile uint8_t servo1_puls, servo2_puls; // 1 - 250 pro puls 1 - 2 ms
//----- main ---------------------------------------------
int main()
{
uart_init();
DDRB = (1<<0) | (1<<1);
TCCR2 = (1<<WGM21); //CTC
TCCR2 |= (1<<CS21)|(1<<CS20); //presc=32, step 4 us
TIMSK = (1<<OCIE2); //enable interrupt COMPARE A
UCSRB |= (1<<RXCIE); //enable interrupt RX
sei();
servo1_puls = 125; //center position
servo2_puls = 125;
while(1)
{
//odkomentuj pro test bez uart
/*
UCSR0B &= ~(1<<RXCIE0); //disable int RX
servo1_puls = 1;
servo2_puls = 1;
_delay_ms(1500);
servo1_puls = 125; //center
servo2_puls = 125;
_delay_ms(1500);
servo1_puls = 250; //right
servo2_puls = 250;
_delay_ms(1500);
*/
}
}
//----- end main -----------------------------------------
ISR(TIMER2_COMP_vect)
{
static uint8_t count;
count++;
switch (count)
{
case 1:
{
SERVO1_ON;
OCR2 = 250;
break;
}
case 2:
{
OCR2 = servo1_puls;
break;
}
case 3:
{
SERVO1_OFF;
SERVO2_ON;
OCR2 = 250;
break;
}
case 4:
{
OCR2 = servo2_puls;
break;
}
case 5:
{
SERVO2_OFF;
OCR2 = 250;
break;
}
case 22:
{
count = 0;
break;
}
}//switch
}
//---------------------------------------------------------------------------
/*
Klávesami A a S posunujeme servo1 na jednu nebo druhou stranu.
Klávesami Q a W řídíme podobně druhé servo.
*/
ISR(USART_RXC_vect)
{
uint8_t recv_byte;
recv_byte = UDR;
if((recv_byte == 'A') || (recv_byte == 'a'))
servo1_puls = servo1_puls + 5;
if((recv_byte == 'S') || (recv_byte == 's'))
servo1_puls = servo1_puls - 5;
if((recv_byte == 'Q') || (recv_byte == 'q'))
servo2_puls = servo2_puls + 5;
if((recv_byte == 'W') || (recv_byte == 'w'))
servo2_puls = servo2_puls - 5;
}