-         

Ergebnis 1 bis 10 von 10

Thema: Interupt überfordert? ATMega644, Timer0 ..

  1. #1
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    02.05.2004
    Alter
    31
    Beiträge
    388

    Interupt überfordert? ATMega644, Timer0 ..

    Anzeige

    Guten Abend,
    Für die Ansteuerung meiner Servos benutze ich den Timer0 eines ATMega644.
    Das funktioniert auch super.. aber nur bis 3Servos und dann "zerfällt" das Signal.
    Folgend Ausschnitte aus meinem Code:

    Code:
    void servoposH(void)
    {
    
    	//HÜFTEN
    	// (1)
    	if(countH1>=posH1)
    		PORTA &= ~(1<<PORTH1);
    	else
    		PORTA |= (1<<PORTH1);
    	
    
    	// (2)
    	if(countH2>=posH2)
    		PORTA &= ~(1<<PORTH2);
    	else
    		PORTA |= (1<<PORTH2);
    	
    	
    	// (3)
    	//........... -6
    
    };
    
    //INTERUPT
    ISR(TIMER0_OVF_vect) 
    {
    
    	TCNT0 = TIMER;
    	
    	if(countH1<=(1740-posH1))
    	countH1++;
    	else
    	countH1 = 0;	
    	
    	if(countH2<=(1740-posH2))
    	countH2++;
    	else
    	countH2 = 0;
    	
    	if....... 
                    //.....-6
    };
    
    int main(void) 
    {
    	//INITIALISIEREN DER PORTS
    	initialisieren();
    
    	//TIMER LADEN
    	timer_init();		
    
    	//GLOBALE INTERUPTS AKTIVIERT
    	sei();
    
    	posH1 = 135;
    	posH2 = 119;
    	posH3 = 130;
    	posH4 = 127;
    	posH5 = 135;
    	posH6 = 132;	
    
    	//HAUPTSCHLEIFE
    	for(;;)
    	{
    		servoposH();
    					
    
    	}
    
    }
    Das mache ich eigentlich 18mal.
    Also in der funktion servoposH() 6x, servoposSCH() 6x, servoposK 6x.
    Die Lowabfragen mache ich 18x im ISR.
    Ich hoffe ihr versteht was ich meine, die High abfragen sind auf 3 Funktionen verteilt und die Lowabfragen stecken im ISR.

    Der obige Quelltext ist natürlich nicht vollständig.

    Die Variablen posH^n sind mit Testwerten initialisiert. Wie gesagt es funktioniert auch alles.. aber sobald ich mehr als 3 Servos (Sprich abfragen) im Interupt verarbeite geschehen mit meinem Signal die merkwürdigtsen Dinge. Ich habe in meinem Programm die restlichen Abfragen einfach ausgeklammert.
    Mir scheint als sei der Interupt damit überfordert. Kann das sein?

    Der Interupt läuft auf 100kHz, also alle 0.01s einmal. Naja nicht genau, aber in etwa.

    Ursprünglich wollte ich ja alle abfragen im ISR machen aber das klappt niemals.

    Danke fürs lesen und allfällige Inputs
    Ich kann auch gerne mehr Code posten aber ich denke, der Rest ist nicht nötig resp. in Ordnung... also alles initialisiert.

  2. #2
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    24.02.2006
    Ort
    3. Planet eines kleinen Sonnensystems in einem Seitenarm der Milchstraße
    Alter
    63
    Beiträge
    622
    Hi,

    Der Interupt läuft auf 100kHz, also alle 0.01s einmal. Naja nicht genau, aber in etwa.
    Ich hoffe, es sind 0,01ms!

    Ich kann auch gerne mehr Code posten aber ich denke, der Rest ist nicht nötig resp. in Ordnung... also alles initialisiert.
    Ohne mehr Code wird Dir kaum jemand helfen können: es ist schon sehr wichtig, wie die Variablen deklariert und initialisiert sind ... und wie schnell der Prozessor läuft!

    Welche Auflösung möchtest Du erreichen?

    Gruß

    Fred
    Only entropy comes easy. - Anton Checkhov

  3. #3
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Der Mega 644 hat 2 hardware PWM units mit 16 Bit Auflösung. Die eignen sich wunderbar für die Servosteuerung.

    Der Code hier sieht ziehmlich ineffektiv aus, auch wenn man wegen der fehlenden Teile nicht alles versteht. Es geht auch einen Effektiven Code für Software PWM zur Steuerung von mehr Servos zu erzeugen. Das Setzen der IO ports sollte zum Beispiel in der ISR erfolgen, und nicht in der schleife im Hauptprogramm. Wenn die Rechenzeit sehr knapp ist, kann man auch die Servopulse nacheinander erzeugen, also immer nur einen zur Zeit.

  4. #4
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    24.02.2006
    Ort
    3. Planet eines kleinen Sonnensystems in einem Seitenarm der Milchstraße
    Alter
    63
    Beiträge
    622
    Hi Besserwessi,

    das hast Du sehr gut formuliert -- ich habe mich nicht getraut, und bei mir hätte es nicht so diplomatisch geklungen.

    Gruß

    Fred
    Only entropy comes easy. - Anton Checkhov

  5. #5
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    02.05.2004
    Alter
    31
    Beiträge
    388
    Code:
    #include <avr/io.h>         // I/O Port definitions 
    #include <avr/interrupt.h>   // Interrupt macros 
    
    #define F_CPU 16000000 		//CPUTAKT
    
    #define TIMER (256-F_CPU/8/100000)	//TIMER VORLADEN
    
    //------------------------------------------------------------------------------------------------------//
    
    //PORT BEZEICHNUNGEN
    #define PORTH1 PORTA0
    #define PORTSCH1 PORTA1
    #define PORTK1 PORTA2
    
    #define PORTH2 PORTA3
    #define PORTSCH2 PORTA4
    #define PORTK2 PORTA5
    
    #define PORTH3 PORTA6
    #define PORTSCH3 PORTA7
    #define PORTK3 PORTC7
    
    #define PORTH4 PORTC6
    #define PORTSCH4 PORTC5
    #define PORTK4 PORTC4
    
    #define PORTH5 PORTD7
    #define PORTSCH5 PORTC2
    #define PORTK5 PORTC3
    
    #define PORTH6 PORTD4
    #define PORTSCH6 PORTD5
    #define PORTK6 PORTD6
    
    //SERVO POSITIONEN
    volatile int posH1 = 0;
    volatile int posSCH1 = 0;
    volatile int posK1 = 0;
    
    volatile int posH2 = 0;
    volatile int posSCH2 = 0;
    volatile int posK2 = 0;
    
    volatile int posH3 = 0;
    volatile int posSCH3 = 0;
    volatile int posK3 = 0;
    
    volatile int posH4 = 0;
    volatile int posSCH4 = 0;
    volatile int posK4 = 0;
    
    volatile int posH5 = 0;
    volatile int posSCH5 = 0;
    volatile int posK5 = 0;
    
    volatile int posH6 = 0;
    volatile int posSCH6 = 0;
    volatile int posK6 = 0;
    
    //TIMER ZÄHLVARIABLEN
    volatile int countH1 = 0;
    volatile int countSCH1 = 0;
    volatile int countK1 = 0;
    
    volatile int countH2 = 0;
    volatile int countSCH2 = 0;
    volatile int countK2 = 0;
    
    volatile int countH3 = 0;
    volatile int countSCH3 = 0;
    volatile int countK3 = 0;
    
    volatile int countH4 = 0;
    volatile int countSCH4 = 0;
    volatile int countK4 = 0;
    
    volatile int countH5 = 0;
    volatile int countSCH5 = 0;
    volatile int countK5 = 0;
    
    volatile int countH6 = 0;
    volatile int countSCH6 =0;
    volatile int countK6 = 0;
    
    volatile int ISRcount = 0;
    
    //------------------------------------------------------------------------------------------------------//
    
    //TIMER LADEN
    void timer_init(void) 
    {
    	TCCR0B |= (1<<CS01) | (!(1<<CS00)) | (!(1<<CS02));		//PRESCALER 8
    	TCNT0 = TIMER;											//TIMER VORLADEN
    	TIMSK0 |= (1<<TOIE0);									//INTERUPTS AKTIVIEREN
    	
    };
    
    //INITIALISIEREN DER PORTS
    void initialisieren(void)
    {
    	DDRA = 0xFF;		//PINS A AUSGANG
    	PORTA = 0x00;		//PINS A LOW
    	DDRC = 0xFF;		//PINS C AUSGANG
    	PORTC = 0x00;		//PINS C LOW
    	DDRD = 0xFF;		//PINS D AUSGANG
    	PORTD = 0x00;		//PINS D LOW
    
    };
    
    //------------------------------------------------------------------------------------------------------//
    
    //SERVOPOSITIONEN ANFAHREN
    void servoposH(void)
    {
    
    	//HÜFTEN
    	// (1)
    	if(countH1>=posH1)
    		PORTA &= ~(1<<PORTH1);
    	else
    		PORTA |= (1<<PORTH1);
    	
    
    	// (2)
    	if(countH2>=posH2)
    		PORTA &= ~(1<<PORTH2);
    	else
    		PORTA |= (1<<PORTH2);
    	
    	
    	// (3)
    	if(countH3>=posH3)
    		PORTA &= ~(1<<PORTH3);
    	else
    		PORTA |= (1<<PORTH3);
    	
    	/*
    	// (4)
    	if(countH4>=posH4)
    		PORTC &= ~(1<<PORTH4);
    	else
    		PORTC |= (1<<PORTH4);
    	
    	// (5)
    	if(countH5>=posH5)
    		PORTD &= ~(1<<PORTH5);
    	else
    		PORTD |= (1<<PORTH5);
    	
    
    	// (6)
    	if(countH6>=posH6)
    		PORTD &= ~(1<<PORTH6);
    	else
    		PORTD |= (1<<PORTH6);*/
    
    };
    
    void servoposSCH(void)
    {
    	//SCHULTERN
    	// (1)
    	if(countH1>posSCH1)
    		PORTA &= ~(1<<PORTSCH1);
    	else
    		PORTA |= (1<<PORTSCH1);
    	if(countSCH1>(1740-posSCH1))
    		countSCH1 = 0;
    
    	// (2)
    	if(countSCH2>posSCH2)
    		PORTA &= ~(1<<PORTSCH2);
    	else
    		PORTA |= (1<<PORTSCH2);
    	if(countSCH2>(1740-posSCH2))
    		countSCH2 = 0;
    
    	// (3)
    	if(countSCH3>posSCH3)
    		PORTA &= ~(1<<PORTSCH3);
    	else
    		PORTA |= (1<<PORTSCH3);
    	if(countSCH3>(1740-posSCH3))
    		countSCH3 = 0;
    	
    	// (4)
    	if(countSCH4>posSCH4)
    		PORTC &= ~(1<<PORTSCH4);
    	else
    		PORTC |= (1<<PORTSCH4);
    	if(countSCH4>(1740-posSCH4))
    		countSCH4 = 0;
    
    	// (5)
    	if(countSCH5>posSCH5)
    		PORTC &= ~(1<<PORTSCH5);
    	else
    		PORTC |= (1<<PORTSCH5);
    	if(countSCH5>(1740-posSCH5))
    		countSCH5 = 0;
    
    	// (6)
    	if(countSCH6>posSCH6)
    		PORTD &= ~(1<<PORTSCH6);
    	else
    		PORTD |= (1<<PORTSCH6);
    	if(countSCH6>(1740-posSCH6))
    		countSCH6 = 0;
    	
    };
    
    void servoposK(void)
    {
    	//KNIEHE
    	// (1)
    	if(countK1>posK1)
    		PORTA &= ~(1<<PORTK1);
    	else
    		PORTA |= (1<<PORTK1);
    	if(countK1>(1740-posK1))
    		countK1 = 0;
    
    	// (2)
    	if(countK2>posK2)
    		PORTA &= ~(1<<PORTK2);
    	else
    		PORTA |= (1<<PORTK2);
    	if(countK2>(1740-posK2))
    		countK2 = 0;
    
    	// (3)
    	if(countK3>posK3)
    		PORTC &= ~(1<<PORTK3);
    	else
    		PORTC |= (1<<PORTK3);
    	if(countK3>(1740-posK3))
    		countK3 = 0;
    	
    	// (4)
    	if(countK4>posK4)
    		PORTC &= ~(1<<PORTK4);
    	else
    		PORTC |= (1<<PORTK4);
    	if(countK4>(1740-posK4))
    		countK4 = 0;
    
    	// (5)
    	if(countK5>posK5)
    		PORTC &= ~(1<<PORTK5);
    	else
    		PORTC |= (1<<PORTK5);
    	if(countK5>(1740-posK5))
    		countK5 = 0;
    
    	// (6)
    	if(countK6>posK6)
    		PORTD &= ~(1<<PORTK6);
    	else
    		PORTD |= (1<<PORTK6);
    	if(countK6>(1740-posK6))
    		countK6 = 0;
    		
    };
    
    //INTERUPT
    ISR(TIMER0_OVF_vect) 
    {
    
    	TCNT0 = TIMER;
    	
    	if(countH1<=(1740-posH1))
    	countH1++;
    	else
    	countH1 = 0;	
    	
    	if(countH2<=(1740-posH2))
    	countH2++;
    	else
    	countH2 = 0;
    	
    	if(countH3<=(1740-posH3))
    	countH3++;
    	else
    	countH3 = 0;
    	/*
    	if(countH4<=(1740-posH4))
    	countH4++;
    	else
    	countH4 = 0;
    
    	if(countH5<=(1740-posH5))
    	countH5++;
    	else
    	countH5 = 0;	
    	
    	if(countH6<=(1740-posH6))
    	countH6++;
    	else
    	countH6 = 0;
    	
    	
    
    	/*countSCH1++;
    	countSCH2++;
    	countSCH3++;
    	countSCH4++;
    	countSCH5++;
    	countSCH6++;
    	
    
    	countK1++;
    	countK2++;
    	countK3++;
    	countK4++;
    	countK5++;
    	countK6++;*/
    	
    };
    
    //------------------------------------------------------------------------------------------------------//
    
    int main(void) 
    {
    	//INITIALISIEREN DER PORTS
    	initialisieren();
    
    	//TIMER LADEN
    	timer_init();		
    
    	//GLOBALE INTERUPTS AKTIVIERT
    	sei();
    
    	posH1 = 135;
    	posH2 = 119;
    	posH3 = 130;
    	posH4 = 127;
    	posH5 = 135;
    	posH6 = 132;	
    
    	posSCH1 = 130;								
    	posSCH2 = 130;
    	posSCH3 = 130;
    	posSCH4 = 130;
    	posSCH5 = 130;
    	posSCH6 = 130;
    	
    	posK1 = 130;
    	posK2 = 130;
    	posK3 = 130;
    	posK4 = 130;
    	posK5 = 130;
    	posK6 = 130;
    
    	//HAUPTSCHLEIFE
    	for(;;)
    	{
    		servoposH();
    		/*servoposSCH();*/
    		
    				
    
    	}
    
    }
    Das ist alles
    Nicht erschrecken, ist mein Bastelcode in dem ich alles ausprobiere

    Ich kenne die Lösung mit dem getimten ansprechen der Servos... gefällt mir irgendwie nicht, da ich das unglaublch unübersichtlich finde.

    Sollte den das Auswerten und setzten des Ausgangssignals in der ISR möglich sein... rein von der Leistung?
    Ursprünglich hatte ich auch das setzten der Ausgänge in der ISR... das hat aber gar nicht funktioniert. Deshalb habe ich sie rausgenommen damit die ISR sich wenigstens beim hochzählen nicht vertut(Ist das überhaupt ein Wort?).

    Wie würde das den mit der Hardware PWM aussehen?
    Ich habe mich damit noch nie beschäftigt.

    Und ja es sollte eigentlich 0.01ms heissen

    Besten dank für eure Hilfe und Hinweise

  6. #6
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Mit hardware PWM geht für 2 (oder wie viele Kanäle mit hoher Auflösung man hat) Servos ganz einfach. Man stellt einmal den Timer ein auf einen PWM mode, den passenden Vorteiler und die passende Frequenz von ca. 50 Hz. Die PWM Werte für den Motor kann man dann einfach in die OCR1x Register schreiben. Sieht dann ungefähr so aus (hier für Mega88 mit 8MHz)
    Code:
       // timer 1 auf fast PWM, top = ICP , CLK / 8  (für Servos)
       TCCR1A = (1<<COM1A1)+(1<<COM1B1)+(1<<WGM11);
       TCCR1B = (1<<CS11)+(1<<WGM12)+(1<<WGM13);
       ICR1 = 50000;
       OCR1A = motorstop1;  // motor aus (Konstante fuer stop)
       OCR1B = motorstop2;

  7. #7
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    24.02.2006
    Ort
    3. Planet eines kleinen Sonnensystems in einem Seitenarm der Milchstraße
    Alter
    63
    Beiträge
    622
    Hallo,

    Zitat Zitat von hosti
    Sollte den das Auswerten und setzten des Ausgangssignals in der ISR möglich sein... rein von der Leistung?
    eher nicht: Du hast 160 Maschinenzyklen zwischen den Interrupts. Sieh Dir mal im Disassembler oder in der "list"-Datei die Länge Deiner ISR (letzte abgekürzte Version, kompiliert mit -O1) an: >80 Befehle (mir ist klar, dass durch die Sprünge nicht alle ausgeführt werden, aber manche brauchen eben >1 Zyklus) -- das kann also nicht mehr funktionieren, wenn Du es um nur ein bis zwei Kanäle erweiterst, zumal allein "servoposH" auch schon >30 Befehle lang ist!

    Also: 16 Bit Timer verwenden, die Servo-Signale per PWM erzeugen, wie von Besserwessi gezeigt, oder die Impulse gestaffelt ausgeben. Wie viele Servos sollen es denn insgesamt werden? Den OVF-Interrupt zu verwenden, wenn Du einen Compare-Interrupt zur Verfügung hast, ist nicht besonders effizient, wie Du gesehen hast. Beim nächsten Anlauf klappt es besser!

    Gruß

    Fred
    Only entropy comes easy. - Anton Checkhov

  8. #8
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Mit etwas Optimierung sollte man auch ein paar PWM Kanäle in software erzeugwn können. Für die PWM-erzeugung reicht ein Zähler, der dann mit den PWM Werten verglichen wird. Das Abziehen des Nullwertes braucht man auch nicht jedes mal neu machen. da man ohnehin kaum besser als 10 µs Auflösung werden kann, sollten schon 8 Bit Auflösung reichen.

    Wenn man die Pulse nacheinander macht kann man etwas mehr auflösung gewinnen, wobei ich nicht sicher wäre dass die normalen Servos das mechanisch auch umsetzten können.

  9. #9
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    02.05.2004
    Alter
    31
    Beiträge
    388
    Danke für die Antworten,

    Laut Datenblatt hat der ATmegha644 6 PWM-kanäle
    Nur dann kann ich Hardware PWM ja nicht nutzen bei 18 Servos?

    Welchen Vorteil bietet den der 16Bit Timer? Durch die höhere Auflösung hat die ISR nur noch weniger Zeit, oder versteh ich das falsch?

    Der Compare Interupt ist ja die Hardware PWM.
    Kann ich mir die den so umformen, das ich sie auf all meine Servos anwenden kann? Würde verm. wieder auf Zeitlich versetztes arbeiten Rauslaufen..

  10. #10
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Ein 16 Bit interrupt kann den ganzen Puls mit der 20 ms Periode in eins erzeugen und hat trotzdem noch eine gute Auflösung (besser als per Software möglich).

    Mit 8 Bit timern müßte man nur den Puls selber über den Timer PWM erzeugen, die Pause wäre extra, mit etwas Rechenzeit.

    Für 18 Kanäle wird man wohl kaum darum kommen nicht alle Pulse gleichzeitig zu erzeugen. Allerdings kann man auch kaum alle hintereinander machen, denn 18 x 2 ms sind etwas lang. Welcher Timer dann die Interrupts für Software PWM erzeugt ist relativ egal, das kann auch gut ein 8 Bit Timer sein.

Berechtigungen

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