PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Servo Ansteuerung



KR-500
28.08.2009, 08:10
Hallo liebes Forum

ich hab jetzt mal den Code für Servos aus dem RN-Wissen auf 10 Stück umgeschrieben, im Moment benutze ich einen atmega32 mit 16mhz. Später will ich den code für einen attiny2313 umschreiben. Ich wollte jetzt fragen ob es noch verbesserungs Vorschläge/Kritik gibt. Danke.


#include <avr/io.h>
#include <avr/interrupt.h>


#define F_CPU 16000000
#define SERVOPIN 7
#define SERVOPORT PORTD
#define DDRSERVO DDRD


#define USART_BAUD_RATE 9600
#define USART_BAUD_SELECT (F_CPU/(USART_BAUD_RATE*16L)-1)

#define Low_Servo0 PORTD&=~(1<<PD7)
#define High_Servo0 PORTD|=(1<<PD7)

#define Low_Servo1 PORTD&=~(1<<PD6)
#define High_Servo1 PORTD|=(1<<PD6)

#define Low_Servo2 PORTD&=~(1<<PD5)
#define High_Servo2 PORTD|=(1<<PD5)

#define Low_Servo3 PORTD&=~(1<<PD4)
#define High_Servo3 PORTD|=(1<<PD4)

#define Low_Servo4 PORTD&=~(1<<PD3)
#define High_Servo4 PORTD|=(1<<PD3)

#define Low_Servo5 PORTD&=~(1<<PD2)
#define High_Servo5 PORTD|=(1<<PD2)

#define Low_Servo6 PORTC&=~(1<<PC4)
#define High_Servo6 PORTC|=(1<<PC4)

#define Low_Servo7 PORTC&=~(1<<PC5)
#define High_Servo7 PORTC|=(1<<PC5)

#define Low_Servo8 PORTC&=~(1<<PC6)
#define High_Servo8 PORTC|=(1<<PC6)

#define Low_Servo9 PORTC&=~(1<<PC7)
#define High_Servo9 PORTC|=(1<<PC7)




void USART_Init( unsigned int baud )
{
/* Set baud rate */
UBRRH = (unsigned char)(baud>>8);
UBRRL = (unsigned char)baud;
/* Enable receiver and transmitter */
UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);
/* Set frame format: 8data, 2stop bit */
UCSRC = (1<<USBS)|(3<<UCSZ0) | (1<<URSEL);
}


void USART_Transmit( unsigned char data )
{
while ( !( UCSRA & (1<<UDRE)) );

UDR = data;
}


unsigned char USART_Receive( void )
{
while ( !(UCSRA & (1<<RXC)) );

return UDR;
}



volatile unsigned char servopos[10] = {100,100,100,100,100,100,100,100,100,100};
volatile static int count;
volatile unsigned char buffer=0,servonr=0,rx_count=0;


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


int main (void) {
servo_init();
USART_Init(USART_BAUD_SELECT);
sei();

while (1) {
//------------------------------------------------------------------------------------------------------------------------------
if(count>servopos[0])Low_Servo0;//SERVOPORT&=~(1<<SERVOPIN);
else High_Servo0;//SERVOPORT|=(1<<SERVOPIN);
if(count>servopos[1])Low_Servo1;//PORTD&=~(1<<PD6);
else High_Servo1;//PORTD|=(1<<PD6);
if(count>servopos[2])Low_Servo2;
else High_Servo2;
if(count>servopos[3])Low_Servo3;
else High_Servo3;
if(count>servopos[4])Low_Servo4;
else High_Servo4;
if(count>servopos[5])Low_Servo5;
else High_Servo5;
if(count>servopos[6])Low_Servo6;
else High_Servo6;
if(count>servopos[7])Low_Servo7;
else High_Servo7;
if(count>servopos[8])Low_Servo8;
else High_Servo8;
if(count>servopos[9])Low_Servo9;
else High_Servo9;

//------------------------------------------------------------------------------------------------------------------------------
TCCR2 |= (1<<WGM21) | (1<<CS20);

}

return 0;

}




ISR(TIMER2_COMP_vect)
{


//if(count<2000+servopos)count++;
if(count<1800)count++;
else count=0;
TCCR2 = 0x00;
};



ISR (USART_RXC_vect) {

buffer = UDR;
switch (rx_count){
case 0: if(buffer > 9){
rx_count = 0;
buffer = 0;
}
else {
servonr = buffer;
rx_count = 1;
}
break;
case 1: servopos[servonr]=buffer;
rx_count = 0;
break;
default:rx_count = 0;
break;


}


}


KR-500

radbruch
28.08.2009, 12:02
Hallo

