Hallo
Grundsätzlich unterscheidet man blockierende und nicht blockierende Ansteuerungen. Blockierend bedeutet, das Programm wird nicht weiter fortgeführt wärend die Servoimpulse erzeugt werden. (Wie so ein Impuls aussehen sollte ist Grundlage, das solltest du schon wissen). Die Impulse werden solange erzeugt bis das Servo genug Zeit hatte die Zielpossition zu erreichen. Ein Beispiel mit Servo an LED1 (IO1):
Code:
// RP6 steuert ein Servo an der SL1-LED mit Sleep()
#include "RP6RobotBaseLib.h"
uint8_t i;
int main(void)
{
initRobotBase();
while(true)
{
i=0; // i mit Startwert laden
while(getBumperLeft() && (i<5)) // Wenn links gedrückt fünf Impulse senden
{
setLEDs(1); // Impuls High senden
sleep(10); // ca. 10 * 100µs warten
setLEDs(0); // Impuls Low senden
sleep(200-10); // ca. 20ms - 1ms Pause
i++;
}
i=0;
while(getBumperRight() && (i<5))
{
setLEDs(1);
sleep(20);
setLEDs(0);
sleep(200-20);
i++;
}
}
return 0;
}
https://www.roboternetz.de/community...l=1#post414115
Es wird solange ein Impuls erzeugt wie ein Bumper gedrückt wird. Ein Sleep(1) dauert beim RP6 0,1ms, Sleep(10) entsprechend 1ms bzw. Sleep(20) eben 2ms. Obwohl man mit dieser Methode auch mehrere Servos gleichzeitig ansteuern kann, ist sie letztlich eine Sackgasse, denn der RP6 kann ja nichts nebenher erledigen. Wenn er doch etwas anderes macht erhalten die Servos keine Impulse mehr und dein Roboterarm knickt ab...
Ganz anders verhalten sich interruptgesteuerte Ansteuerungen. Hier wird vom (Haupt-)Programm ledig die Zielposition vorgegeben, die Interruptsteuerung sorgt dann im Hintergrund dafür, das die entsprechenden Impulse erzeugt werden. Blöd ist allerdings dabei, dass die Lib des RP6 alle Timer belegt. Die einfachste libverträgliche Lösung ist deshalb wohl dieser Ansatz:
Code:
// Servoansteuerung mit Timer1 31.1.2010 mic
// Einfach und elegant, warum finde ich das erst jetzt? Timer1 (RP6-Motoransteuerung)
// läuft ja sowieso im Hintergrund. Deshalb kann man die "klassische" Servoansteuerung
// in die Overflow-ISR einbauen und mit ca. 19kHz aufrufen lassen. Timersetup der Lib:
// Mode 10, Phase Correct mit ICR1 als Top ergibt bei ICR1=210 ca. 8MHz/420=19047,6Hz ;)
// Drehbereich meiner RS-2-Servos ist von ca. 14 bis ca. 38
#include "RP6RobotBaseLib.h"
volatile uint8_t servo1, servo2, servo3, p; // Servopositionen und Impulszähler
uint8_t c; // ein Char zur freien Verfügung
int main(void)
{
initRobotBase();
servo1=servo2=servo3=26; // Servomitte?
TIMSK |= (1 << TOIE1); // Die Timer1 Overflow-ISR zur Servoansteuerung
DDRA |= (E_INT1); // Servopins auf Ausgang setzen
DDRC |= (SCL | SDA);
setLEDs(1); // und los!
startStopwatch1();
startStopwatch2();
startStopwatch3();
while(1)
{
for(c=0;c<6;c++) // 6 mal ein einfaches Demo....
{
setLEDs(1<<c);
servo1=servo2=servo3=26; // mitte
p=50; while(p); // warten bis 50 Impulse gesendet (ca. 1 Sek.)
servo1=servo2=servo3=14; // links
p=50; while(p);
servo1=servo2=servo3=38; // rechts
p=50; while(p);
}
setStopwatch1(0);
setStopwatch2(0);
setStopwatch3(0);
while(c) // und 6 mal ein etwas anspruchsvolleres Demo
{
setLEDs(1<<c);
if(getStopwatch1()>1000)
{
setStopwatch1(0);
switch(servo1)
{
case 38: servo1=30; break;
case 30: servo1=20; break;
case 20: servo1=14; break;
case 14: servo1=21; break;
case 21: servo1=31; break;
case 31: servo1=38; break;
}
}
if(getStopwatch2()>100)
{
setStopwatch2(0);
servo2++;
if (servo2 > 38) { servo2=14; c--; }
}
if(getStopwatch3()>300)
{
setStopwatch3(0);
if (servo3 == 10) servo3=50; else servo3=10;
}
task_RP6System(); // Verträglichkeitstest ;)
}
}
return 0;
}
ISR (TIMER1_OVF_vect)
{
static uint16_t servocount=1;
if(servocount > servo1) PORTA &= ~E_INT1; else PORTA |= E_INT1; // PA4 XBUS 8
if(servocount > servo2) PORTC &= ~SCL; else PORTC |= SCL; // PC0 XBUS 10
if(servocount > servo3) PORTC &= ~SDA; else PORTC |= SDA; // PC1 XBUS 12
if(servocount < 400) servocount++; else {servocount=1; if(p) p--;} // p = 1/50 Sek
}
https://www.roboternetz.de/community...l=1#post447000
Nachteil dieser eigentlich bestechend einfachen Lösung ist die geringe Auflösung der Servopositionen. (Vielleicht kann man das auch mit einem anderen Timer tricksen?) Vorteil ist eben, dass die Impuls durchgehend erzeugt werden, egal was man im Hauptprogramm macht.
Beim Anschluss an ADC0 oder ADC1 muss man beachten, dass das Pinout auf der RP6-Platine nicht servokonform ist. Deshalb muss man am Servostecker Plus und Minus tauschen:
Vielleicht reicht dir das schon zum Einstieg.
Gruß
mic
Lesezeichen