Elektroda.pl
Elektroda.pl
X
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

[ATmega8][C] Sterowanie silnikiem krokowym.

polprzewodnikowy 16 Jan 2010 00:21 5694 16
  • #1
    polprzewodnikowy
    Level 26  
    Witam, stworzyłem taki program do sterowania silnika krokowego:

    //Sterowanie silnikiem krokowym
    //Ver 1.0 beta
    //Tranzystory na wyjściu PORTD
    #define F_CPU 1000000L
    #include <avr/io.h>
    #include <util/delay.h>
    
    int main(void)
    {
      // PD.1 - PD.4 jako wyjścia
      DDRD = 0x0f;
      //PORTC jako wejścia
      DDRC = 0x00;
      //Przycisk do 1 i 2 pinu PORTC
      PORTC = 0x03;
    
      while(1)
      {
         if(!(PINC & 0x01))
         {
         PORTD = 0x08;  //Włącz PD.4
         _delay_ms(10); //Opóźnienie
         PORTD = 0x04;  //Włącz PD.3
         _delay_ms(10); //Opóźnienie
         PORTD = 0x02;  //itd...
         _delay_ms(10);
         PORTD = 0x01;
         _delay_ms(10);
         }
         if(!(PINC & 0x02))
         {
         PORTD = 0x01;
         _delay_ms(10);
         PORTD = 0x02;
         _delay_ms(10);
         PORTD = 0x04;
         _delay_ms(10);
         PORTD = 0x08;
         _delay_ms(10);
         }
      }
    }


    Jak pewnie wiadomo jestem początkujący nie wiem czy dobrze napisałem ten program, niestety nie mam teraz dostępu do ATmegi8 więc nie sprawdzę tego na żywo. Z góry dziękuję za odpowiedzi, pozdrawiam, Polprzewodnikowy.
  • Helpful post
    #2
    Karol966
    Level 30  
    Generalnie powinno to działać aczkolwiek mam 2 propozycje:
    -zadeklaruj sobie zmienną np Time i wywołuj opóźnienie jako _delay_ms(Time) bądź utwórz własną funkcję opóźnienia, w której co najwyżej wykorzystasz _delay_ms() lecz będziesz robił to tak by zmieniać prędkość obrotu.
    Ponadto proponuje napisać funkcje dla pracy pół krokowej - silnik będzie pracował płynniej.
    Pozdrawiam
  • #3
    polprzewodnikowy
    Level 26  
    Poprawiłem nieco kod i dodałem obsługę co pół kroku:
    //Sterowanie silnikiem krokowym 
    //Ver 1.1 beta 
    //Tranzystory na wyjściu PORTD 
    #define F_CPU 1000000L 
    #include <avr/io.h> 
    #include <util/delay.h> 
    
    int main(void) 
    {
    int czas = 10; 
      DDRD = 0x0f;  // PD.1 - PD.4 jako wyjścia
      DDRC = 0x00;  //PORTC jako wejścia 
      PORTC = 0x03; //Przyciski do 1 i 2 pinu PORTC 
      while(1) 
      { 
         if(!(PINC & 0x01)) 
         { 
         PORTD = 0x09;     //Włącz PD.4 
         _delay_ms(czas);  //Opóźnienie 
         PORTD = 0x0C;     //Włącz PD.3 
         _delay_ms(czas);  //Opóźnienie 
         PORTD = 0x06;     //itd... 
         _delay_ms(czas); 
         PORTD = 0x03; 
         _delay_ms(czas); 
         } 
         if(!(PINC & 0x02)) 
         { 
         PORTD = 0x03; 
         _delay_ms(czas); 
         PORTD = 0x06; 
         _delay_ms(czas); 
         PORTD = 0x0C; 
         _delay_ms(czas); 
         PORTD = 0x09; 
         _delay_ms(czas); 
         } 
      } 
    }


    Czy o to chodziło?
  • #4
    Karol966
    Level 30  
    Zakładając, że Twoje wyjścia portu D to odpowienio
    
    PD.1 - wy_A
    PD.2 - wy_B
    PD.3 - wy_C
    PD.4 - wy_D
    

    dla pracy pól krokowej musisz zrealizować sekwencję:
    
    A
    AB
    B
    BC
    C
    CD
    D
    DA
    
    

    Przy okazji, możesz sobie zdefiniować 4 ziemne odpowiadające wyjściom sterownika. Będzie to ładnie wyglądało ;)
  • Helpful post
    #5
    janbernat
    Level 38  
    To chyba nie jest sterowanie półkrokowe- ma tylko 4 stany a przy półkrokowym powinno mieć 8.
    1-1i2-2-2i3-3-3i4-4-4i1 itd.
    P.S.
    tak jak napisał karol 966.
    P.S2
    I żadne opóźnienia nie są potrzebne.
    Bo jak dołożysz coś do swojego programu to będzie chodził strasznie wolno.
    Zastosuj przerwanie od któregoś Timera i zmieniaj stany po wystąpieniu przerwania- w głównej pętli albo w przerwaniu.
    Zmieniając wartość początkową w timerze można sobie regulować prędkość.
    Sprawdź na jakie napięcie i prąd jest silnik- bo zaczną się kłopoty ze spalonymi tranzystorami.
    I czy ten silnik może pracować jako unipolarny- czy ma 6 wyprowadzeń albo chociaż 5.
  • Helpful post
    #6
    BoskiDialer
    Level 34  
    Karol966 wrote:
    [...]zadeklaruj sobie zmienną np Time i wywołuj opóźnienie jako _delay_ms(Time) [...]

    Wykluczone! _delay_ms jest makrem które działa tylko na wartościach stałych, użycie zmiennej spowoduje wkompilowanie biblioteki liczb zmiennoprzecinkowych a same opóźnienie będzie się miało nijak do tego oczekiwanego. Time może być stałą preprocesora (#define), nigdy zmienną. Dla zmiennej lepiej napisać osobną funkcję, która w pętli wykonuje _delay_ms(1) lub inny kwant czasu.

    Kod można dość zgrabnie przepisać używając jednej zmiennej która będzie wskazywać jaki jest aktualny krok:
    #define F_CPU 1000000L 
    #include <avr/io.h> 
    #include <util/delay.h> 
    #include <stdint.h>
    
    int main(void) 
    { 
    	// PD.1 - PD.4 jako wyjścia 
    	DDRD = 0x0f; 
    	// PORTC jako wejścia 
    	DDRC = 0x00; 
    	// Przycisk do 1 i 2 pinu PORTC 
    	PORTC = 0x03; 
    
    	// numer kroku
    	uint8_t krok = 0;
    	// tablica kroków
    	static const uint8_t krok_map[4] = { 0x08, 0x04, 0x02, 0x01 };
    	while(1) 
    	{
    		if(!(PINC & 0x01))
    			krok++;
    		if(!(PINC & 0x02)) 
    			krok--;
    		
    		PORTD = krok_map[krok & 3];
    		_delay_ms(10);
    	} 
    }

    Mikrokroki można dorobić zwiększając rozmiar tablicy krok_map, dodając odpowiednie wartości oraz zmieniając "krok & 3" na "krok & 7" (dla 8 wartości w tablicy).

    Jakkolwiek kod z pierwszego postu wydaje się być poprawny.
  • #7
    polprzewodnikowy
    Level 26  
    janbernat wrote:
    To chyba nie jest sterowanie półkrokowe- ma tylko 4 stany a przy półkrokowym powinno mieć 8.
    1-1i2-2-2i3-3-3i4-4-4i1 itd.
    P.S.
    tak jak napisał karol 966.
    P.S2
    I żadne opóźnienia nie są potrzebne.
    Bo jak dołożysz coś do swojego programu to będzie chodził strasznie wolno.
    Zastosuj przerwanie od któregoś Timera i zmieniaj stany po wystąpieniu przerwania- w głównej pętli albo w przerwaniu.
    Zmieniając wartość początkową w timerze można sobie regulować prędkość.
    Sprawdź na jakie napięcie i prąd jest silnik- bo zaczną się kłopoty ze spalonymi tranzystorami.
    I czy ten silnik może pracować jako unipolarny- czy ma 6 wyprowadzeń albo chociaż 5.


    No teraz to trochę namieszałeś, jak napisałem dopiero zaczynam i jeszcze nie wiem to to "timer". Co do silnika ma 6 wyprowadzeń i był sprawdzany z tranzystorami na porcie LPT więc na pewno działa.

    BoskiDialer - Więc nigdy nie można stosować do funkcji "delay" wartości zmiennej? Co do twojego programu rozumiem że jeśli wciśniemy przycisk to silnik zmieni swoje położenie tylko o jeden krok? A co będzie jak przytrzymamy przycisk, czy silnik będzie się wciąż obracał?
  • #8
    janbernat
    Level 38  
    "No teraz to trochę namieszałeś, jak napisałem dopiero zaczynam i jeszcze nie wiem to to "timer"."
    Nic nie namieszałem- ATMega to nie komputer z procesorem Pentium i z ogromną szybkością i pamięcią.
    Jesteś bardzo blisko sprzętu- to jest programowanie niskopoziomowe.
    Nie znam się na C ale poszukaj sobie tutoriala do AVRGcc na temat przerwań.
  • #10
    polprzewodnikowy
    Level 26  
    Czyli sterowanie z obsługą przerwań będzie wyglądać tak?

    //Testowanie silnika krokowego z przerwaniami
    
    #include <avr/io.h>                // dostęp do rejestrów
    #include <avr/interrupt.h>         // biblioteka przerwań
    #include <utils/delay.h>           // obsługa opóźnień
    
    SIGNAL (SIG_INTERRUPT0)   // przerwanie INT0
    {
         PORTD = 0x08;  //Włącz PD.4 
         _delay_ms(10); //Opóźnienie 
         PORTD = 0x04;  //Włącz PD.3 
         _delay_ms(10); //Opóźnienie 
         PORTD = 0x02;  //itd... 
         _delay_ms(10); 
         PORTD = 0x01; 
         _delay_ms(10); 
    }
    SIGNAL (SIG_INTERRUPT1)   // przerwanie INT1
    {
         PORTD = 0x01;  //Włącz PD.1
         _delay_ms(10); //Opóźnienie
         PORTD = 0x02;  //Włącz PD.2
         _delay_ms(10); //Opóźnienie
         PORTD = 0x04;  //itd...
         _delay_ms(10); 
         PORTD = 0x08; 
         _delay_ms(10); 
    }
    
    int main(void)                   // program główny
    {
      DDRD = 0x0f;                   // PD.1 - PD.4 jako wyjścia
      DDRC = 0x00;                   // PORTC jako wejścia
      PORTC = 0x03;                  // podciąganie bitów 1 i 2 PORTC (przyciski)
      GIMSK = _BV(INT0)|_BV(INT1);   // włącz obsługę przerwań Int0 i Int1
      MCUCR = _BV(ISC01)|_BV(ISC11); // włącz generowanie przerwań przez opadające zbocze na Int0 i Int1
      sei();                         // włącz obsługę przerwań
      while(1);                      // pętla nieskończona
    }
  • Helpful post
    #11
    BoskiDialer
    Level 34  
    polprzewodnikowy: _delay_ms może ma coś z funkcji, ale więcej to ma wspólnego z makrem niż funkcją. Przy tych makrach zabronione jest (pod rygorem ogromnych opóźnień oraz wzrostu rozmiaru kodu) używanie wyrażeń, których wartości kompilator nie może policzyć podczas kompilowania - wynika to z tego, że czas opóźnienia jest przeliczany na liczbach zmiennoprzecinkowych na ilość iteracji pętli opóźniającej, jeśli kompilator zna wartość to przeliczy to sam a do kodu wstawi liczbę całkowitą, jeśli nie to obliczenia zostaną przesunięte do kodu. Aby uzyskać zmienną długość opóźnienia - napisać pętlę zawierającą _delay_ms(1) o czym wspomniałem wcześniej.

    Co do mojego kodu jak i tego z pierwszego postu - silnik będzie się obracał cały czas. Sprawdzenie jest typu "czy aktualnie przycisk jest wciśnięty" - jeśli tak, to zmienna "krok" zostanie zwiększona/zmniejszona za każdym razem, kiedy podczas testu przycisk jest wciśnięty. Aby zrobić pojedynczy krok do każdego przyciśnięcia należało by pamiętać poprzedni stan przycisku a kroku dokonywać tylko podczas przejścia 1->0.
  • #12
    User removed account
    Level 1  
  • #13
    polprzewodnikowy
    Level 26  
    Poszukałem informacji o timerze i napisałem taki program, czy będzie działać poprawnie?

    #define F_CPU 1000000L
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <util/delay.h>
    
    SIGNAL (SIG_OVERFLOW1)
    {
         PORTD = 0x08;  //Włącz PD.4 
         _delay_ms(10); //Opóźnienie 
         PORTD = 0x04;  //Włącz PD.3 
         _delay_ms(10); //Opóźnienie 
         PORTD = 0x02;  //itd... 
         _delay_ms(10); 
         PORTD = 0x01; 
         _delay_ms(10);
         TCNT1 = 0xFF00;
    }
    
    int main(void)
    {
         DDRD = 0x0F;                   // wszystkie linie PORTC jako wyjścia
         TIMSK = _BV(TOIE1);            // włącz obsługę przerwań T/C1
         TCNT1 = 0xFF00;                // wartość początkowa T/C1
         TCCR1A = 0x00;                 // włącz tryb czasomierza T/C1
         TCCR1B = _BV(CS10)|_BV(CS12);  // preskaler ck/1024
         sei();                         // włącz obsługę przerwań
         while(1);                      // pętla nieskończona
    }
  • #14
    User removed account
    Level 1  
  • #15
    polprzewodnikowy
    Level 26  
    Dodałem zmienną "stan" i przypisałem jej wartość 1 oraz usunąłem linie związane z "delay.h" ale dalej nie wiem co zrobić.

    #define F_CPU 1000000L
    #include <avr/io.h>
    #include <avr/interrupt.h>
    
    int stan = 1;                       //Zmienna ale gdzie ją wpisać?
    
    SIGNAL (SIG_OVERFLOW1)
    {
         PORTD = 0x08;   //Włącz PD.4 
         PORTD = 0x04;   //Włącz PD.3 
         PORTD = 0x02;   //Włącz PD.2
         PORTD = 0x01;   //Włącz PD.1
         TCNT1 = 0xFF00; //Przeładuj timer
    }
    
    int main(void)
    {
         DDRD = 0x0F;                   // wszystkie linie PORTD jako wyjścia
         DDRC = 0x00;                   // PORTC jako wejścia 
         PORTC = 0x03;                  // podciąganie bitów 1 i 2 PORTC (przyciski)
         TIMSK = _BV(TOIE1);            // włącz obsługę przerwań T/C1
         TCNT1 = 0xFF00;                // wartość początkowa T/C1
         TCCR1A = 0x00;                 // włącz tryb czasomierza T/C1
         TCCR1B = _BV(CS10)|_BV(CS12);  // preskaler ck/1024
         sei();                         // włącz obsługę przerwań
         while(1);                      // pętla nieskończona
    }
  • Helpful post
    #16
    User removed account
    Level 1  
  • #17
    polprzewodnikowy
    Level 26  
    Dziękuję za pomoc, temat można zamknąć.