PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Interupt überfordert? ATMega644, Timer0 ..



hosti
03.10.2008, 18:39
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:


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.

fhs
03.10.2008, 18:55
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

Besserwessi
03.10.2008, 19:10
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.

fhs
03.10.2008, 20:50
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

hosti
03.10.2008, 22:53
#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

Besserwessi
03.10.2008, 23:37
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)


// 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;

fhs
04.10.2008, 09:08
Hallo,


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

Besserwessi
04.10.2008, 11:05
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.

hosti
04.10.2008, 11:21
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..

Besserwessi
04.10.2008, 23:32
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.