-         

Ergebnis 1 bis 4 von 4

Thema: atmega16 3 Servos ansteuern Timer0

  1. #1
    Benutzer Stammmitglied
    Registriert seit
    05.04.2008
    Beiträge
    40

    atmega16 3 Servos ansteuern Timer0

    Anzeige

    SMARTPHONES & TABLETS-bis zu 77% RABATT-Kostenlose Lieferung-Aktuell | Cool | Unentbehrlich
    Hallo,

    ich habe bereits ein Programm geschrieben mit dem ich einen Servo einwandfrei ansteuern kann.
    Nun habe ich versucht es so zu verändern das ich 3 Servos gleichzeitig ansteuern kann.
    Eigentlich soll das Programm folgendes tun:
    1. Signalpin von Servo1 auf High
    2. Warten bis dessen Stellzeit rum
    3. Signalpin Servo 1 auf Low
    4. Signalpin 2 auf High
    ...
    nach dem 3. Servo soll er nochmal so lange warten, das 20ms für Servo1 voll sind. Und dann wieder mit Servo 1 anfangen.
    Aber iwie reagieren die Servos garnicht (außer beim einschalten des Stroms).

    Entdeckt iwer den Denkfehler in meinem Programm? Und wie gesagt die Zeiten müssten stimmen.

    Danke, Knipser.

    Code:
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <stdint.h>
    #include <math.h>
    
    #define F_CPU       16000000UL
    
    #define BAUD        9600UL
    #define UBRR_BAUD   ((F_CPU/(16UL*BAUD))-1)
    
    #define SERVO_DDR1		DDRA
    #define SERVO_PORT1		PORTA
    #define SERVO_PORTPIN1	PA0
    
    #define SERVO_DDR2		DDRA
    #define SERVO_PORT2		PORTA
    #define SERVO_PORTPIN2	PA1
    
    #define SERVO_DDR3		DDRA
    #define SERVO_PORT3		PORTA
    #define SERVO_PORTPIN3	PA2
    
    #define LED_DDR			DDRD        
    #define LED_PORT		PORTD       
    #define LED_PORTPIN		PD7         
    
    volatile uint16_t z,q,p;						
    volatile uint8_t t;							
    volatile uint16_t servo1,servo2,servo3;
    
    TIMER0_interrupt_init(void){
    	z = 0;
    	TCNT0 = 0;
    	OCR0 = 9;
    	TCCR0 = 0x0a;
    	TIMSK |= (1<<OCIE0);            //Interrupt aller 1/200ms  ->200 je ms
    	sei();
    	}
    
    ISR(TIMER0_COMP_vect){
    	z++;
    
    	}
    
    
    int main(void)
    {
        // USART initialisieren
        uart_init();
    	
    	TIMER0_interrupt_init();
    	
    	t = 1;
    		
    	servo1 = 255;
    	servo2 = 255;
    	servo3 = 355;
    	
    	LED_DDR |= (1<<LED_PORTPIN);
    	
    	LED_PORT &= ~(1<<LED_PORTPIN);
    	
    	SERVO_DDR1 |= (1<<SERVO_PORTPIN1);
    	SERVO_DDR2 |= (1<<SERVO_PORTPIN2);
    	SERVO_DDR3 |= (1<<SERVO_PORTPIN3);
    	
    	SERVO_PORT1 |= (1<<SERVO_PORTPIN1);
    
        sei();
    
        while (1){		
    		if (t==1 && z==servo1){
    			t=2;
    			z=0;
    			SERVO_PORT1 &= ~(1<<SERVO_PORTPIN1);
    			SERVO_PORT2 |= (1<<SERVO_PORTPIN2);
    			LED_PORT |=(1<<LED_PORTPIN);			
    			}
    		if (t==2 && z==servo2){
    			t=3;
    			z=0;
    			SERVO_PORT2 &= ~(1<<SERVO_PORTPIN2);
    			SERVO_PORT3 |= (1<<SERVO_PORTPIN3);
    			}
    		if (t==3 && z==servo3){
    			t=4;
    			z=0;
    			SERVO_PORT3 &= ~(1<<SERVO_PORTPIN3);
    			q = (4000-servo2-servo3);
    			}
    		if (t==4 && z==q){
    			z = 0;
    			t = 1;
    			SERVO_PORT1 |= (1<<SERVO_PORTPIN1);			
    			}
    		if (z==4000){
    			z = 0;
    			t = 1;
    			SERVO_PORT1 |= (1<<SERVO_PORTPIN1);
    			}
    		return 0;
    	}
    }

  2. #2
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    12.06.2006
    Beiträge
    473
    Hi Knipser,

    eine Macke ist in den IF Abfragen:

    if (t==1 && z==servo1)

    Das muss

    if ((t==1) && (z==servo1)) heißen.

    Die einzelnen Statements müssen für sich in Klammern stehen, damit daraus eine eigenständige Aussage wird.

    Es währe auch gut, wenn du ein paar mehr Worte zu deinem Programm verlieren würdest.
    So wie ich das sehe, schaltest du den Kanal für den nächsten Servo ein, wenn du den vorherigen ausschaltest. Das hat natürlich zur Folge, dass sich die Einschaltpunkte aller Servos außer des ersten komultativ (sorry für das Wort^^) in Abhängigkeit ALLER vorrangegangenen Pulsdauern verändern.
    Das heißt, du musst nach jeden Servo noch eine Abfrage machen, ob die 2ms voll sind.

    Und noch was. Bau die math.h nur ein, wenn es sich nicht verhindern lässt. Mit den Controlern, die wir hier benutzten, frißt das richtig Resourcen. Letztendlich bedeutet alles was über Strichrechnung hinausgeht einen erheblichen Aufwand für die kleinen Dinger. Deswegen sein auch sehr vorsichtig mit Kommarechnung. Aber das währe ein eigener Thread.

    mfg,
    The Man

  3. #3
    Moderator Robotik Visionär Avatar von radbruch
    Registriert seit
    27.12.2006
    Ort
    Stuttgart
    Alter
    54
    Beiträge
    5.781
    Blog-Einträge
    8
    Hallo

    Das muss

    if ((t==1) && (z==servo1)) heißen.
    Ähnlich wie "Punkt vor Strich" gibt es in C die Rangfolge der Operatoren:
    http://www.hs-augsburg.de/~sandman/c...Anhang_000.htm

    Da "gleich" (==) vor "logisches Und" (&&) steht und deshalb dessen Bindung höher ist dienen die Klammern nur der Übersichtlichkeit. Der Kompiler übersetzt beides gleich.

    TIMER0_interrupt_init(void){
    z = 0;
    TCNT0 = 0;
    OCR0 = 9;
    TCCR0 = 0x0a;
    TIMSK |= (1<<OCIE0); //Interrupt aller 1/200ms ->200 je ms
    sei();
    }
    Wo wird der CTC-Mode des Timers eingestellt? Wie sieht im Vergleich der funktionierende Code für ein Servo aus?

    Gruß

    mic

    Edit: Möglicherweise ist deine Hauptschleife nun zu lang und z wird pro Durchlauf mehrfach erhöht. Das hätte zur Folge das ein Vergleich auf "gleich" nicht immer trifft. Sicherheitshalber solltest du deshalb so prüfen:

    if (t==1 && z>=servo1){

    Besser (und noch blockierender) wäre es so:

    Code:
    ...
        while (1){
          if (t==1) while(z<=servo1);
             t=2;
             z=0;
             SERVO_PORT1 &= ~(1<<SERVO_PORTPIN1);
             SERVO_PORT2 |= (1<<SERVO_PORTPIN2);
             LED_PORT |=(1<<LED_PORTPIN);
    
          if (t==2) while(z<=servo2);
             t=3;
             z=0;
             SERVO_PORT2 &= ~(1<<SERVO_PORTPIN2);
             SERVO_PORT3 |= (1<<SERVO_PORTPIN3);
    
          if (t==3) while(z<=servo3);
             t=4;
             z=0;
             SERVO_PORT3 &= ~(1<<SERVO_PORTPIN3);
             q = (4000-servo2-servo3);
    
          if (t==4) while(z<=q);
             z = 0;
             t = 1;
             SERVO_PORT1 |= (1<<SERVO_PORTPIN1);
    
    /*      if (z==4000){
             z = 0;
             t = 1;
             SERVO_PORT1 |= (1<<SERVO_PORTPIN1);
    */
          }
    ...
    (ungetestet!)

    Atmel’s products are not intended, authorized, or warranted for use
    as components in applications intended to support or sustain life!

  4. #4
    Benutzer Stammmitglied
    Registriert seit
    05.04.2008
    Beiträge
    40
    Hallo

    erstmal ein großes Danke für eure Antworten .

    -also der Interrupt ist der gleiche wie bei meinem funktionierenden Servoprogramm
    -der CTC-Mode wird mit

    Code:
    TCCR0 = 0x0a;
    aktiviert ( die Zeile stellt gleichzeitig noch den Prescaler auf 8 )

    @The Man
    -ja die Einschaltpunkte aller Servos verändern sich, aber das ändert ja nicht die Zeit die der Servo high geschaltet wird
    -ich gebe zu das die 20ms eher ein circa Wert sind
    -vl. ist auch das der Fehler

    Also ich werde mal den Code von Radbruch ausprobieren und auch mal versuchen die Hauptschleife zu kürzen und die 20ms genauer einzuhalten.
    Kann es aber leider nicht heute machen, da ich nicht zu Hause bin.

    Nochmals Danke,

    Knipser

    Fast vergessen:
    Ich hatte auch schonmal versucht die Servos gleichzeitig anzusteuern. Und zwar wollte ich nach 20ms alle 3 Signalpins auf High schalten und dann halt 3 If-Schleifen die dann nach der jeweiligen Zeit für die einzellnen Servos die Pins wieder auf Low ziehen.
    Könnte das auch klappen?
    Problem wäre wenn die Servos die gleiche Zeit haben.

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •