PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Servo code was mache ich falsch?



MrTaco
10.01.2012, 15:16
#define F_CPU 4000000UL

#include <avr/io.h>
#include <util/delay.h>
#define SERVOPIN 7
#define SERVOPORT PORTD
#define DDRSERVO DDRD

volatile unsigned char servopos;

void servo_init()
{
TIMSK|=(1<<OCIE2);
TCCR2 |= (1<<WGM21) | (1<<CS20); //Prescale=1, CTC mode
OCR2 = F_CPU/100000; //alle 10µS ein IRQ
DDRSERVO|=(1<<SERVOPIN);
};

ISR(TIMER2_COMP_vect)
{
static int count;
if(count>servopos)SERVOPORT&=~(1<<SERVOPIN);
else SERVOPORT|=(1<<SERVOPIN);
if(count<2000)count++; // Die Impulse sollten alle 20ms gesendet werden! 6.2.11 mic
else count=0;
};

int main (void)
{
servo_init();
while(1)
{

}
return 0;
}



Atmega 8 4Mhz
Was ist da falsch es tut sich nichts

sternst
10.01.2012, 15:45
Was ist da falsch es tut sich nichtsEs könnte helfen, die Interrupts zu aktivieren.

MrTaco
10.01.2012, 18:36
#define F_CPU 4000000UL
#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>
#define SERVOPIN 7
#define SERVOPORT PORTD
#define DDRSERVO DDRD

volatile unsigned char servopos;

void servo_init()
{
TIMSK|=(1<<OCIE2);
TCCR2 |= (1<<WGM21) | (1<<CS20); //Prescale=1, CTC mode
OCR2 = F_CPU/100000; //alle 10µS ein IRQ
DDRSERVO|=(1<<SERVOPIN);
};

ISR(TIMER2_COMP_vect)
{
static int count;
if(count>servopos)SERVOPORT&=~(1<<SERVOPIN);
else SERVOPORT|=(1<<SERVOPIN);
if(count<2000)count++; // Die Impulse sollten alle 20ms gesendet werden! 6.2.11 mic
else count=0;
};

int main (void)
{
sei();
servo_init();
while(1)
{

}
return 0;
}
geht dennoch leider nicht danke aber für den tip

MagicWSmoke
10.01.2012, 19:14
Du musst servopos auch passend vorbesetzen. Außerdem dauert Deine ISR min. 69 Takte, der OC2-Int löst aber alle 41 Takte aus. Was sagt uns das ? :D

MrTaco
10.01.2012, 19:44
#define F_CPU 4000000UL
#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>
#define SERVOPIN 7
#define SERVOPORT PORTD
#define DDRSERVO DDRD

volatile unsigned char servopos = 150;

void servo_init()
{

TIMSK|=(1<<OCIE2);
TCCR2 |= (1<<WGM21) | (1<<CS20); //Prescale=1, CTC mode
OCR2 = F_CPU/100000; //alle 10µS ein IRQ
DDRSERVO|=(1<<SERVOPIN);
};



int main (void)
{

DDRD = 0xff;
sei();
servo_init();
while(1)
{

}
return 0;
}

ISR(TIMER2_COMP_vect)
{

static int count;
if(count>servopos)SERVOPORT&=~(1<<SERVOPIN);
else SERVOPORT|=(1<<SERVOPIN);
if(count<2000)count++; // Die Impulse sollten alle 20ms gesendet werden! 6.2.11 mic
else count=0;
};


Ok hier ist jetzt meine neuer Code.
OK ich mus die Interrupts in dem Interrupt deaktivieren ?

wkrug
10.01.2012, 19:50
Soweit ich weiß ist der Timer 2 beim ATMEGA 8 ein 8 Bit Timer.
Also gehen Zahlen von 0 bis 255.
Du läufst alle 10µs in diesen Interrupt, nach 40 Taktzyklen.
Ich denk mal, das der Controller dabei welche verschluckt.

