Problem riadenie serva

Diskuze a poradna o programátorech a programování různých obvodů

Moderátor: Moderátoři

Odpovědět
Zpráva
Autor
Uživatelský avatar
pokrivnik
Příspěvky: 79
Registrován: 05 úno 2009, 00:00

Problem riadenie serva

#1 Příspěvek 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 :D
Přílohy
serva.txt
serva
(2.63 KiB) Staženo 198 x
Uživatelský avatar
pokrivnik
Příspěvky: 79
Registrován: 05 úno 2009, 00:00

#2 Příspěvek od pokrivnik »

problem vyrieseny. napisal som si to odznova a chyba bola v navratovej hodnote funkcie. :lol:

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
Uživatelský avatar
Zaky
Příspěvky: 7050
Registrován: 30 říj 2010, 00:00
Bydliště: Praha

#3 Příspěvek 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
Uživatelský avatar
pokrivnik
Příspěvky: 79
Registrován: 05 úno 2009, 00:00

#4 Příspěvek 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?
Uživatelský avatar
mtajovsky
Příspěvky: 3728
Registrován: 19 zář 2007, 00:00
Bydliště: Praha

#5 Příspěvek 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?
Uživatelský avatar
AB1
Příspěvky: 312
Registrován: 23 lis 2009, 00:00

#6 Příspěvek 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
//---------------------------------------------------------------------
Uživatelský avatar
pokrivnik
Příspěvky: 79
Registrován: 05 úno 2009, 00:00

#7 Příspěvek 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ší.
Uživatelský avatar
mtajovsky
Příspěvky: 3728
Registrován: 19 zář 2007, 00:00
Bydliště: Praha

#8 Příspěvek 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.
Uživatelský avatar
AB1
Příspěvky: 312
Registrován: 23 lis 2009, 00:00

#9 Příspěvek 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;
}
Odpovědět

Zpět na „Programování PIC, ATMEL, EEPROM a dalších obvodů“