PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Servo ansteuern mit ein 8 Bit PWM Signal über 122 Schritte?!



Staind
26.10.2011, 02:38
Hallo,

ich bin noch ein ziemlicher Neuling wenn es ums uC programmieren geht. Ich weiß auch das hier ältliche Themen über PWM stehen, wie es funktioniert. Naja wie es funktioniert weiß ich in der Regel schon.
Meine Servos kann ich auch ansteuern, aber halt nicht präzise.

Habe ein Atmega644 (6PWM Ausgänge (2x10Bit/4x8Bit)) der mit einen 16 Mhz Quarzoszillator getaktet ist.

Hier versuche ich mal mein Problem zu beschreiben
Habe wie im unteren Bild, oberes Signal, folgende Impulse. Wenn ich für OCR0A den Wert 8 eingebe (Den Wert OCR0A = 8 kann mit Hilfe eines C# Programmes, welches ich geschrieben habe ändern)
habe ich ein Impuls von 0,6ms und der Servo steht ganz links. Wenn ich dagegen den Wert 39 eingebe steht der Servo ganz rechts. Somit habe ich 31 Schritte um den Servo von links nach rechts zu fahren. Dieses ist mir aber zu grob...

20373

Darum meine Frage: Kann man die Impulse Zählen so das nur jeder 4te Impuls ausgegeben wird? Habe hierzu nochmal ein Bild gezeichnet.
Hier habe ich eine Periode von 4ms. Wenn ich jetzt nur jeden 4ten Impuls durchlasse so kann ich die Servos mit 122 Schritten genauer steuern.

20374

Ist das möglich? Wenn ja wie realisier ich das?

ich gebe euch mal mein Programm mit rein vielleicht könnt ihr da ein wenig dran rumbasteln und das für ein PWM Signal fertig machen. Ober ein Linkverweis, wo genau das beschrieben ist wäre auch nicht schlecht.





//-------------------------------------------------------------------------------------------//
//-------------------------------------------------------------------------------------------//
// //
// Miniprojekt Roboterarm V.1.0 //
// von Thomas B. & André K. //
// //
//-------------------------------------------------------------------------------------------//
//-------------------------------------------------------------------------------------------//

//-------------------------------------------------------------------------------------------
// Include Datein
//-------------------------------------------------------------------------------------------

#include <avr/io.h>
#include <inttypes.h>
#include "lcd-routines.h" //LCD-Display
#include <util/delay.h>

#define BAUD 9600UL // Baud einstellen 9600
#define UBRR_BAUD ((F_CPU/(16UL*BAUD))-1)

//-------------------------------------------------------------------------------------------
// Methoden
//-------------------------------------------------------------------------------------------

// USART initialisieren

uart_init(void)
{
// Baudrate einstellen (Normaler Modus)
UBRR0H = (uint8_t) (UBRR_BAUD>>8);
UBRR0L = (uint8_t) (UBRR_BAUD & 0x0ff);

// Aktivieren von receiver und transmitter
UCSR0B = (1<<RXEN0)|(1<<TXEN0);

// Einstellen des Datenformats: 8 Datenbits, 1 Stoppbit
UCSR0C = (1<<UCSZ01)|(3<<UCSZ00);
}

//-------------------------------------------------------------------------------------------
// Hauptschleife
//-------------------------------------------------------------------------------------------

main(void)
{

//-------------------------------------------------------------------------------------------
// Initialisierung
//-------------------------------------------------------------------------------------------

uart_init();
lcd_init();

// Variablen
uint8_t buffer=150;

//PWM 1 & PWM 2
DDRD = (1 << PD4 )|(1 << PD5 );

TCCR1A = (1<<WGM11) | (1<<COM1A1)|(1<<COM1B1);
TCCR1B = (1<<CS12)|(1<<WGM13)|(1<<WGM12);
ICR1 = 800; //Zählt bis zu diesen Wert
OCR1A = 120; //Bei diesen Wert wird eine 0 gesetzt
OCR1B = 200; //Bei diesen Wert wird eine 0 gesetzt

//PWM 3 & PWM 4
DDRB = (1 << PB3 )|(1 << PB4 );
TCCR0A = (1<<WGM01) |(1<<WGM00) | (1<<COM0A1) |(1<<COM0B1);
TCCR0B = (1<<CS00) |(1<<CS02);
OCR0A = 120;
OCR0B = 120;

//PWM 5 & PWM 6
DDRD |= (1 << PD6 )|(1 << PD7 );
TCCR2A = (1<<WGM21) |(1<<WGM20) | (1<<COM2A1) |(1<<COM2B1);
TCCR2B = (1<<CS20) |(1<<CS21) |(1<<CS22)|(0<<WGM22); // Datenblatt S.149
OCR2A = 120;
OCR2B = 120;

//-------------------------------------------------------------------------------------------
// Display
//-------------------------------------------------------------------------------------------

//Zeile 1
lcd_setcursor( 0, 1) ;
lcd_string("Zeile 1");
//Zeile 2
lcd_setcursor( 0, 2 );
lcd_string("Zeile 2");
//Zeile 3
lcd_setcursor( 20, 1 );
lcd_string("Zeile 3");
//Zeile 4
lcd_setcursor( 20, 2 );
lcd_string("Zeile 4");
//-------------------------------------------------------------------------------------------
// Schleife
//-------------------------------------------------------------------------------------------

while (1)
{

// Warten bis Daten empfangen wurden

if ( (UCSR0A & (1<<RXC0)) )
{
buffer = UDR0;
}
else
{

}
OCR2B=buffer;
}
return 0; // never reached
}



Hoffe auf schnelle Antwort. Muss in den Ferien noch viel schaffen.

Gruß André

Geistesblitz
26.10.2011, 08:58
Das sind aber komische Werte mit 16ms Periode und auch die anderen Zeiten passen da irgendwie nicht richtig rauf. Normalerweise ist eine Periode ja 20ms lang und der längste Puls etwa 2-2,25ms, also kommt das irgendwie auch von den Verhältnissen gar nicht richtig hin. Benutzt du eigentlich Digital- oder Analogservos?
Möglichkeiten wären, zusätzlich ein spezielles Servoboard zu verwenden (zB. von Pololu) oder eine Software-PWM mit Timer zu realisieren. Wenn du dich mal umsiehst, im Moment gibts wieder ein paar Themen zu Servos(hier (https://www.roboternetz.de/community/threads/55333-9-Servos-Auflösung-1µs-gt-Wie-löse-ich-das) oder hier (https://www.roboternetz.de/community/threads/54277-.-Vinculum-.-Hexabot)).

Staind
26.10.2011, 10:09
Ja könnte ihn auch 5 mal zählen lassen dann wäre ich bei einer Pause von 18ms. Habe Analoge Servos. So wie ich das gelesen habe sind die 20ms nur ein Richtwert. Die Pause kann minimm 14ms betragegen, damit sie noch vernünftig laufen. Habe ich zumindest so getestet. Ein Servoboard wollten wir nicht verwenden.

Geistesblitz
26.10.2011, 10:25
Dann würde ich vielleicht mal versuchen, eine Software-PWM zu integrieren. Da gibt es eigentlich unterschiedliche Konzepte, ich wollte das mal so probieren, dass jedes Servo nacheinander seinen Impuls bekommt (bei 20ms Periodendauer und max 2,5ms Impuls könnte man also 8 Servos ansteuern) und der µC hat dann eigentlich immer min. 0,5 ms Zeit, um irgendwas zu rechnen, da die Impulslänge zwischen 0,5 und 2,5ms angenommen wird. Ich geh hier einfach mal von gemoddeten Servos aus, die über ihre 180° hinaus bewegt werden können. Dafür bräuchte man dann allerdings eine rel. lange Interrupt-Routine, die dann immer den richtigen Wert an den aktuellen Servo übermitteln muss.
Ablauf wäre also: Pin für Servo 0 auf High, warte Impulslänge, wieder low setzen, rest bis zum ende der 2,5ms warten
und dann wieder von vorn mit dem nächsten Pin, bis alle durch sind.
Zwischendurch müsste der µC dann entweder die neuen Impulszeiten berechnen oder vom PC übermittelt bekommen.

Woanders hatte auch jemand etwas gepostet, dass er sich die Impulszeiten der Servos vorher berechnet und in eine Liste schreibt, die nur noch abgerufen werden muss. Dann würden die Servos auch halbwegs synchron laufen. Allerdings sehe ich bei diesem Prinzip noch nicht ganz durch, müsstest ihn wohl selbst anschreiben. Ich mag ja eigentlich lieber flexible Lösungen, als feste Tabellen.

oberallgeier
26.10.2011, 10:34
Hallo André!
... Atmega644 ... Impuls von 0,6ms ... ganz links... Wert 39 ... ganz rechts. ... 31 Schritte ... ist mir aber zu grob...Die 16 ms sind etwas kurz aber die meisten Servos sind ja seeehr tolerant. Es gibt Fälle, in denen Servos, auch analoge, schon mit 10 ms oder weniger Periodendauer korrekt gefahren werden. Wäre aber nett, wenn Du uns aufklärst über analog oder digital. Übrigens ist zu vermuten, dass Dein Servo bei Deiner Ansteuerung sowieso schon in den Anschlägen wimmert, wenn Du Deine 31 ticks Unterschied fahren kannst.

So - jetzt etwas zur Theorie der Ansteuerung, siehe auch hier (klick). (http://www.rn-wissen.de/index.php/Servo#Ansteuerung:_Signalform_und_Schaltung) Du siehst hier, dass der Servo standardmässig vom rechten zum linken Anschlag mit einem Pulsunterschied von 5 % der Periodendauer gesteuert wird (1 ms von 20 ms = 5 %). WENN Du also eine 8bittige PWM hast - 0...255 - dann hast Du maximal 12 Schritte (5% von 255) für die Servofahrt von "links" bis "rechts" - immer Standardbedingungen vorausgesetzt und auch nur dann, wenn Du die Periodendauer mit der gleichen PWM steuerst wie den Puls.

Wenn Du eine feiner Auflösung brauchtswillsthabenmusst, dann musst Du die Geschichte eben anders lösen.

Staind
26.10.2011, 13:50
Hallo Oberallgeier,

Habe wie oben schon geschrieben analoge Servos und diese lassen sich mit 16ms in der Periode (pausen sind also weniger) gut fahren.

Habe ein Video vom aktuellen Stand mal hochgeladen. Dort sieht man am anfang mein C# Programm. Dort ist einmal eine Trackbar die man von 10-42 (32 Schritte) einstellen kann. Sobald man den Wert ändert wird dieser in der Textbox angezeigt und über RS232 versendet. Der Botten lässt den Wert einmal von 10 auf 42 hochzählen.
Wenn ich die Zahl 9 oder 43 verschicken würde, wäre der Servo am Anschlag. So passt es aber noch, ohne das der Servo brummt bzw am Anschlag hängt. Im zweiten Teil sieht man mein Roboter Arm. Dort sieht man auch wozu das ganze gut ist. Dieser hat 6 Achsen, somit müssen 6 PWM Signale erzeugt werden.
Im letzten Teil des Videos sieht man noch mein Oscilloscope, wo das Signal angezeigt wird. Dort sieht man auch, das die Periodendauer ~16ms ist.


http://www.youtube.com/watch?v=T7tacaQcOQU

Nun zu meiner ursprünglichen Frage. Kann man nur jeden 4ten Impulz durchlassen oder muss ich damit leben das ich nur 32 Schritte zum Verfahren habe.

Andere Möglichkeit wäre ein Atmega64 zu nehmen. Dort habe ich aber das Problem das er nur in einer SMD Bauweise zu erwerben ist

Geistesblitz
26.10.2011, 14:38
Wie gesagt, probiers mal mit Software-PWM (http://www.rn-wissen.de/index.php/Pwm#PWM_per_Software).
Nimmst Timer1 (http://halvar.at/elektronik/kleiner_bascom_avr_kurs/timer_counter/) mit Prescaler 8 dafür, der kann bis zu 65535 hochzählen und bei 16MHz Taktfrequenz bedeutet das, dass er bis 32,277ms in 0,5µs-Schritten hochzählen kann. reicht also für die geforderten 20ms pro Periode.
Dann den Interrupt so programmieren, dass er deinen Servo-Pin für die Impulszeit auf High setzt, beispielsweise für ein 1,5ms-Signal stellst du den Timer auf 3000, und für den Rest der 20ms auf Low, also 40000-3000=37000.
Der µC-Takt wird also durch den Prescaler durch 8 geteilt, sodass man eine Frequenz von 2MHz hat, daher die 0,5µs-Schritte, der Pin wird für 3000*0,5µs=1,5ms High gesetzt und für 37000*0,5µs=18,5ms auf Low.
So lässt sich der Stellbereich von 1ms bis 2ms in 2000 Schritte unterteilen. Dürfte genauer sein, als der Servo auflösen kann ;)