Ich würde eine andere Vorgehensweise nehmen.
1. nimm Timer 1, der hat schon mal 16 Bit.
2. Verwende nicht! den CTC mode, das machts einfacher, geht aber auch mit.
3. Nimm Prescaler 8, dann hast Du eine Auflösung von 2µs, also 5 mal besser als Deine Variante.

4. Definiere eine Variable, die die entsprechende Ausgabephase hochzählt.
Ist sie 0 dann Pause ist sie 1 Kanal 1. Dadurch lässt sich die Routine einfach auf mehrere Kanäle erweitern.

Nehmen wir mal an der Zähler hat die Phase 0 = Pause. Im Comparematch Interrupt wird zum aktuellen Comparematchwert 9000 dazugezählt ( 18ms ) und wieder im Comparematch Register gespeichert. Alle Ausgänge werden auf 0 gesetzt.
Der Phasenzähler wird auf 1 gesetzt.

Im nächsten Comparematch Interrupt wird dann Ausgang 1 gesetzt und die gewünschte Servoimpulslänge zum Comparematchregister dazugezählt.
Z.B.750 für 1,5ms.
Der Phasenzähler wird auf den nächsten Kanal ( 2 ) oder 0 gesetzt, wenn schon alle Kanäle dran waren.

Beim nächsten Comparematch Interrupt wird Kanal 1 abgeschaltet es sind ja jetzt 1,5ms rum, der Ausgang für Kanal 2 eingeschaltet und der Wert für den zweiten Kanal zum Comparematch Wert dazuaddiert. Also z.B. 800 für 1,6ms.
Jetzt auf weiteren Kanal, oder 0 im Phasenzähler setzen.

Nehen wir an du wolltest nur 2 Kanäle, dann folgt nun wieder die allererste Phase mit der Pause und dem Abschalten aller Kanäle.

Auch bei 1ms Impulslänge hast Du 4000 Taktzyklen, bis ein neuer Comparematch auftaucht, bei Dir sind es 40.
Zudem hast Du ein 5x bessere Auflösung der Servoimpulslänge, als bei Deinem Vorschlag.

Die Überläufe die nach den 16Bit also rund 130ms enstehen sind im Normalfall kein Problem, da diese einfach verworfen werden, solange du unsingned int verwendest. Bei signed kanns ein Problem geben, da eine führende 1 als negativer Wert interpretiert wird.

Mit dieser Methode kannst Du maximal 8 Servos ansteuern, die Pause sollte für jeden zusätzlichen Servokanal um 2ms verkürzt werden, weil ein Servosignal ja maximal 2ms haben kann. Wenn Du keine Pause für eine Empfängersynchroniserung brauchst, gehen auch 10 Kanäle.

Der Timer 1 hat 2 Comparematch Einheiten OCR1A und OCR1B. Jede dieser Einheiten kann somit maximal 10 Servos bearbeiten. Also insgesamt 20, das ist mehr als der ATMEGA 8 freie Ausgänge hat.



volatile unsigned char uc_count=0;
unsigned int ui_servo[2]={750,800};

ISR(TIMER1_COMPA_vect)
{ switch(uc_count)
{
CASE 0:
PORTB=0x00; //Alle Ausgänge abschalten Beispiel OCR1A+=ui_servo[1]; //16ms Pause einfügen
uc_count++; //Zählvariable hochzählen
break;

CASE 1:
PORTB=0x01; //Ausgang 1 aktivieren
OCR1A+=ui_servo[1]; //Impulslänge für Servo 1 dazuaddieren
uc_count++; //Zählvariable hochzählen
break;

CASE 2:
PORTB=0x00; //Alle Ausgänge aus
PORTB=0x02; //Ausgang 2 aktivieren
OCR1A+=ui_servo[2]; //Impulslänge für Servo 2 dazuaddieren
uc_count=0; //Wieder mit Pause beginnen
break;
};

};
Der Quelltext mal ohne Gewähr, das da Syntax Fehler drin sind, hab das mal eben aus dem Kopf gemacht.
Die Werte hab ich direkt in den Code geschrieben, man kanns natürlich auch per define zuweisen.