logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Sterowanie ośmioma serwami na Atmega8 - błędy w kodzie C/C++

thomas_191 03 Kwi 2008 01:09 3971 15
REKLAMA
  • #1 4987617
    thomas_191
    Poziom 11  
    Posty: 64
    Ocena: 8
    Witam
    od dłuższego czasu siedzę nad jednym problemem a mianowicie zrobiłem płytkę na Atmega8 sterującą ośmioma serwami... napisałem kod...
    #include <avr/interrupt.h>
    #include <avr/delay.h>
     
    volatile int counter=0;
    int serwo1, serwo2, serwo3, serwo4, serwo5, serwo6, serwo7, serwo8;
    
    //   volatile unit8_t flagi;
      unsigned char liczt0;
    
     
      ISR(SIG_OVERFLOW0)
     
         {
          TIMSK |= _BV(TOIE2);
          TCNT0=200;
          counter++;
        }
    
       
     int main(void)
      {
       DDRB=0b00000111;
       DDRD=0b11101100;
       DDRC=0x00;
       
     
    
       TIMSK = (1<<TOIE0) | (1<<TOIE2); //elaczenie przerwan
     //  TCNT0=200 ; // od 61 liczy do 255 - przepelnienie
       TCCR0 = _BV(CS00) ; //ust bez preskalera
     
     
       
       sei();
      //   if ((PINC & 0x01) == 0)
    //
    //      serwo7=10;
    //   else
    //      serwo7=22;
    
       
            serwo1=8;
            serwo2=12;
            serwo3=12;
            serwo4=12;
            serwo5=12;
            serwo6=12;
            serwo7=12;
            serwo8=12;
    
       while(1)
       {
          if (counter<=1)
          {
          PORTD |= _BV(2); // SERWO 1
          PORTD |= _BV(3); // SERWO 2
          PORTD |= _BV(5); // SERWO 3
          PORTD |= _BV(6); // SERWO 4         
          PORTD |= _BV(7); // SERWO 5            
          PORTB |= _BV(0); // portb0=1 SERWO 6
          PORTB |= _BV(1); // SERWO 7
          PORTB |= _BV(2); // SERWO 8   
          }
          
             if (counter>=serwo1)
          PORTD &= ~(_BV(2));
    
             if (counter>=serwo2)
          PORTD &= ~(_BV(3));
    
             if (counter>=serwo3)
          PORTD &= ~(_BV(5));
    
             if (counter>=serwo4)
          PORTD &= ~(_BV(6));
    
             if (counter>=serwo5)
          PORTD &= ~(_BV(7));
    
          if (counter>=serwo6)
          PORTB &= ~(_BV(0));
    
          if (counter>=serwo7)
          PORTB &= ~(_BV(1));
    
          if (counter>=serwo8)
          PORTB &= ~(_BV(2));
    
    
          if (counter>=500)
          counter=0;
    
    /*   if(licznik==zadane0)
          PORTD &= ~(_BV(6));
       if(licznik==zadane0)
          PORTD &= ~(_BV(7));
       if(licznik==zadane0)
          PORTB &= ~(_BV(0));
       if(licznik==zadane1)
          PORTB &= ~(_BV(1));      
       if(licznik == zadane2)
             PORTB &= ~(_BV(2));
       if(licznik == 100)
          {
          TIMSK &= ~(_BV(TOIE2));
          TCCR2 = 0;
          licznik = 0;
          }                        */
       }
    }
    
    

    Problem polega na tym ze serwa chodzą ładnie ale nie dla każdej zadanej pozycji(dla niektórych drgają).
    jak zmieniam wartość TCNT0 (przy TCNT0=200 jeszcze w miarę działa, przy 230 już nie działa praktycznie nic, nie widać nawet nic na oscyloskopie) na większa w celu zwiększenia częstotliwości przerwania to procesor prawdopodobnie nie nadąża wykonywać instrukcji warunkowych.
    Chcemy zwiekszyc częstotliwość przerwań w celu możliwości sterowania serwem z większa dokładnością(mniejszy kąt), aby zmienne serwo1 .... były rzędu kilkuset. Kiedy te zmienne są w okolicach 10-20 wtedy zmiana o 1 licznika (count) wywołuje zbyt duży ruch serwa.
    Co zrobić aby móc sterować serwem z większą precyzją i żeby program nie głupiał? Czy moje podejrzenia są słuszne, że procek nie nadąża wykonywać instrukcji?
  • REKLAMA
  • #2 4987654
    asembler
    Poziom 32  
    Posty: 2099
    Pomógł: 123
    Ocena: 11
    Jezeli ustawisz na 230 to masz 26 taktów na wykonanie przerwania, zakladam ze preskaler 't0' ustawiony na 1/1. Czyli słusznei podejrzewasz ze sie procek niewyrabia, ale to ty mu dajesz w kość bo wychodzi na to z przerwanie od licznika T0 przychodzi Fosc/26=..... podlicz i zobaczysz co wyjdzie.
  • #3 4988892
    thomas_191
    Poziom 11  
    Posty: 64
    Ocena: 8
    Cytat:
    preskaler 't0' ustawiony na 1/1
    tak bo chcialem uzyskac jak najmniejsza dokladkosc (regulacje kata wychylenia)


    ok czestotliwosc taktowania to 10 MHz czyli wychodzi okolo 384615 ... wiec jak mam rozwiązać ten problem zeby bylo dobrze?? bo jak zmieniam np.serwo1=8; na serwo1=9; to serwo wychyla sie o za duzy kąt (jak dla mnie) a jak uzyskac mniejszy kat wychylenia...??
  • REKLAMA
  • #4 4988909
    asembler
    Poziom 32  
    Posty: 2099
    Pomógł: 123
    Ocena: 11
    Jak rozumiem to ty robisz poprostu PWM programowe?
    Jezeli tak to trzeba zastosowac inny algorytm który przy przerwaniu 40 000 bedzie mial czestotliwosc 5000 hz przy 8 bitach
  • #5 4989045
    thomas_191
    Poziom 11  
    Posty: 64
    Ocena: 8
    to nie jest PWM... pAtmega8 ma chyba tylko 3 PWM a ja chce zaprogramowac 8 serw wiec sie nie da ... wiec wyjscia mam jak widac w kodzie(port B i D)
    DDRB=0b00000111; 
       DDRD=0b11101100; 
       DDRC=0x00;
    a serwa
    PORTD |= _BV(2); // SERWO 1 
          PORTD |= _BV(3); // SERWO 2 
          PORTD |= _BV(5); // SERWO 3 
          PORTD |= _BV(6); // SERWO 4          
          PORTD |= _BV(7); // SERWO 5            
          PORTB |= _BV(0); // portb0=1 SERWO 6 
          PORTB |= _BV(1); // SERWO 7 
          PORTB |= _BV(2); // SERWO 8 

    wyjscia na potrach ... wiec tu nie chodzi o PWM...
  • #6 4989068
    asembler
    Poziom 32  
    Posty: 2099
    Pomógł: 123
    Ocena: 11
    A 8 PWM by załątwiło sprawę?
    Opisz moze jakie te serwa są? bo nic o nich nie wiadomo.
    Swoją drogą jak doczytasz uważnei to ja mówiłem o PWM programowych a ich moze miec 8 przy 10 bitach mysle ze 1000 kroków to wystarczy w zupelnosci
  • #8 4989104
    asembler
    Poziom 32  
    Posty: 2099
    Pomógł: 123
    Ocena: 11
    Czyli jednak serwa sterowane za pomocą PWM?
    Da sie zrobic na Atmega8 jak napisałem wyzej. Jezeli to do robota to faktycznie wystarczy 8 bitów rozdzielczosci. To ci zostanie jeszcze pamieci na kolejne PWM bo tam ktos pisał ze potrzebnych jest 12 serwomechanizmów do takiego robota kroczącego.
  • REKLAMA
  • #9 4989173
    thomas_191
    Poziom 11  
    Posty: 64
    Ocena: 8
    nie da sie sterowac atmega8 osmioma serwami za pomoca PWM... zgodnie z dokumentacja atmega8 ma 3 PWM ...
    – Three PWM Channels
    ... wiec... no chyba dobrze rozumie ...

    Dodano po 8 [minuty]:

    tu nie chodzi o PWM ... steruje wyjsciami na portach ...jak dobrac czasy aby to ladnie chodzilo?? bo preskaler juz nie moge zmniejszyc ... tu chodzi o to zwiekszyc częstotliwość przerwań w celu możliwości sterowania serwem z większa dokładnością(mniejszy kąt)
  • REKLAMA
  • #10 4989239
    asembler
    Poziom 32  
    Posty: 2099
    Pomógł: 123
    Ocena: 11
    Jak sterujesz czasami z portów? Czy nie przypadkiem szerokościa impulsu?
  • #11 4989361
    thomas_191
    Poziom 11  
    Posty: 64
    Ocena: 8
    z kodu wszystko widac ... steruje na zasaddzie czasu TCNT0=200; (200 odpowiada 55Hz) a raczej zliczanych impulsow do zadanej pozycji ... wiec jak zmienic TCNT0 by program sie "nie rozjezdzal" ... moim zdaniem przy malych czestotliwosciach petle "if" jeszcze wyrabiaja ale jak im zwieksze TCNT0=230; (czyli nawieksze czestotliwosc) to juz atmega nie wyrabia wiec cos jest z czasem nie tak .... jak z tym sobie poradzic??
  • #12 4989383
    asembler
    Poziom 32  
    Posty: 2099
    Pomógł: 123
    Ocena: 11
    Podejrzewaj sobie dalej. Ani razu nie odpowiadasz na pytania to rób sobie sam.

    Żegnam
  • #13 4989418
    thomas_191
    Poziom 11  
    Posty: 64
    Ocena: 8
    jak nie odp ...wystarczy spojrzec na kod ... asembler jest tak szerokosc impulsu faktycznie zalezy od zadanej pozycji czyli od impulsow zliczanych prze counter to logiczne... ale nie w tym tkwi problem lecz w TCNT0=200... jak zmienic aby wychylenia serw byly bardziej dokladne czyli jak zmienic program aby atmega sie wyrabiala??
  • #14 5083861
    seru83
    Poziom 12  
    Posty: 23
    Pomógł: 1
    Ocena: 7
    hej, probuje z thomasem wspolnie zrobic ten kontroler i moze on troszke niejasno sie wyrazil wiec postaram sie sprostowac.
    padło pytanie

    czy to ma byc programowy pwm

    Tak owszem dązymy dokladnie do tego aby na wyjsciach uzyskac sygnaly takie jak z PWM ale nie mozemy kozystac z PWM gdyz atmega ma tylko 3 PWM a nam potrzeba 8 wyjsc.
    Serwo sterowane jest szerokością imulsu, problem z drgajacymi serwami zniknal po wpisaniu instrukcji if w przerwaniu
    OBECNIE PROGRAM WYGLĄDA TAK
    
    // 8 = neutral		3=noga prawo		14=noga lewo
    #include <avr/interrupt.h> 
    #include <avr/delay.h> 
     
    volatile int counter, licz =0;
    int serwo1, serwo2, serwo3, serwo4, serwo5, serwo6, serwo7, serwo8;
    
    
      
      ISR(SIG_OVERFLOW0) 
      
      	{ 
    		TIMSK |= _BV(TOIE0);
    		TCNT0=250; 
    		counter++
    
    		if (counter>=50)  // 107 to ok 20ms
    		counter=0; 
    
    		if (counter==0)
    		{
    		PORTD = 0b11101100;
    	
    		}
    		
    
    			if (counter==serwo1)
    		PORTD &= ~(_BV(2));
    
    			if (counter==serwo2)
    		PORTD &= ~(_BV(3));
    	} 
    
    // przewanie pomocnicze przerw miedzy ruchem poszczegolnych serw
    						
                                                           ISR(SIG_OVERFLOW2) 
      
      				{ 
    					TIMSK |= _BV(TOIE2);
    					
    					TCNT2=0; 
    					licz++;
    					}					
        
     int main(void) 
      { 
      	
       DDRB=0b00000111; 
       DDRD=0b11101100;
      
       TIMSK = (1<<TOIE0) | (1<<TOIE2); //wlaczenie przerwan 
     
       TCCR0 = _BV(CS00) ; //ust preskalera dla Timer0 na 1
       TCCR2 = _BV(CS22)| _BV(CS21)| _BV(CS20) ; //ust preskalera dla Timer2 na 1024
     
       
       sei();
    
    	
    		  serwo1=8;
    		  serwo2=8;
    
    
    	 void prawa_przod ()
    		{
    		if (licz<100)
    		serwo1=5;
    
    		if (licz==50)
    		{
    		serwo2=5;
    		
    		}
    		if (licz==100)
    		{
    		serwo1=3;
    	
    		}
    		if (licz==150)
    		{
    		serwo2=3;
    	
    		}
    		if (licz==200)
    		{
    		serwo1=14;
    	
    		}
    		if (licz==250)
    		{
    		serwo2=11;
    		
    		}
    		if (licz==300)
    		{
    		serwo1=11;
    		
    		}
    		
    		}
       while(1)
       {
     prawa_przod();
    		
       }
    }
    


    jest to proba wygenerowania ruchu jednej nogi czyli tylko dla 2 serw, docelowo musi byc ciągłe sterowanie 8 serwami.
    problem jest taki ze zanim przerwanie OVERFLOW0 skonczy obsluge instrukcji w nim zawartych to przepelnia sie juz TCNT0 i program nie wyjdzie z przerwania bo juz musi je obsluzyc po raz kolejny.

    Przy 8 serwach instrukcji w przerwaniu OVERFLOW0 jest kilkakrotnie wiecej, jezeli instrukcje warunkowe sa poza przerwaniem to pojawia sie problem z drgajacymi serwami po osiagnieciu zadanej pozycji.



    Cytat:
    A 8 PWM by załątwiło sprawę?

    prawdopodobnie TAK

    Dodano po 4 [minuty]:

    Cytat:
    Swoją drogą jak doczytasz uważnei to ja mówiłem o PWM programowych a ich moze miec 8 przy 10 bitach mysle ze 1000 kroków to wystarczy w zupelnosci

    1000 kroków to by było SUPER !
    tylko czy da sie to uzyskac, jesli tak to jak?
    w optymalnej opcji (pod wzgledem ilosci krokow) udalo nam sie uzyskac max kilkanascie krokow.
  • #15 5084036
    Ch.M.
    Poziom 27  
    Posty: 1009
    Pomógł: 62
    Ocena: 15
    Witam
    Po 1 używaj komentrarzy przy pisaniu kodów, bo Twoje są szczątkowe.
    Po 2 brakuje Ci czasu a odmierzasz go programowo? Przecież w tym samym momencie mógłbyś obliczać inne rzeczy a ty marnujesz czas na zapętlanie nopów...
    Po 3 masz 8 serw i 1/50s na ich ustawienie co daje nam aż 25000 instrukcji na ustawienie każdego serwa i nie wyrabiasz się czasowo?

    Z tego co pamiętam to regulując wypełnienie czasu w zakresie 1-20ms osiągamy różny kąt wychylenia serwa, ale faktycznie jest to nieliniowe i przy małych zmianach otrzymujemy na początku duży skok a pod koniec zakresu róznica jest już nieduża. Zwiększenie rozdzielczości do 1000 wcale nie jest głupim pomysłem i można o to się pokusić.

    Ja bym napisał program w ten sposób:
    Timer 8bitowy odlicza nam ok.25tys taktów (preskaler 256 i liczymy do 97) Jeśli lubisz przerwania to ustawiasz tutaj przerwanie przełączające kolejne 8 "liczników" cyklicznie
    Timer 16bitowy zlicza nam od 1 do XXX i jest to nasz zadajnik wychylenia serwa (preskaler np.32 i zliczamy do ok.750 lub preskaler 16 i zliczamy do 1500) i po osiągnięciu zadanej wartości wyzwalane jest przerwanie. Oczywiście czas zliczania jest równocześnie stanem wysokim na aktywnym obecnie "liczniku". Po osiągnięciu zadanej wartości przechodzimy do oczekiwania na przerwanie od licznika 8bitowego, ktore informuje nas o kolejnej szczelinie czasowej dla "liczników" i zliczanie rozpoczynamy od nowa.

    Pozdrawiam
  • #16 5388838
    cianek
    Poziom 11  
    Posty: 5
    Witam.
    Nie wiem czy ktoś jeszcze rozgląda się w tym temacie, ale postanowiłem zapytać. Napisałem program zgodnie ze wskazówkami od Ch.M. (wielkie dzięki) i wyszło mi coś takiego jak ponieżej. Jednak mogę wysterować serwem nie do końca i zostaje mi ok 20-30 stopni do pełnego wychylenia. Z obserwacji na oscyloskopie wynika, że czas tego wychylenia to ok 2ms, a powinno być ok2,5 ms lecz ta wartość jest już przy FF. Sądzę, że to może być kwestia wpisania pełnych 16 bitów, ale nie wiem czy to coś da i jak to zrobić albo kwestia dobrania odp preskalerów.
    Byłbym wdzięczny jeśli ktoś mógłby mi pomóc:)
    Oto kod:
    
    //dla 16 MHz - Atmega8
    //40 - 180stopni, FF - ok.30 stopni:/
    #include <avr/io.h>
    #include <avr/interrupt.h>
    
    unsigned char volatile polozenie[8] = {0x40,0x30,0x70,0x90,0x40,0xFF,0xFF,0xFF}; // wartosc impulsu do serwa
    unsigned char volatile wartosc[8] = {0x04,0x04,0x04,0x08,0x10,0x20,0x40,0x80}; 	// kolejne numery portu D
    int numer; 
    
    
    ISR(TIMER2_COMP_vect)
    {
    if (numer==8) numer=0;
    
    if (polozenie[numer]>0)
      {
       OCR1AL = polozenie[numer];
       numer++; 
     }
    
    TCCR1B|=_BV(CS11);
    TCNT1L=0x00;
    PORTD=wartosc[numer];
    }
    
    
    ISR(TIMER1_COMPA_vect)
    {
       PORTD=0x00;
       TCCR1B=(0<<CS11);
       TCNT1L=0x00;
    }
    
    
    void main(void)
    {
    // Ustawienia licznika 2   
    TCCR2=_BV(WGM21)|_BV(CS22)|_BV(CS20); 	// kasowanie timera przy zrównaniu (CTC) | preskaler 128
    OCR2= 0x12;								// wartość porównywana do TCNT2 i przerwanie
       
    //Ustawienia licznika 1
    TCCR1B=_BV(CS11)/*|_BV(CS10)*/|_BV(WGM12);// preskaler na 8 | CTC z OCR1A
    OCR1AL=0x00;								// porównywane z TCNT1
       
    //Odblokowanie przerwan   
       TIMSK=_BV(OCIE2)|_BV(OCIE1A);
       TIFR =_BV(OCF2)|_BV(OCF1A);
       sei();
       DDRD=0xFC;
    
       while(1)
       {
       }
    } 
    
    

