Archiv verlassen und diese Seite im Standarddesign anzeigen : Servo machen nicht das, was sie sollen!
Pommestuete
28.04.2009, 19:41
Hallo,
ich arbeite momentan an einem Projekt, bei dem Ich Servos ansteuern muss.
Ich versuche es mit folgendem Code, der einen Servo ansteuern soll:
#define F_CPU 8000000L
#include <avr/io.h>
#inlcude <util/delay.h>
void delay_ms(unsigned int ms)
{
while(ms--)
_delay_ms(1);
}
int main(void)
{
DDRB |= (1<<PB0); //Servo an PB0
while(1)
{
PORTB |= (1<<PB0);
delay_ms(2);
PORTB &= ~(1<<PB0);
delay_ms(15);
}
}
Die Servos wurden, wie im Elektor-Schaltungsheft 07 beschrieben, modifieziert.
Aber die Servos drehen nicht, sonder bewegen sich nur ganz langsam oder gar nicht.
Gruß Pommestuete
Was hast du genau an ihnen verändert?
Funktioniert ein "unveränderter" Servo?
Kannst du das Servosignal mit nem Oszi anschauen?
MFG
Hallo tüte,
mixxers Vorschlag ist richtig: es geht darummöglichst einfach zu unterscheiden, ob du den Fehler auf der Servoseite oder auf der MC Seite suchen sollst.
Genau genommen ist ein Oszi nicht nötig, ein SoftwareOszi reicht da locker aus:
http://www.jbergsmann.at/2-rc-channels.GIF
Damit siehst du genau, womit die Servos gefüttert werden. Ein kleines Programm reicht aus, um zu zeigen, was am Linein Eingang der Soundkarte anliegt. In meinem Beispiel werden die Servosignale von 2 Kanälen gezeigt.
grüsse,
Hannes
Pommestuete
29.04.2009, 17:05
ob ein normaler servo funktioniert:
Nein, er lässt sich nicht stellen.
Er bewegt sich nicht oder schlägt voll aus!
Mit deinem Programm ist auch der Impuls 2ms - das heist voller Ausschlag. Wenn du ihn auf 1,5ms stellst (Pause dann 20ms-1,5ms) dann sollte der Servo auf Mittelstellung fahren!
MFG Mixxer
radbruch
29.04.2009, 18:18
Hallo
Sowohl mit einer wie auch mit zwei Millisekunden Wartezeit funktioniert mein Billigservo mit dem an Port A0 angepassten Programm:
// #define F_CPU 8000000L
#include <avr/io.h>
#include <util/delay.h>
void delay_ms(unsigned int ms)
{
while(ms--)
_delay_ms(1);
}
int main(void)
{
DDRA |= (1<<PA0); //Servo an PA0
while(1)
{
PORTA |= (1<<PA0);
delay_ms(2);
PORTA &= ~(1<<PA0);
delay_ms(15);
}
}
Stimmt denn die Taktfrequenz? Mein RP6 läuft mit Sicherheit mit 8 MHz.
Gruß
mic
Versuch mal das zweite delay auf 18ms zu stellen, vielleicht hilfts was.
Pommestuete
30.04.2009, 15:02
Super, des mit den 18ms Funktioniert!
Aber mein Problem ist jetzt, wie ich 2 Servos mit Timern verwenden kann, da der ablauf ja nur einen Servo steuert.
Ich hab nen Atmega16 mit 5MHz.
Ach ja: Vorher warens 8.
Hallo Tüte,
alles zusammen in eine Schleife: Erst das eine Servo ansteuern, dann das andere Servo am anderen Port. Die Pause wieder so ergänzen, dass eine Runde 20ms dauert.
grüsse,
Hannes
Pommestuete
30.04.2009, 15:31
so inteliigent bin ich auch schon, aber ich will eigentlich ne "intelligenz" in meiner main-schleife machen.
Da hab ich "keine" Zeit mehr, mich um die Servos zu kümmern.
OK,
dann solltest du auch so intelligent sein, und dir die Handhabung von Timern und Interrupts ansehen.
grüsse,
vohopri
radbruch
30.04.2009, 17:29
...oder mal die Suchfunktion bemühen...
Pommestuete
01.05.2009, 10:04
stimmt, ich schau mir mal des an, was in RN-Wissen steht.
Pommestuete
01.05.2009, 10:56
Ich hab jetz mal folgenden Code ausprobiert:
#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+servopos)count++;
else count=0;
};
der hat funktioniert, nachedm ich servopos als integer deklariert hab.
der folgende code für zwei servos funktioniert aber nicht so richtig:
#define SERVOPIN_LEFT (1<<PA1)
#define SERVOPIN_RIGHT (1<<PA2)
#define SERVOPORT PORTA
#define DDRSERVO DDRA
#define LEFT_SERVO 450
#define RIGHT_SERVO 420
volatile unsigned int servopos_left;
volatile unsigned int servopos_right;
void servo_init()
{
TIMSK |= (1<<OCIE2);
TCCR2 |= (1<<WGM21) | (1<<CS20); //Prescale=1, CTC mode
OCR2 = F_CPU/100000; //alle 10µS ein IRQ
};
ISR(TIMER2_COMP_vect)
{
static int count_left;
static int count_right;
if(count_left > servopos_left)
SERVOPORT &= ~SERVOPIN_LEFT;
else
SERVOPORT |= SERVOPIN_LEFT;
if(count_left < 2000 + servopos_left)
count_left++;
else count_left=0;
if(count_right > servopos_right)
SERVOPORT &= ~SERVOPIN_RIGHT;
else
SERVOPORT |= SERVOPIN_RIGHT;
if(count_right < 2000 + servopos_right)
count_right++;
else count_right=0;
};
wenn ich im Interrupt den Teil für den ersten Servo auskommentier läuft der erste, wenn ich den zweiten auskommentier läuft der zweite.
Aber beide gleichzeitig laufen nicht.
Ach ja, ich hab ne main-schleife :) :
int main(void)
{
DDRA = 255;
servo_init();
servopos_left = 500;
servopos_right = 370;
sei();
while(1)
{
}
while(1);
return 0;
}
Pommestuete
02.05.2009, 14:28
Weiß irgendwer, wo der Fehler in meinem Programcode liegt?
Mach die Servo-Ansteuerung lieber über hardware-PWM an den Pins OC1a und OC2.
Lied dir dazu im Datenblatt des Controllers den Artikel "Timer/counter" (oder so ähnlich) durch, dann brauchst du nicht mal Interrupts.
Ich würde Fast-PWM empfehlen (ich finde, das kann man am schönsten einstellen.
Grüße, Yaro
Pommestuete
02.05.2009, 18:46
ah, danke, ich werds ausprobieren
Pommestuete
03.05.2009, 11:34
Ok, ich hab jetz mal nen Code erstell:
DDRD |= (1<<PD4) | (1<<PD5);
TCCR1A |= (1<<COM1A1) | (1<<COM1B1) | (1<<COM1A0) | (1<<COM1B0)| (1<<WGM12) | (1<<WGM11) | (1<<WGM10);
TCCR1B |= (1<<CS11);
Pommestuete
03.05.2009, 11:36
Ok, ich hab jetz mal nen Code erstell:
DDRD |= (1<<PD4) | (1<<PD5);
TCCR1A |= (1<<COM1A1)|(1<<COM1B1)|(1<<COM1A0)|(1<<COM1B0)|(1<<WGM12)|(1<<WGM11)|(1<<WGM10);
TCCR1B |= (1<<CS11);
Jetzt weiß ich bloß nicht, wie ich OCR1A und OCR1B einstellen muss.
radbruch
03.05.2009, 12:23
Hallo
Deine ISR von oben sollte besser so aussehen:
ISR(TIMER2_COMP_vect)
{
static int count;
if(count > servopos_left)
SERVOPORT &= ~SERVOPIN_LEFT;
else
SERVOPORT |= SERVOPIN_LEFT;
if(count > servopos_right)
SERVOPORT &= ~SERVOPIN_RIGHT;
else
SERVOPORT |= SERVOPIN_RIGHT;
if(count < (2000)
count++;
else
count=1;
};Mit dem "count=1" kann man mit servopos=0 den Impuls ganz ausschalten.
Trotzdem stimmt etwas mit dem Takt nicht denn die Werte für die Servopositionen sind viel zu hoch:
servopos_left = 500;
servopos_right = 370;
Bei einem 10µs-Interrupt wären das 5ms oder 3,7ms! Und 2000 als count-Wert würden auch keine 20ms ergeben. Deshalb reicht dir auch das char für servopos nicht aus.
TCCR1A |= (1<<COM1A1) | (1<<COM1B1) | (1<<COM1A0) | (1<<COM1B0)| (1<<WGM12) | (1<<WGM11) | (1<<WGM10);
TCCR1B |= (1<<CS11);Sollen wir nun das Datenblatt wälzen um das Setup des Timers zu kontrollieren? Je nach Timer-Mode und Taktfrequenz/Prescaler werden OCR1A und OCR1B eingestellt. Bei nur zwei Servos erscheint mir die Verwendung der ISR-Lösung einfacher.
Gruß
mic
Schau dir mal diese Seite zur Ansteuerung von Servos an, und richte nach ihr dann deine Timer-Programmierung aus (die Zeiten): https://www.roboternetz.de/wissen/index.php/Servo
Ich empfehle dir das Fast-PWM mit ICR als TOP, damit kannst du die durchlaufszeit des Timers genau einstellen (abhängig vom prescaler), und mit OCR1a/b kannst du dann die high- bzw. low-Zeit des Pins bestimmen.
Wenn dir irgendwelche grundlegenden Begriffe nicht klar sind, dann hab keine Scheu zu fragen.
P.S. du solltest deinen code etwas aufmerksamer schreiben, denn im TCCR1A gibt es kein WGM12 =)
Pommestuete
12.05.2009, 08:55
Ich hab mein Problem gefunden:
Es lag an den Servos, die Elektronik von denen meinte, er stehe um 180° versetzt.
Ich heb jetzt ne neue Elektronik und jetzt funktioniert es auch.
Hallo Tüte,
jetzt wird vielleicht verständlich, was ich im Posting vom 28.04.2009, 20:48 vorgeschlagen habe. Das hätte viel Zeit und Mühe erspart.
grüsse,
Hannes
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.