Eine aussagekräftigere Überschrift für deinen Tread wäre sinnvoll ;)

Die Idee die Servos in der Hauptschleife zu steuern und nicht direkt in der ISR scheint auf den ersten Blick recht pfiffig. So könnte man vielleicht den Überlauf der ISR verhindern. Allerdings könnte es vorkommen, dass die ISR die while-Schleife "überholt" und sich dadurch count während der einzelnen Servoabfragen ändert. Dann würden die Servos möglicherweise zittern. Ähnliche Effekte könnte auch der Empfang von Zeichen verursachen. Ich bin mir aber nicht sicher, ob das bei 16MHz-Takt auch auftreten kann. Zum Testen könnte man vielleicht diesen Ansatz verwenden:


In der Hauptschleife:

while (1) {
test='1';
//------------------------------------------------------------------------------------------------------------------------------
if(count>servopos[0])Low_Servo0;//SERVOPORT&=~(1<<SERVOPIN);


//------------------------------------------------------------------------------------------------------------------------------
TCCR2 |= (1<<WGM21) | (1<<CS20); // warum wird hier am TCCR2 gefummelt?
USART_Transmit(test); // eine '0' gesendet wird gab es einen Überlauf!
// UDR = test; // Variante gegen Seiteneffekte von USART_Transmit()?
}

In der ISR:

ISR(TIMER2_COMP_vect)
{
test='0';

Wobei sowas eigentlich ja immer auftreten kann, weil die while-Schleife ja nicht syncron mit der ISR läuft. Vielleicht könnte man das so lösen:

while (1) {
while(count==count_old);
count_old=count;
//------------------------------------------------------------------------------------------------------------------------------


//------------------------------------------------------------------------------------------------------------------------------

if(count!=count_old) UDR = '*'; // Überlauf aufgetreten!

TCCR2 |= (1<<WGM21) | (1<<CS20); // warum wird hier am TCCR2 gefummelt?
}


Zeitsparen könnte man mit einzelnen Variablen für die Servopositionen (pos1, pos2,..) ohne Positionen-Array. Es ist ungünstig, wenn alle Servossignale gleichzeitig starten. Das verursacht hohe Einschaltströme.

Ich hoffe, das ist nicht allzuviel Unsinn. Ich habe das nicht selbst getestet!.

Gruß

mic

[Edit]
Ich habe das "TCCR2 = 0x00;" am Ende der ISR erst jetzt beachtet. Das stoppt doch das Timeing komplett und es wird erst wieder gestartet, wenn die Hauptschleife fertig ist. Das sollte eigentlich gar nicht funktionieren. Spannend, das muss ich wohl doch selbst mal testen...

KR-500
28.08.2009, 14:45
Hi radbruch,

Danke für deine schnelle Antwort.


Ich habe das "TCCR2 = 0x00;" am Ende der ISR erst jetzt beachtet. Das stoppt doch das Timeing komplett und es wird erst wieder gestartet, wenn die Hauptschleife fertig ist. Das sollte eigentlich gar nicht funktionieren. Spannend, das muss ich wohl doch selbst mal testen...

Das Stoppen des Timers hab ich eingefügt, weil ich vorher mit 8Mhz gearbeitet habe und dann der USART Empfangs Interrupt nicht funktionierte. Beim Debuggen hab ich dann festgestellt ,dass es am Timer_interrupt liegt. Weil durch das Stoppen des Timers der Interrupt aber nicht mehr alle 10 µsec eintrifft habe ich im Code die Variable count nicht mehr bis 2000 sondern bis 1800 gezälht, dieser Wert beruht aber nur auf ausprobieren.

Es ist ungünstig, wenn alle Servossignale gleichzeitig starten. Das verursacht hohe Einschaltströme.

Das klingt logisch, davon hab ich glaub ich schon mal gelesen, wie viele Sekunden bzw. millisekunden sollte man denn warten bevor man das Nächste Servosignal ausgibt?
Kritik, Anregungen und Verbesserungsvorschläge sind weiterhin erwünscht.

KR-500

chientech
28.08.2009, 16:59
Hi,

Kannst du was zu deiner HW sagen bzw. nen Schaltplan posten?

Wie hängen die Servos am µC.

mfg
Christoph

KR-500
28.08.2009, 19:52
Hi,

im Moment benutze ich das "ATMEL Evaluations-Board Version 2.0.1" von Pollin und das RS-2 Standardservo von Conrad (Artikel-Nr.: 233751 - 62). Die Servos bekommen die selbe Stromversorgung wie der µc und das Signal geht vom Port über einen 10k Schutzwiderstand zum Servo.

KR-500