Podsumowanie tematu

LABEL_AI_GENERATED
Dyskusja dotyczy problemów z programowym sterowaniem ośmioma serwomechanizmami za pomocą mikrokontrolera Atmega8, który posiada tylko trzy sprzętowe kanały PWM. Autor próbuje realizować sterowanie serwami poprzez generowanie impulsów o zmiennej szerokości na wyjściach portów B i D, wykorzystując przerwania timera 0 i manipulację rejestrem TCNT0. Problemy pojawiają się przy zwiększaniu częstotliwości przerwań (np. TCNT0=230), co powoduje, że mikrokontroler nie nadąża z obsługą przerwań i precyzyjnym sterowaniem kątem wychylenia serw. Wskazano, że programowe PWM wymaga odpowiedniego algorytmu i zarządzania czasem, a także że rozdzielczość sterowania kątem jest ograniczona przez częstotliwość przerwań i czas procesora. Zaproponowano użycie timerów 8- i 16-bitowych z preskalerami do cyklicznego przełączania kanałów i precyzyjnego odmierzania szerokości impulsów. W dyskusji pojawiły się sugestie dotyczące zwiększenia rozdzielczości do 1000 kroków oraz optymalizacji kodu, aby mikrokontroler mógł obsłużyć wszystkie serwa bez opóźnień. Przykładowy kod i obserwacje oscyloskopowe wskazują na konieczność dopasowania wartości preskalerów i rejestrów, aby uzyskać pełny zakres wychylenia serw. Serwomechanizmy sterowane są szerokością impulsu, a nie sprzętowym PWM, co wymaga precyzyjnego odmierzania czasu w przerwaniach. Model serw używany w projekcie to Tower Pro SG-90 9g.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA