Archiv verlassen und diese Seite im Standarddesign anzeigen : RP6 Servo Ansteuerung
Robotoni
30.09.2007, 19:48
Hallo verfolge mit viel Interesse das Forum .. habe seit 3 Wochen einen RP6
ein wenig aufgerüstet ... (LCD, zusätzliche LEDs, Tastatur, Sensor) und komme trotz eingeschränkter Programmier Fähigkeiten ganz gut zurecht (dank Forum) Ich möchte gerne einen Servo mit Sensor anbringen, der nach links und rechts einen Programmierten Winkel sich bewegt. Ich bitte um Mithilfe
Hallo Robotoni,
Glückwunsch zu deinem RP6. Er hat ja in den 3 Wochen schon eine Menge an Hardware spendiert bekommen!
Zur Ansteuerung eines Servos gibt es hier im Forum viele gute Lösungen, so dass ich mich nicht traue, eine in reinem GCC für den RP6 zu posten (wer weiß, was es dann an Kritik hagelt: Zu einfach! Gab es schon tausendmal und viel besser ...). :wink:
Aber das Prinzip gilt auch für den RP6:
Das Servo braucht einen positiven Impuls von 1ms (linker Anschlag) bis 2ms (rechter Anschlag). Dieser Impuls sollte sich ca. alle 20ms wiederholen.
Du must also einen Port z.B. auf High schalten und nach 1,5ms wieder auf Low, wenn das Servo in Mittelstellung sein soll. Das wiederholt sich alle 20ms. Sieh dir mal die Funktion "delayCycles" an! Damit könntest du auch feinere Abstimmungen für die Impulsdauer erreichen.
Ich würde als erstes eine Task schreiben, die alle 20ms einen Impuls erzeugt (die Stopwatches sind dafür ideal). Die Impulslänge veränderst du dann nach Bedarf.
Gruß Dirk
radbruch
01.10.2007, 10:03
Hallo
Mein Ansatz für eine Servoansteuerung mit dem RP6:
http://radbruch.roboterbastler.de/rp6/pics/rp6_servo1.gif
(Bild anklicken für 1,8MB mpg) (http://radbruch.roboterbastler.de/rp6/pics/rp6_servo1.mpg)
Hardware ist ein 5€-Miniservo von Conrad, aufgeklebt auf den RP6 mit einem Power-Strip. Aus dem Stecker(JBL) habe ich den Taktpin (orange) ausgebaut. Nun kann ich den Servo-Stecker mit +(rot) und -(braun) in den XBUS-stecken (Pin1=GND, Pin2=5V-Vcc) und das Signal z.b. in Pin8 (INT1) oder Pin10 (SCL).
Für die Ansteuerung zapfe ich den 72kHz-Interrupt an, der Timerinterrupt hat mit nur 10 kHz eine zu geringe Auflösung (=weniger mögliche Servopositionen). Das ergibt zwar "krumme" Werte für die Positionen, funktioniert aber wunderbar:
#include "RP6RobotBaseLib.h"
#define servo_min 26
#define servo_mitte 85
#define servo_max 155
volatile unsigned char servo_winkel;
uint8_t servo_pos=servo_mitte;
void servo(uint8_t winkel)
{
uint16_t servo_stellzeit;
if (servo_winkel != winkel)
{
TIMSK |= (1 << OCIE2);
servo_stellzeit=200*abs(servo_winkel-winkel);
servo_winkel=winkel;
while (servo_stellzeit--);
TIMSK &= ~(1 << OCIE2);
}
}
int main(void) {
uint8_t i;
initRobotBase();
DDRC |= 1; // SCL auf Ausgang stellen
while (1)
{
servo(servo_mitte);
mSleep(500);
servo(servo_min);
mSleep(200);
for (i=servo_max; i>servo_min; i-=2)
{
servo(i);
setLEDs(16);
}
for (i=servo_min; i<servo_max; i+=2)
{
servo(i);
setLEDs(32);
}
}
while(1);
return 0;
}
/*
ISR (TIMER2_COMP_vect)
{
#define Servo on
#ifdef Servo
extern unsigned char servo_winkel;
static unsigned int servo_zyklus=0;
if (servo_winkel)
{
if (servo_zyklus>servo_winkel) extIntOFF(); else extIntON();
//if (servo_zyklus>servo_winkel) PORTC &= ~1; else PORTC |= 1;
if (servo_zyklus<1440) servo_zyklus++; else servo_zyklus=0;
}
//else
if (0)
{
#endif
...
#ifdef Servo
}
#endif
}
/**
*/
Der einfache Democode definiert die Lageendpunkte und die Mitte, und steuert das Servo endlos auf diese Punkte. In der Funktion servo() wird die Stellzeit berechnet und der Winkel in servo_winkel eingetragen. Dann wird der Interrupt freigegeben und die Erweiterung des Interrupts steuert dann das Servo automatisch. Nach Ablauf der Stellzeit wird der Interrupt wieder gesperrt. Um Seiteneffekte zu vermeiden, wird in der Library der restliche Code der ISR ausgeblendet. Der Code für die Änderung der ISR befindet sich am Ende des Democodes. Das Ganze ist noch in der Entwicklung.
Gruß
mic
Robotoni
02.10.2007, 19:35
Erst mal danke für die Antworten, komme aber leider nicht richtig weiter, wie ändere ich die ISR ? kurze Schrittweise Erklärung wäre super "radbruch"
PS ich glaube in deiner Pinbelegung ist ein kleiner Fehler :
" (Pin1=GND, Pin2=5V-Vcc) " Richtig Pin 1=GND Pin3=VDD
radbruch
02.10.2007, 22:17
Hallo
Entschuldigung, das war natürlich Quatsch gewesen, 5V sind am Pin3.
Meine ISR in "RP6RobotBaseLib.c" sieht so aus:
ISR (TIMER2_COMP_vect)
{
#define Servo on
#ifdef Servo
extern unsigned char servo_winkel;
static unsigned int servo_zyklus=0;
if (servo_winkel)
{
if (servo_zyklus>servo_winkel) PORTC &= ~1; else PORTC |= 1;
if (servo_zyklus>servo_winkel) extIntOFF(); else extIntON();
if (servo_zyklus<1440) servo_zyklus++; else servo_zyklus=0;
}
//else
if (0)
{
#endif
static uint8_t ircomm_pulse;
if(acs_state < 2) { // If ACS is not active, perform IRCOMM transmissions
if(ircomm_pulse) { // Do we have IR pulses to send?
if(ircomm_pulse < 60) { // Bi-Phase encoding...
if(ircomm_data & 0x4000) // check current bit
PORTD ^= (1<<PIND7); // Toggle IRCOMM port
else
PORTD &= ~(1<<PIND7); // deactivate IRCOMM port
}
else if(ircomm_data & 0x4000) // The same as above, but the other way round:
PORTD &= ~(1<<PIND7); // deactivate IRCOMM port
else
PORTD ^= (1<<PIND7); // Toggle IRCOMM port
ircomm_pulse--;
}
else if(ircomm_send) { // Do we still have data?
PORTD &= ~(1<<PIND7);
ircomm_data <<= 1; // Next Bit!
ircomm_pulse = 120;
ircomm_send--;
}
else
PORTD &= ~(1<<PIND7); // no more pulses - IR LEDs off!
}
else if(acs_pulse) { // Send ACS IR pulses?
if(sysStatACS.channel == ACS_CHANNEL_LEFT) // which channel?
PORTB ^= ACS_L;
else
PORTC ^= ACS_R;
acs_pulse--;
}
else { // no more pulses - IR LEDs off!
PORTB |= ACS_L;
PORTC |= ACS_R;
}
#ifdef Servo
}
#endif
}
Mit
//else
if (0)
wird die orginale ISR nicht mehr abgearbeitet. Wie geschrieben ist das noch in der Entwicklung und soll nur zeigen, wie man es machen könnte. Möglichkeiten gibt es sicher mehrere, aber ich mag Lösungen, die keinen großen Aufwand bedeuten.
Gruß
mic
Hallo Robotoni,
hier auch von mir noch eine kleine Demo zu Servo-Ansteuerung.
Sie soll eigentlich nur zeigen, dass es auch ohne Einklinken in eine ISR geht, allerdings mit dem Nachteil einer Verschwendung von Rechenpower.
Damit soll dies hier nur zum Experimentieren dienen!
Viel Spaß!
Gruß Dirk
Hallo Robotoni,
kleiner Tipp:
da Du das RP6-M32 Erweiterungsmodul hast, gäbe es neben den Möglichkeiten die mic und Dirk angegeben haben auch die Möglichkeit ein PWM Modul des MEGA32 auf dem RP6-M32 zu verwenden. Timer1 ist beim RP6-M32 komplett frei und PD5(OC1A) ist auf dem I/O Erweiterungsanschluss verfügbar. Das Hardware PWM Modul zu verwenden hätte den Vorteil, dass es keinerlei Rechenzeit kostet und immer nur einmal kurz die Position des Servos gesetzt werden muss.
Beispielcode habe ich gerade leider nicht parat, aber nähere Infos dazu findest Du z.B. im Datenblatt des MEGA32 im Abschnitt zum Timer1.
MfG,
SlyD
Hallo Robotoni,
ich habe mir gerade noch einmal dein Bild vom RP6 angesehen:
Da hast du ja auch eine Tastatur draufgebaut.
Wie hast du die angeschlossen?
Gruß Dirk
Robotoni
04.10.2007, 16:17
Hallo Dirk,
Schaue unter http://www.avr-asm-tutorial.net/avr_de/keypad/keyboard.html
nach. Gemäß dem Plan "Widerstandsmatrix" habe ich diese gebaut und an ADC7 an das RP6 Erweiterungsmodul angeschlossen. Dazu habe ich mit meinen Eingeschränkten Programmierfähigkeiten ein Programm erstellt womit
der ADC7 ausgelesen wird, und das Wandler-Ergebniss einer Taste zugeordnet wurde :
if ((adc7 > 40) && (adc7< 65)) //Taste 1
{ setLEDs(0b0001);
sound(140,10,0);
writeStringLCD_P("Taste 1");
mSleep(500);
}
else if ((adc7 > 66) && (adc7< 105)) //Taste 2
{setLEDs(0b0010);
sound(140,10,0);
writeStringLCD_P("Taste 2");
mSleep(500);
}...........
meine ermittelten Werte
1=50/2=88/3=131/4=196/5=310/6=412/7=537/8=650/9=770/0=885
Demnächst Starte ich damit meine Programme...
Die Teile sind von Conrad alle Teile 7€
Gruß Toni
Robotoni
04.10.2007, 16:42
Das Programm von Dirk läuft gut.. aber es wäre schon toll, wenn das Signal über den Timer des RP6-M32 Erweiterungsmodules laufen würde.
Brauche eine kleine Programmierhilfe ! Ziel ist es das das Servo nach links läuft dann eine Messung durchgeführt (Entfernungsmessung) wird... und das selbe rechts abläuft...
Gruß Toni O:)
Hallo Robotoni,
ich habe da jetzt kein Beispielprog in der Schublade.
"stochri" hat hier 'mal einen GCC-Code veröffentlicht, mit dem 10 Servos mit Timer1-PWM angesteuert werden.
Geh mal auf die Suche.
Gruß Dirk
Hallo Robotoni,
... aber es wäre schon toll, wenn das Signal über den Timer des RP6-M32 Erweiterungsmodules laufen würde.
Da habe ich mich dann 'mal dran gesetzt und das ist heraus gekommen.
Viel Spaß!
Gruß Dirk
Robotoni
06.10.2007, 17:35
Hallo Dirk... toll wenn man richtig Ahnung hat ...... funktioniert super...
bin bereits am Experimentieren.... mit meinem Entfernungssensor und einem Servo.. danke..danke..danke
Gruß Robotoni (Toni)
Hallo RP6 Servo-Tester,
SlyD: ... gäbe es ... auch die Möglichkeit ein PWM Modul des MEGA32 auf dem RP6-M32 zu verwenden.
Diese tolle Lösung zur Servoansteuerung fehlte ja noch.
Hier ein 1. Vorschlag zur Umsetzung für den RP6.
Viel Spaß damit!
Gruß Dirk
carlitoco
28.12.2007, 00:23
Ich habe das ganze mal wie oben beschrieben durch probiert...
1. mal das programm mit copy paste übernommen
2. die Lib für das programm verändert.
3. make all
Das rote lämpchen leuchtet aber der servo tut nix... außer beim anschließen kurz zucken
!Wichtig habe bis jetzt nur das RP6 basis modul - aber das sollte ja klappen wie im video zu sehen ist ...!
kann mir da jemand weiter helfen ?
Danke :)
Hallo carlitoco,
da müßte radbruch etwas zu sagen.
Wenn du erst 'mal nicht die Lib verändern willst, probier evtl. meine Version (RP6Base_Servo.zip vom 3.10.07).
Gruß Dirk
carlitoco
28.12.2007, 13:39
ok da hat er sich jetzt schon mal in eine richtung gedreht - prima -jetzt muss ich mal schauen warum er versucht immer weiter in die selbe richtung zu drehen..
Danke
radbruch
28.12.2007, 15:27
Ups, ich dachte, ihr redet über Servos am Erweiterungsmodul. Inwischen steure ich die Servos etwas anders an:
// Vierradantrieb mit RC-Steuerung v1.0 9.12.2007 mic
#include "rblib.h"
#include "rblib.c"
uint8_t servo_l, servo_r, demo;
uint16_t i, j, time;
uint8_t rc_input_pwr, rc_input_dir, rc_pwr, rc_dir, rc_misch=6;
ISR(TIMER0_COMP_vect)
{
static uint16_t count=0;
uint8_t rc_in;
static uint8_t rc_temp_pwr=0;
static uint8_t rc_temp_dir=0;
if(count>servo_l) PORTA &= ~1; else PORTA |= 1; // ADC0
if(count>servo_r) PORTA &= ~2; else PORTA |= 2; // ADC1
if(count<2000)count++; else { count=0; time++; }
rc_in=PINC;
if (rc_in & 1) rc_temp_dir++; else // SCL (Pin10)
if (rc_temp_dir) { rc_input_dir=rc_temp_dir; rc_temp_dir=0; }
if (rc_in & 2) rc_temp_pwr++; else // SDA (Pin12)
if (rc_temp_pwr) { rc_input_pwr=rc_temp_pwr; rc_temp_pwr=0; }
}
void servo_init(void)
{
//DDRA |= 16; // E_INT1 als Ausgang
//DDRC |= 3; // SCL und SDA als Ausgang
DDRA |= 3; // ADC0(PortA0) und ADC1(PortA1) als Ausgang
TCCR0 = (0 << WGM00) | (1 << WGM01); // CTC-Mode
TCCR0 |= (0 << COM00) | (0 << COM01); // ohne OCR-Pin
TCCR0 |= (0 << CS02) | (1 << CS01) | (0 << CS00); // prescaler /8
TIMSK = (1 << OCIE0); // Interrupt ein
OCR0 = 13; // 100kHz?
time=0;
}
int main(void)
{
rblib_init();
servo_init();
servo_l=130;
servo_r=120;
setMotorDir(FWD,FWD);
setMotorPWM(130,90);
setLEDs(0b1001);
while(0)
{
if (1) {
writeString("\n\r");
writeInteger(rc_input_pwr, 10);
writeString("-");
writeInteger(rc_input_dir, 10);
if (rc_input_pwr > 105)
{
rc_pwr=rc_input_pwr-100;
servo_l=servo_r=rc_input_pwr;
setMotorDir(FWD,FWD);
setMotorPWM(rc_pwr*rc_misch,rc_pwr*rc_misch);
}
else if (rc_input_pwr < 95)
{
rc_pwr=100-rc_input_pwr;
servo_l=servo_r=rc_input_pwr;
setMotorDir(BWD,BWD);
setMotorPWM(rc_pwr*rc_misch,rc_pwr*rc_misch);
}
else
{
servo_l=servo_r=100;
setMotorPWM(0,0);
}
}
}
while(1);
return 0;
}
Ich verwende nicht die orginalen Libraries, so kann ich freier über die Funktionen des Mega32 verfügen. (Meine abgespeckte Lib ist im Anhang, Verwendung auf eigene Gefahr, weil ohne Überwachungen! Ist ideal beim Anhauchen wenn ein Antrieb spinnt*grins*)
Alternativ könnte man auch die Timer 2 Compare ISR in der RP6BaseLib verwenden, die wird aber nicht ganz so häufig aufgerufen, die Werte für die Servostellung sind dann anders. Und man muss von Hand dafür sorgen, dass der Interrupt überhaupt läuft. Deshalb meine eigene Lib, damit geht das einfacher.
Zum Programm(, das eigentlich die Vorbereitung meines 4-Rad-Antriebs ist):
Die zwei Servos hängen an ADC0/1, das sind die freien ADC-Anschlüsse hinter dem IR-Empfänger auf der RP6-Platine. Hier habe ich zwei 3-polige Stiftleisten (+,-,ADC0/1) angelötet. Blöderweise sind die nicht servokompaktibel (bei den Servos ist + in der Mitte), aber an den Steckern der Servos kann man recht einfach die Adern vertauschen. Die Werte für die Servostellungen werden in uint8_t servo_l/servo_r übergeben, count erzeugt die 20ms-Wiederholung (und time dient als allgemeiner Timer):
if(count>servo_l) PORTA &= ~1; else PORTA |= 1; // ADC0
if(count>servo_r) PORTA &= ~2; else PORTA |= 2; // ADC1
if(count<2000)count++; else { count=0; time++; }
SDA und SCL verwende ich hier zum Einlesen der Signale einer RC-Fernbedienung die am xBus hängt, das wird in der ISR gleich miterledigt. Mit dem Programm steure ich momentan die Motoren und die Servos zusammen an. Denn unnötigen Code kann man natürlich rausschmeissen. Das passiert im unteren Teil der ISR:
rc_in=PINC;
if (rc_in & 1) rc_temp_dir++; else // SCL (Pin10)
if (rc_temp_dir) { rc_input_dir=rc_temp_dir; rc_temp_dir=0; }
if (rc_in & 2) rc_temp_pwr++; else // SDA (Pin12)
if (rc_temp_pwr) { rc_input_pwr=rc_temp_pwr; rc_temp_pwr=0; }
Man kann auch gut die SDA/SCL am xBus zur Ansteuerung der Servos verwenden.
Gruß
mic
Stratege993
28.12.2007, 16:48
Also ich will auch mal ein Servo ansteuern, aber leider werde ich aus den ganzen Beiträgen nicht schlau. Ich habe ein Robby ohne Erweiterung. Welche Möglichkeiten gibt es jetzt, um einen oder mehrere Servos anzusteuern und vor allem welche Ausgänge nutzt man dann?
Stratege993
carlitoco
28.12.2007, 17:29
@Radbruch Hm das beispiel aber einmal nachzuarmen wird nicht toll, da ich mir jetzt überlegen muss wo lege ich die rblib.c /.h ab um anschließend die makefile zu bearbeiten... jedoch frage ich mich ob ich für das program nur deine beiden libs brauch oder die anderen aus dem RP6Lib-ordner auch...
Wie ist das?
MfG carlitoco
radbruch
28.12.2007, 18:12
Sorry, wenn meine Vorschläge nicht wirklich einfach nachzuvollziehen sind. Ich bastle eben schon eine Weile dran rum und das ist alles eher "natürlich gewachsen".
Die einfachste Lösung für den RP6 habe ich wohl hier beschrieben:
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=34950
Oder ohne sleep(), dafür mit Verzögerungsschleifen:
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=321483#321483
Oder so wie bei meinem Monoleg (https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=34593):
#include "RP6RobotBaseLib.h"
uint8_t i, pause, servo_stellzeit, servo_delay;
void servo(uint8_t w0, uint8_t w1, uint8_t w2)
{
unsigned int count=0;
do{
PORTA |= E_INT1; // E_INT1 (Pin8)
sleep(w0);
PORTA &= ~E_INT1;
PORTC |= 1; // SCL (Pin10)
sleep(w1);
PORTC &= ~1;
PORTC |= 2; // SDA (Pin12)
sleep(w2);
PORTC &= ~2;
sleep(servo_delay-(w0+w1+w2));
//sleep(127);
} while (count++ < servo_stellzeit);
mSleep(10*pause);
}
int main(void) {
initRobotBase();
i=0;
servo_stellzeit=15;
DDRA |= E_INT1; // E_INT1 als Ausgang
DDRC |= 3; // SCL und SDA als Ausgang
// 5 - 15 - 25 // Min - Mitte - Max
servo(15,15,15); // Grundstellung
while (1)
{
switch (i)
{
case 0: i++; pause=30; servo_delay=255; mSleep(1000); break;
case 1: i++; pause=10; servo_delay=255; break;
case 2: i++; pause=10; servo_delay=127; break;
case 3: i++; pause=1; servo_delay=127; break;
case 4: i=0; pause=1; servo_delay=127; break;
}
servo(10,15,15); // Finger zurück
servo(15,15,15);
servo(20,15,15); // Finger vor
servo(15,15,15);
servo(15,10,15); // Arm runter
servo(15,15,15);
servo(15,20,15); // Arm hoch
servo(15,15,15);
servo(15,15,10); // Schulter links
servo(15,15,15);
servo(15,15,20); // Schulter rechts
servo(15,15,15);
servo(20,10,15); // Finger vor, Arm runter
servo(25,5,15);
servo(20,10,15);
servo(15,15,15);
servo(20,20,15); // Finger vor, Arm hoch, Schulter links
servo(15,15,10);
servo(10,20,10); // Finger zurück, arm hoch
servo(5,25,10);
servo(10,20,10);
servo(15,15,15);
}
return 0;
}
Alles Lösungen mit der orginalen Library und Anschluß am XBUS-Stecker. Einfach zu finden wenn man hier im RN-Forum nach "servo AND rp6 AND radbruch" sucht.
Einfacher geht's wohl nur so:
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=29902
rblib muss ins selbe Verzeichniss wie das Programm (wegen den "", bei <> müßte es in den Pfad).
Gruß
mic
Hardware ist ein 5€-Miniservo von Conrad, aufgeklebt auf den RP6 mit einem Power-Strip. Aus dem Stecker(JBL) habe ich den Taktpin (orange) ausgebaut. Nun kann ich den Servo-Stecker mit +(rot) und -(braun) in den XBUS-stecken (Pin1=GND, Pin3=5V-Vcc) und das Signal z.b. in Pin8 (INT1) oder Pin10 (SCL).
Für die Ansteuerung zapfe ich den 72kHz-Interrupt an, der Timerinterrupt hat mit nur 10 kHz eine zu geringe Auflösung (=weniger mögliche Servopositionen). Das ergibt zwar "krumme" Werte für die Positionen, funktioniert aber wunderbar:
#include "RP6RobotBaseLib.h"
#define servo_min 26
#define servo_mitte 85
#define servo_max 155
volatile unsigned char servo_winkel;
uint8_t servo_pos=servo_mitte;
void servo(uint8_t winkel)
{
uint16_t servo_stellzeit;
if (servo_winkel != winkel)
{
TIMSK |= (1 << OCIE2);
servo_stellzeit=200*abs(servo_winkel-winkel);
servo_winkel=winkel;
while (servo_stellzeit--);
TIMSK &= ~(1 << OCIE2);
}
}
int main(void) {
uint8_t i;
initRobotBase();
DDRC |= 1; // SCL auf Ausgang stellen
while (1)
{
servo(servo_mitte);
mSleep(500);
servo(servo_min);
mSleep(200);
for (i=servo_max; i>servo_min; i-=2)
{
servo(i);
setLEDs(16);
}
for (i=servo_min; i<servo_max; i+=2)
{
servo(i);
setLEDs(32);
}
}
while(1);
return 0;
}
/*
ISR (TIMER2_COMP_vect)
{
#define Servo on
#ifdef Servo
extern unsigned char servo_winkel;
static unsigned int servo_zyklus=0;
if (servo_winkel)
{
if (servo_zyklus>servo_winkel) extIntOFF(); else extIntON();
//if (servo_zyklus>servo_winkel) PORTC &= ~1; else PORTC |= 1;
if (servo_zyklus<1440) servo_zyklus++; else servo_zyklus=0;
}
//else
if (0)
{
#endif
...
#ifdef Servo
}
#endif
}
/**
*/
Der einfache Democode definiert die Lageendpunkte und die Mitte, und steuert das Servo endlos auf diese Punkte. In der Funktion servo() wird die Stellzeit berechnet und der Winkel in servo_winkel eingetragen. Dann wird der Interrupt freigegeben und die Erweiterung des Interrupts steuert dann das Servo automatisch. Nach Ablauf der Stellzeit wird der Interrupt wieder gesperrt.
Um Seiteneffekte zu vermeiden, wird in der Library der restliche Code der ISR ausgeblendet. Der Code für die Änderung der ISR befindet sich am Ende des Democodes. Das Ganze ist noch in der Entwicklung.
Hi mic,
es ist schon lange her, aber vielleicht erinerst du dich noch. Ich habe es so versucht, wie hier beschrieben, der servo bewegt sich leider nicht (der kontakt 1 am XBUS1 ist ja auf der platine gekennzeichnet)...
Was muss ich da mit dem ISR tun?
danke
@Dirk:
ich habe mir auch Deine version angeschaut, wie schliesse ich den servo an die LED1, bzw. wo finde ich freie anschlüsse für SL1? Bin ich blind, oder zu doof?
thanx...
radbruch
07.01.2010, 14:36
Hallo
Das funktioniert nur, wenn du in der RP6BaseLib.c die Timer2-Overflow-ISR komplett ersetzt:
https://www.roboternetz.de/phpBB2/viewtopic.php?p=318456#318456
Inzwischen, das ist ja schon zwei Jahre her, erkenne ich auch, dass diese Art der Ansteuerung ziemlicher Mist ist. Deshalb rate ich von der Benutzung eher ab.
Da die RP6-Library alle Timer verwendet, muss man bei der Servoansteuerung immer Kompromisse eingehen, die von der jeweiligen Anwendung abhängig sind:
Das einfachste (wie oben schon beschrieben) ist eine blockierende Ansteuerung (mit Sleep() oder Zählschleife) bei der das Programm wartet, bis das Servo seine Position erreicht hat. Das ist für erste Servoversuche die übersichtlichste Lösung. Diesen Ansatz mit einem Servo an SCL hatte ich hier mal in das Tasksystem eingebunden:
#include "RP6RobotBaseLib.h"
#define pos1 800
#define pos2 1600
extern uint8_t acs_state;
uint16_t servo_pos, servo_timer, servo_dummy;
int main(void) {
initRobotBase();
DDRC |= 1;
startStopwatch1();
startStopwatch2();
servo_pos = pos1;
while (1)
{
if ((getStopwatch1() > 20) && (acs_state < 2)) // alle 20ms einen Servoimpuls erzeugen
{
setStopwatch1(0);
servo_timer = servo_pos;
PORTC|=1;
while(servo_timer--) servo_dummy ^= servo_timer;
PORTC&=~1;
}
if (getStopwatch2() > 1000) // alle 1000ms Servo auf andere Seite fahren
{
if (servo_pos == pos1) servo_pos=pos2; else servo_pos=pos1;
setStopwatch2(0);
}
//task_RP6System();
task_ADC();
task_ACS();
task_Bumpers();
task_motionControl();
}
return 0;
}
Alle Servoansteuerungen, die im Hintergrund ausgeführt werden, müssen einen Timer umbiegen und blockieren damit die Lib-Funktion des betreffenden Timers. Ohne Timer0 gibt es keine StopWatch() und andere Zeitfunktionen mehr, ohne Timer1 funktionieren die Motoren nicht mehr und ohne Timer2 gibt es keine 36kHz-Trägerfrequenz fürs IR. Das bedeutet, man muss sich entscheiden, auf welche Funktionsgruppe man verzichten möchte und steuert die Servos dann mit dem freien Timer an. Dabei kann man natürlich jederzeit wieder die orginale Funktion des Timers aktivieren indem man initRobotBase() aufruft und das Timersetup der Lib wiederherstellt. Hier eine resourcenschonende Variante mit Timer2 und drei Servos an SDA, SCL und E-INT am XBUS:
//Servos mit Timer2 Overflow-ISR 27.2.2008 mic
#include "RP6RobotBaseLib.h"
uint8_t pos[3]={255,170,85}; // Positionen der drei Servos an SDA, SCL und E_INT
int main(void)
{
initRobotBase();
DDRA |= 16;
PORTA &= ~16;
DDRC |= 3;
PORTC &= ~3;
//Timer2 Initialisierung
TCCR2 = (0 << WGM21) | (0 << COM20) | (1 << CS22); // Normal Mode, prescaler /64
TIMSK |= (1 << TOIE2); // Timer2 Overflow-Interrupt erlauben
while(1)
{
pos[0]=pos[1]=pos[2]=170;
mSleep(1000);
pos[0]=pos[1]=pos[2]=220;
mSleep(1000);
pos[0]=pos[1]=pos[2]=170;
mSleep(1000);
pos[0]=pos[1]=pos[2]=120;
mSleep(1000);
}
return(0);
}
ISR (TIMER2_OVF_vect)
{
static uint8_t servo_nr=0;
static uint16_t impulspause;
if(servo_nr)
{
if(servo_nr==1) {TCNT2=-pos[0]; PORTC |= 1; impulspause-=pos[0];}
if(servo_nr==2) {TCNT2=-pos[1]; PORTC &= ~1; PORTC |= 2; impulspause-=pos[1];}
if(servo_nr==3) {TCNT2=-pos[2]; PORTC &= ~2; PORTA |= 16; impulspause-=pos[2];}
if(servo_nr==4) {PORTA &= ~16; servo_nr=0;}
if(servo_nr) servo_nr++;
}
else
{
if(impulspause>256) impulspause-=256;
else {TCNT2=-impulspause; servo_nr++; impulspause=1500;}
}
}
In diesem Zusammenhang möchte ich noch den ADC-Interrupt erwähnen. Er wird von der RP6-Lib nicht verwendet und eignet sich auch dazu, Servoimpulse im Hintergrund zu erzeugen. Der ADC läuft dabei im Dauerlauf, dadurch wird allerdings der Zugriff auf unterschiedliche ADC-Kanäle etwas komlizierter. Ein Beispiel mit Servos an den LEDs und SDA/SCL:
// Domino Day für den RP6 mit neuem Greifarm und ADC-ISR 19.1.2008 mic
#include "RP6RobotBaseLib.h"
uint16_t s1, s2, s3, s4 ,s5, s6;
void servo_ON(void)
{
cli();
// Freeruning, 5V-Ref, ISR enable, prescale /32 ((8MHZ/32)/12,5 sind ca. 20kHz bzw. 0,05us)
// AVCC with external capacitor at AREF pin, Ergebniss linksbündig, Kanal ADC7 (UBAT)
ADMUX = (0<<REFS1) | (1<<REFS0) | (1<<ADLAR) | 7;
// setzte free running triggern
SFIOR = (0<<ADTS2) | (0<<ADTS1) | (0<<ADTS0);
// Interrupt ein, Wandler einschalten, prescaller /32
ADCSRA = (1<<ADIE) | (1<<ADEN) | (1<<ADPS2) | (0<<ADPS1) | (1<<ADPS0);
// Autotriggern bedeutet jetzt free running aktivieren, altes Flag löschen
ADCSRA |= (1<<ADATE) | (1<<ADIF);
// Initialisierung starten
ADCSRA |= (1<<ADSC);
// und noch die wohl eher unnötige Initiallesung
while (!(ADCSRA & (1<<ADIF)));
ADCSRA |= (1<<ADIF);
DDRC |= 0x70; // LED1-3 auf Ausgang und low
PORTC &= ~0x70;
DDRB |= 0x83; // LED4-6 auf Ausgang und low
PORTB &= ~0x83;
DDRC |= 0x3; // SCL und SDA auf Ausgang und low
PORTC &= ~0x3;
sei();
}
void servo_OFF(void)
{
cli();
// Initialize ADC: (Defaultsetup der RP6-Library)
ADMUX = 0; //external reference
ADCSRA = (0<<ADIE) | (0<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADIF);
SFIOR = 0;
sei();
setLEDs(0); // LEDs restaurieren
}
int main(void)
{
initRobotBase();
s1=25;
s2=25;
s3=25;
s4=25;
s5=25;
s6=25;
servo_ON();
setLEDs(63);
mSleep(2000);
setLEDs(0);
while(1)
{
mSleep(1000);
s1=40;
mSleep(1000);
s1=25;
mSleep(1000);
s2=35;
mSleep(1000);
s2=15;
}
return 0;
}
ISR(ADC_vect)
{
static uint16_t count=0;
(count>s1)?(PORTC&=~SL1):(PORTC|=SL1);
(count>s2)?(PORTC&=~SL2):(PORTC|=SL2);
(count>s3)?(PORTC&=~SL3):(PORTC|=SL3);
(count>s4)?(PORTB&=~SL4):(PORTB|=SL4);
(count>s5)?(PORTB&=~SL5):(PORTB|=SL5);
(count>s6)?(PORTB&=~SL6):(PORTB|=SL6);
(count>s1)?(PORTC&=~1):(PORTC|=1); // SCL
(count>s2)?(PORTC&=~2):(PORTC|=2); // SDA
(count<500)?count++:(count=0);
}
Bitte beachtet, dass ich inzwischen ziemlich viel Zeit mit Servoansteuerungen vergeudet habe...
Gruß
mic
danke erstmal mic,
servoanschluss braun an XBUS1-1, rot an XBUS1-3, orange an XBUS1-10, codebeispiel 1 fehlerfrei kompiliert und upgeloaded - servo (conrad, VSD-1 (http://conrad.de/goto.php?artikel=205387)) steht, nur die SL5 blinkt in bestimmten abständen 10x...:-(
radbruch
07.01.2010, 15:49
Hallo
Der Anschluß 1(GND, bn), 3(Vdd, rt) und 10(PC0/SCL, orange) sollte passen(, wenn du wirklich am XBUS bist). Zuckt das Servo eigentlich beim Einschalten des RP6 oder wenn du es unter Spannung einsteckst? Wenn nicht, sind vielleicht die Anschlussleitungen im Servo "abgefallen". Das Problem habe ich häufig bei meinen (Billig-)Servos.
Das Blinken der LED ist die Power-On-Anzeige der RP6-Lib. Daran erkennt man, dass der RP6 eingeschaltet ist. Die Anzeige wird nur aktiv, wenn das Programm selbst keine LEDs ansteuert und soll verhindern, dass die Akkus leergesaugt werden.
Gruß
mic
[Edit]
Die Anschlüsse SL1-6 sind die Lötpads IO1-4 und BPL/PBR auf der RP6-Platine neben den LEDs:
http://radbruch.roboterbastler.de/rp6/greifer2/servoanschluss4_klein.jpg (http://radbruch.roboterbastler.de/rp6/greifer2/servoanschluss4.jpg)
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=356247#356247
hi mic,
also XBUS1- 1,3, und 10 stimmen
- wenn ich den RP6 einschalte leuchtet SL2, 5 und PWRON auf, erlischen dann und SL1 blinkt. (servo ist angeschlossen)
- beim drücken des tasters leuchten kurz SL2 und 6 auf.
- beim abziehen, bzw. wiederaufstecken des servokontaktes auf SCL bewegt sich der servo und bleibt in der endposition (unter spannung) stehen
- beim abgeschalteten RP6 und beim bewegen des servorotors leuchten SL2, 5 und PWRON
das blinken der SL5 bei laufendem programm ist die anzeige dafür das der RP6 eingeschaltet ist und ein programm läuft, ok?
radbruch
07.01.2010, 17:02
Hallo
Ich habe jetzt die drei Beispiele von oben nochmals kompiliert und getestet. Mein Servo an SCL dreht dabei wie erwartet (im StopWatch()-Beispiel zuckelt es etwas), also muss dein Servo oder der Anschluss fehlerhaft sein.
Das Hex vom Timer2-OVL-ISR-Demo oben erzeugt Servoimpulse an SDA, SCL und E-INT.
Gruß
mic
hi mic,
danke erstmal, werde also schauen, dass ich ein enderes servo zum testen bekommme.
Zwei fragen noch:
- das servo ist extra, ich habe es bei conrad sonts an keinem gesehen, mit "digital" gekennzeichnet. muss es deshalb evtl. anders behandelt werden?
- vielleicht ist durch editieren meine frage untergegangen: sind die SL1-6 anschlüsse noch irgendwo herausgeführt, oder muss ich an den dioden rumlöten?
man kann digitale servos auch an normalen rcempfängern anschliesen,
also auch wie normale Servos behandeln !
digitale sind etwas genauer und schneller, brauchen aber auch mehr strom, weil sie öfter nachregeln.
hi,
ich hab´s jetzt mit der sw von Dirk zum laufen gebracht, also versorgung am PIN 1 & 3 der XBUS1 und steuerung über SL1. Sogar die herausgeführten anschlüsse habe ich gefunden, man muss halt doch in den schaltplan schauen :oops: dann findet man sachen!
noch eine frage: der servo fährt zuerst die 0-pos an, dann die zweite position (von vorn gesehen nach rechts) und kehrt nach einer weile wieder in die 0-pos zurück. manchmal zuckt er in der rechten position zuerst etwas inn richtung 0-pos, aber erst nach ca. 1 sec fährt er los. Nicht bei jedem rücklauf. Woran kann es liegen?
danke...
ich kämpfe grade mit einem digitalen Miniservo von Conrad,
wenn ich den Ansteuern will, bricht sofort alles zusammen, und manchmal erschent auf dem Display E4.
Selbst wenn ich den servo nicht ansteuere, sodern nur das Horn antippse resetten die Atmegas.
Ich würde so vermuten das bei mir wie bei dir inka der Servo etwas zu viel zieht.
hilft da ein dicker Elo?
wie müsste man ihn dimensionieren?
radbruch
09.01.2010, 13:23
Hallo
Also irgendetwas macht ihr falsch. Mein RP6 treibt locker 6 Billigservos. Und wenn du mit "digitalen Miniservo" dieses kleine blaue Servo meinst, damit hatte ich noch nie Probleme. Mein Deltarobot mit 4 Servos wird übrigens auch mit der RP6-Lib verträglichen Timer2-OVL-ISR gesteuert:
https://www.roboternetz.de/phpBB2/viewtopic.php?p=432541#432541
Gruß
mic
hi,
offensichtlich, aber was? Ich habe festgestellt, dass diesem "zucken" in der rechten position offensichtlich ein reset vorausgeht, oder hat das aufleuchten aller LED´s eine andere bedeutung? Es ist auch nicht nachvollziehbar wann das passiert, ist also völlig villkürlich...
radbruch
09.01.2010, 18:29
Vielleicht fährt das Servo gegen seinen mechanischen Anschlag und blockiert dann. Das würde einen extrem großen Strom verursachen. Der Spannungsregler auf dem RP6 hat einen Überlastschutz der in bei Überstrom abschaltet. Wenn ich mich nicht irre kann er ca. 1A, im Dauerbetrieb allerdings nur mit zusätzlichem Kühlkörper.
Wie sieht denn dein aktuelles Programm aus? Ich könnte es mal mit meinem RP6 testen.
der servo bleibt eigendlich einfach irgendwo stehen,
bei einer impulsdauer von 1,5 dürfte da doch kein anschlag sein, oder?
radbruch
09.01.2010, 18:39
Dreh doch mal das Servo von Hand von Anschlag zu Anschlag, dann siehst du, wo der Drehbereich endet. Die blauen digitalen Miniservos haben übrigends keinen Endanschlag.
Selbst wenn ich den servo nicht ansteuere, sodern nur das Horn antippse resetten die Atmegas.Wie schon oben geschrieben fallen in den Billigservos gelegentlich die Anschlussleitungen von der Platine ab. Wenn sich dann die Kabel berühren gibt es möglicherweise einen Kurzschluss.
hi mic,
ich verwende das demoprogramm von Dirk aus diesem thread, so wie ich das sehe ist weder der linke noch der rechte wendepunkt identisch mit einem anschlag...
die litzen sind gut angelötet...
Hallo inka,
... so wie ich das sehe ist weder der linke noch der rechte wendepunkt identisch mit einem anschlag...
Da alle Servos unterschiedlich sind, must du LEFT_TOUCH und RIGHT_TOUCH so anpassen, dass es bei deinem Servo auf beiden Seiten nicht gegen den Anschlag arbeitet.
Insofern haben die Grenzwerte schon etwas mit dem Anschlag zu tun.
Auf deutsch: Wenn's brummt, ist der Anschlag erreicht.
Gruß Dirk
radbruch
09.01.2010, 21:56
Hallo
Ich habe Dirks Stopwatch-Ansteuerung nun auch getestet und es funktioniert ohne Probleme, wenn die Werte innerhalb des mechanischen Drehbereichs sind. Die Maxwerte für mein ES-030-Servo sind ca.:
#define LEFT_TOUCH 515 // Left servo touch
#define RIGHT_TOUCH 200 // Right servo touch [max. 255]
Was mir an task_Servo() nicht so recht gefällt ist der Rücklauf nach dem erreichen der rechten Endposition. Es wird dann sofort auf pos=0 geschaltet, das Servo hat aber keine Zeit, die linke Position anzufahren. Da bei diesem Seitenwechsel pos wieder erhöht wird, ist es von der Servosgeschwindigkeit abhängig, wann die Servoposition wieder mit pos syncron wird. (Das kann man schön sehen, wenn man pos++ nach pos+=5 ändert). Wenn nun LEFT_TOUCH zu klein ist, könnte das Servo kurzzeitig gegen den linken Anschlag fahren.
Da sich die rechte Endposition aus LEFT_TOUCH und RIGHT_TOUCH zusammensetzt, würde ich LEFT_TOUCH erstmal ziemlich groß und RIGHT_TOUCH eher klein wählen, beides nahe der Drehbereichsmitte, und dann Schrittweise ändern.
Gruß
mic
hi mic,
was mich jetzt wundert ist, dass hier offensichtlich von mehreren durchläufen der li/re bewegung ausgegangen wird. Bei mir bewegt sich der servo einmal nach rechts, dann nach links, dann steht er bis ich wieder den taster drücke.
@Dirk:
die mechanischen anschläge (ich dachte die sind hier gemeint) liegen jeweils außerhalb des bereiches in dem der servo elektronisch bewegt wird,,,
radbruch
09.01.2010, 22:45
Hallo
Also ich habe jetzt dieses Programm ausprobiert:
https://www.roboternetz.de/phpBB2/viewtopic.php?p=318650#318650
Die Impulslänge für die Servoposition setzt sich aus LEFT_TOUCHT und pos zusammen:
void pulseSERVO(uint8_t position)
{
cli();
// Impulsstart, Ausgang auf High
PORTC |= SERVO_OUT;
// Der Gesamtimpuls besteht aus zwei Teilen, zuerst der Anteil für linke Position
delayCycles(LEFT_TOUCH);
// und dann der Abstand von Links zum gewünschten Drehwinkel in Einzelschritten*pos
while (position--) {
delayCycles(PULSE_ADJUST);
}
// Impuls ist gesendet, Ausgang wieder auf Low
PORTC &= ~SERVO_OUT; // SERVO_OUT -> LO (pulse end)
sei();
}
void task_SERVO(void)
{static uint8_t pos;
// wenn Stopwatch1 größer 19 ist sind 20ms seit dem letzten Impulsstart vergangen
if (getStopwatch1() > PULSE_REPETITION) {
//Impuls zum Servo senden
pulseSERVO(pos);
// ---------------------------------------------------------------------
// Nach ca. 50ms wird der Impuls für einen neuen Winkel berechnet
if (getStopwatch2() > 48) {
pos++; // Impulslänge um einen Schritt erhöhen
//pos+=3; // größere Schritte
// Wenn der rechte Endpunkt erreicht ist, starten wir wieder links
if (pos > RIGHT_TOUCH) {pos = 0;}
setStopwatch2(0); // restart der 50ms-Verzögerung
}
// ---------------------------------------------------------------------
setStopwatch1(0); // restart der 20ms-Impulswiederholung
}
}
Und das wird eben endlos wiederholt.
Gruß
mic
morgen mic,
habe deinen codeschnipsel (hoffe richtig) ergänzt
#include "RP6RobotBaseLib.h"
#include "RP6BaseServoLib.h"
int main(void)
{
initRobotBase();
while(true)
{
void pulseSERVO(uint8_t position)
{
cli();
// Impulsstart, Ausgang auf High
PORTC |= SERVO_OUT;
// Der Gesamtimpuls besteht aus zwei Teilen, zuerst der Anteil für linke Position
delayCycles(LEFT_TOUCH);
// und dann der Abstand von Links zum gewünschten Drehwinkel in Einzelschritten*pos
while (position--) {
delayCycles(PULSE_ADJUST);
}
// Impuls ist gesendet, Ausgang wieder auf Low
PORTC &= ~SERVO_OUT; // SERVO_OUT -> LO (pulse end)
sei();
}
void task_SERVO(void)
{static uint8_t pos;
// wenn Stopwatch1 größer 19 ist sind 20ms seit dem letzten Impulsstart vergangen
if (getStopwatch1() > PULSE_REPETITION) {
//Impuls zum Servo senden
pulseSERVO(pos);
// ---------------------------------------------------------------------
// Nach ca. 50ms wird der Impuls für einen neuen Winkel berechnet
if (getStopwatch2() > 48) {
pos++; // Impulslänge um einen Schritt erhöhen
//pos+=3; // größere Schritte
// Wenn der rechte Endpunkt erreicht ist, starten wir wieder links
if (pos > RIGHT_TOUCH) {pos = 0;}
setStopwatch2(0); // restart der 50ms-Verzögerung
}
// ---------------------------------------------------------------------
setStopwatch1(0); // restart der 20ms-Impulswiederholung
}
}
}
return 0;
}
die libs von Dirk eingebunden, beim versuch zu kompilieren kommt:
../servo_radbr_2.c: In function `pulseSERVO':
../servo_radbr_2.c:13: error: `SERVO_OUT' undeclared (first use in this function)
../servo_radbr_2.c:18: error: `PULSE_ADJUST' undeclared (first use in this function)
wo, bzw. wie muss ich diese deklarieren? sind sie in Deinen libs?
danke...
radbruch
10.01.2010, 13:11
Hallo
Das ist nun wirklich totaler Schrott. Wir sollten uns mal wieder synchronisieren:
Ich:
Wie sieht denn dein aktuelles Programm aus? Ich könnte es mal mit meinem RP6 testen.
Du:
ich verwende das demoprogramm von Dirk aus diesem thread
Ich:
Suche ein Demoprogramm von Dirk in diesem Thread und finde das:
/*
* ************************************************** **************************
* RP6 ROBOT SYSTEM - ROBOT BASE EXAMPLES
* ************************************************** **************************
* Example: Simple Servo Example
* Author(s): D. Ottensmeyer
* ************************************************** **************************
* Description:
* This example shows how to use a standard servo with RP6Library.
* There are much better solutions to do this job, but you may use
* this simple program to make some first experiments with servos.
* The servo is connected to PINC1 (SDA).
*
* ################################################## ##########################
* The Robot does NOT move in this example! You can simply put it on a table
* next to your PC and you should connect it to the PC via the USB Interface!
* ################################################## ##########################
* ************************************************** **************************
*/
/************************************************** ***************************/
// Includes:
#include "RP6RobotBaseLib.h" // The RP6 Robot Base Library.
// Always needs to be included!
/************************************************** ***************************/
// Defines:
// RP6 Base Servo Connection:
#define SERVO_OUT SDA // PINC1 XBUS Pin 12
// Servo movement limits (depending on servo type):
#define LEFT_TOUCH 550 // Left servo touch
#define RIGHT_TOUCH 254 // Right servo touch [max. 255]
#define PULSE_ADJUST 4
#define PULSE_REPETITION 19 // Pulse repetition frequency
/************************************************** ***************************/
// Functions:
/**
* INIT SERVO
*
* Call this once before using the servo task.
*
*/
void initSERVO(void)
{
DDRC |= SERVO_OUT; // SERVO_OUT -> OUTPUT
PORTC &= ~SERVO_OUT; // SERVO_OUT -> LO
startStopwatch1(); // Needed for 20ms pulse repetition
}
/**
* PULSE SERVO
*
* This is the servo pulse generation. This function
* must be called every 20ms (pulse repetition).
*
* position = 0 : Left touch
* position = RIGHT_TOUCH : Right touch
*
* ATTENTION: ! This function is BLOCKING all other activities for about 1 !
* ! to 2ms (depending on the value of position)!!! !
* ! If you generate a pulse every 20ms 5-10% of the processor's !
* ! calculating time is wasted by this kind of pulse generation. !
* ! If this is a problem for the rest of your program, you !
* ! cannot use this method. !
* ! You will need an interrupt-based solution instead. !
*
*/
void pulseSERVO(uint8_t position)
{
cli();
PORTC |= SERVO_OUT; // SERVO_OUT -> HI (pulse start)
delayCycles(LEFT_TOUCH);
while (position--) {
delayCycles(PULSE_ADJUST);
}
PORTC &= ~SERVO_OUT; // SERVO_OUT -> LO (pulse end)
sei();
}
/**
* SERVO TASK
*
* This is the servo demo task.
* The positioning demo shows the servo lever rapidly
* moving to the left touch, then slowly moving to
* the right touch and so on ...
*
*/
void task_SERVO(void)
{static uint8_t pos;
if (getStopwatch1() > PULSE_REPETITION) { // Pulse every ~20ms
pulseSERVO(pos); // Servo pulse [1..2ms]
// ---------------------------------------------------------------------
// Your test code for positioning the servo here:
if (getStopwatch2() > 48) { // Change position every ~50ms
pos++; // Next position to the right
if (pos > RIGHT_TOUCH) {pos = 0;} // pos: 0..RIGHT_TOUCH
setStopwatch2(0);
}
// ---------------------------------------------------------------------
setStopwatch1(0);
}
}
/************************************************** ***************************/
// Main function - The program starts here:
int main(void)
{
initRobotBase(); // Always call this first! The Processor will not work
// correctly otherwise.
// ---------------------------------------
// Write messages to the Serial Interface:
writeString_P("\n\n _______________________\n");
writeString_P(" \\| RP6 ROBOT SYSTEM |/\n");
writeString_P(" \\_-_-_-_-_-_-_-_-_-_/\n\n");
writeString_P("################\n");
writeString_P("<<RP6 Base>>\n");
writeString_P(" Servo - Test 1 \n");
writeString_P(" Version 1.00 \n");
writeString_P("################\n\n");
mSleep(2500);
setLEDs(0b111111); // Turn all LEDs on
mSleep(500); // delay 500ms
setLEDs(0b000000); // All LEDs off
initSERVO();
startStopwatch2(); // Used for the demo task
while(true)
{
task_SERVO();
task_RP6System();
}
return 0;
}
/************************************************** ****************************
* Additional info
* ************************************************** **************************
* Changelog:
* - v. 1.0 (initial release) 03.10.2007 by D. Ottensmeyer
*
* ************************************************** **************************
*/
/************************************************** ***************************/
(Fundstelle in diesem Thread: https://www.roboternetz.de/phpBB2/viewtopic.php?p=318650#318650)
Dieses Demo habe ich dann ausprobiert, die Werte an mein Servo angepasst, nach möglichen Fehlern gesucht (wobei Dirk es ja drauf hat und weiß, wie man gut programmiert) und schließlich die entscheidenden Programmteile nochmals kommentiert.
Bin jetzt Schneeschippen.
Gruß
mic
hi mic,
schneeschippen ist manchmal eine echte alternative...
ich habe diese demo gemeint (war bei den lib´s von Dirk mit dabei):
// Uncommented Version of RP6BaseServo.c
// written by Dirk
// ------------------------------------------------------------------------------------------
#include "RP6BaseServoLib.h"
uint16_t pos = 0;
int main(void)
{
initRobotBase();
writeString_P("\n\n _______________________\n");
writeString_P(" \\| RP6 ROBOT SYSTEM |/\n");
writeString_P(" \\_-_-_-_-_-_-_-_-_-_/\n\n");
writeString_P("################\n");
writeString_P("<<RP6 Base>>\n");
writeString_P(" Servo - Test 1 \n");
writeString_P(" Version 1.20 \n");
writeString_P("################\n\n");
mSleep(2500);
setLEDs(0b111111);
mSleep(500);
initSERVO(SERVO1 | SERVO2);
startStopwatch2();
while(true)
{
if (getStopwatch2() > 48) {
servo1_position = pos;
servo2_position = pos;
pos++;
if (pos > RIGHT_TOUCH) {pos = 0;}
setStopwatch2(0);
}
task_SERVO();
task_RP6System();
}
return 0;
}
nun werde ich den von Dir zuletzt geposteten code probieren…
bis dann danke…
folgenden code habe ich nun getestet:
(servo angeschlossen an XBUS1 1,3,12)
/*
* ************************************************** **************************
* RP6 ROBOT SYSTEM - ROBOT BASE EXAMPLES
* ************************************************** **************************
* Example: Simple Servo Example
* Author(s): D. Ottensmeyer
* ************************************************** **************************
* Description:
* This example shows how to use a standard servo with RP6Library.
* There are much better solutions to do this job, but you may use
* this simple program to make some first experiments with servos.
* The servo is connected to PINC1 (SDA).
*
* ################################################## ##########################
* The Robot does NOT move in this example! You can simply put it on a table
* next to your PC and you should connect it to the PC via the USB Interface!
* ################################################## ##########################
* ************************************************** **************************
*/
/************************************************** ***************************/
// Includes:
#include "RP6RobotBaseLib.h" // The RP6 Robot Base Library.
// Always needs to be included!
/************************************************** ***************************/
// Defines:
// RP6 Base Servo Connection:
#define SERVO_OUT SDA // PINC1 XBUS Pin 12
// Servo movement limits (depending on servo type):
#define LEFT_TOUCH 100 // Left servo touch
#define RIGHT_TOUCH 50 // Right servo touch [max. 255]
#define PULSE_ADJUST 4
#define PULSE_REPETITION 19 // Pulse repetition frequency
/************************************************** ***************************/
// Functions:
/**
* INIT SERVO
*
* Call this once before using the servo task.
*
*/
void initSERVO(void)
{
DDRC |= SERVO_OUT; // SERVO_OUT -> OUTPUT
PORTC &= ~SERVO_OUT; // SERVO_OUT -> LO
startStopwatch1(); // Needed for 20ms pulse repetition
}
/**
* PULSE SERVO
*
* This is the servo pulse generation. This function
* must be called every 20ms (pulse repetition).
*
* position = 0 : Left touch
* position = RIGHT_TOUCH : Right touch
*
* ATTENTION: ! This function is BLOCKING all other activities for about 1 !
* ! to 2ms (depending on the value of position)!!! !
* ! If you generate a pulse every 20ms 5-10% of the processor's !
* ! calculating time is wasted by this kind of pulse generation. !
* ! If this is a problem for the rest of your program, you !
* ! cannot use this method. !
* ! You will need an interrupt-based solution instead. !
*
*/
void pulseSERVO(uint8_t position)
{
cli();
// Impulsstart, Ausgang auf High
PORTC |= SERVO_OUT;
// Der Gesamtimpuls besteht aus zwei Teilen, zuerst der Anteil für linke Position
delayCycles(LEFT_TOUCH);
// und dann der Abstand von Links zum gewünschten Drehwinkel in Einzelschritten*pos
while (position--) {
delayCycles(PULSE_ADJUST);
}
// Impuls ist gesendet, Ausgang wieder auf Low
PORTC &= ~SERVO_OUT; // SERVO_OUT -> LO (pulse end)
sei();
}
/**
* SERVO TASK
*
* This is the servo demo task.
* The positioning demo shows the servo lever rapidly
* moving to the left touch, then slowly moving to
* the right touch and so on ...
*
*/
void task_SERVO(void)
{static uint8_t pos;
// wenn Stopwatch1 größer 19 ist sind 20ms seit dem letzten Impulsstart vergangen
if (getStopwatch1() > PULSE_REPETITION) {
//Impuls zum Servo senden
pulseSERVO(pos);
// ---------------------------------------------------------------------
// Nach ca. 50ms wird der Impuls für einen neuen Winkel berechnet
if (getStopwatch2() > 48) {
pos++; // Impulslänge um einen Schritt erhöhen
//pos+=3; // größere Schritte
// Wenn der rechte Endpunkt erreicht ist, starten wir wieder links
if (pos > RIGHT_TOUCH) {pos = 0;}
setStopwatch2(0); // restart der 50ms-Verzögerung
}
// ---------------------------------------------------------------------
setStopwatch1(0); // restart der 20ms-Impulswiederholung
}
}
/************************************************** ***************************/
// Main function - The program starts here:
int main(void)
{
initRobotBase(); // Always call this first! The Processor will not work
// correctly otherwise.
// ---------------------------------------
// Write messages to the Serial Interface:
writeString_P("\n\n _______________________\n");
writeString_P(" \\| RP6 ROBOT SYSTEM |/\n");
writeString_P(" \\_-_-_-_-_-_-_-_-_-_/\n\n");
writeString_P("################\n");
writeString_P("<<RP6 Base>>\n");
writeString_P(" Servo - Test 1 \n");
writeString_P(" Version 1.00 \n");
writeString_P("################\n\n");
mSleep(2500);
setLEDs(0b111111); // Turn all LEDs on
mSleep(500); // delay 500ms
setLEDs(0b000000); // All LEDs off
initSERVO();
startStopwatch2(); // Used for the demo task
while(true)
{
task_SERVO();
task_RP6System();
}
return 0;
}
/************************************************** ****************************
* Additional info
* ************************************************** **************************
* Changelog:
* - v. 1.0 (initial release) 03.10.2007 by D. Ottensmeyer
*
* ************************************************** **************************
*/
/************************************************** ***************************/
- beim einschalten lässt sich der servo beliebig durchdrehen (ist spannungsfrei), nur durch die mechanischen anschläge begrenzt, grüne LED1 blinkt
- druck auf den taster: rechte position (oder die nächste?) wird gesucht, elektrischer "anschlag" gefunden, reset (alle 6 LED´s leuchten auf), servo steht unter spannung, grüne LED1 blinkt
- beim nächsten druck auf den taster wieder reset, servo zuckt nach links, bleibt dann aber stehen
der letzte punkt wiederholt sich bei jedem druck auf den taster
radbruch
10.01.2010, 17:09
Ich weiß echt nicht ob ich lachen oder weinen soll:
#define LEFT_TOUCH 100 // Left servo touch
#define RIGHT_TOUCH 50 // Right servo touch [max. 255]
Die 100 für LEFT_TOUCH sind definitiv viel zu wenig! Erhöhe jetzt mal diesen Wert in 100er-Schritten (200, 300, 400, 500) und teste jedesmal, was passiert.
LEFT_TOUCH ist der Wert für die kleinste Impulslänge, LEFT_TOUCH+RIGHT_TOUCH ergibt dann den längsten Impuls.
Ich will dich wirklich nicht unnötig verwirren, aber wir quälen uns hier durch Dirks Servoprogramme und verlieren dabei zunehmend das eigentliche Ziel aus den Augen: Wir wollen ein Servo ansteuern und müssen dazu einen Impuls erzeugen den das Servo als Drehwinkel interpretiert. Dieser Impuls sollte ca. 1,5ms lang sein und alle 20ms wiederholt werden:
// Servoansteuerung1: Wir erzeugen den Impuls mit sleep() 10.1.2010 mic
// sleep(1) verzögert ca. 0,1ms
// Servo an SDA (PC1) (XBUS Pin 12)
#include "RP6RobotBaseLib.h"
int main(void)
{
initRobotBase();
DDRC |= SDA;
PORTC &= ~SDA;
while(1)
{
PORTC |= SDA; // Impulsstart
sleep(15); // 1,5ms warten
PORTC &= ~SDA; // Impulsende
sleep(185); // 18,5ms warten
}
return(0); // wird nie erreicht
}
Bevor das Programm gestartet wird, drehst du das Servo von Hand an einen Anschlag (links oder rechts ist egal, du kannst beides ausprobieren). Wenn dann das Programm gestartet ist, dreht das Servo auf eine Position (ca. Mitte) und bleibt dort. Wenn du nun mit der Hand versuchst das Servo zu verdrehen, spürst du deutlich einen Widerstand.
Wenn das funktioniert ist dein Servo in Ordnung und der Anschluß richtig. Melde dich dann bitte wieder.
Gruß
mic
hin mic,
also lachen find ich viel besser, der servo ist also ok...
zu dem zu kleinen wert - ich hatte einfach schiss etwas kaputt zu machen, jetzt teste ich die 100er schritte
danke, ich melde mich dann wieder...
radbruch
10.01.2010, 18:42
Hallo
Das "der servo ist also ok" interpretiere ich jetzt mal als "die Ansteuerung mit sleep() funktioniert". Dann wäre jetzt Schritt zwei an der Reihe:
// Servoansteuerung2: Wir erzeugen zwei Impulse im Wechsel 10.1.2010 mic
// sleep(1) verzögert ca. 0,1ms, 50 Impulse dauern ca. 1 Sekunde
// Servo an SDA (PC1) (XBUS Pin 12)
#include "RP6RobotBaseLib.h"
uint8_t c; // allgemeine 8-Bit-Variable (ein Char)
int main(void)
{
initRobotBase();
DDRC |= SDA;
PORTC &= ~SDA;
while(1)
{
for(c=0; c<50; c++) // 50 Impulse senden, das dauert ca. 1 Sekunde
{
PORTC |= SDA; // Impulsstart
sleep(12); // 1,2ms warten = Servoposition 1
PORTC &= ~SDA; // Impulsende
sleep(190); // 19ms warten
}
for(c=0; c<50; c++)
{
PORTC |= SDA;
sleep(18); // 1,8ms warten = Servoposition 2
PORTC &= ~SDA;
sleep(190);
}
}
return(0); // wird nie erreicht
}
Das Servo sollte damit im Wechsel zwei Positonen anfahren, dazu hat es jeweils eine Sekunde Zeit. Wenn du die Werte für die jeweiligen Positionen änderst um den maximalen Drehbereich auszutesten sollte das Servo in der jeweiligen Position kurz verweilen. Wenn es dies nicht macht, muss die Zeit (=maxwert für c in der Schleife) solange erhöht werden, bis die kurze Pause erkennbar ist.
Wie gehts nun weiter? Entweder erhöhen wir die Auflösung der Positionen (und trennen uns damit von der sleep()-Ansteuerung) oder wir fahren die Positionen langsamer an oder wir steuern gleichzeitig ein zweites Servo an. Sag was du willst/brauchst.
Gruß
mic
void Servo(uint8_t pos)
{
DDRC |= IO_PC5;
PORTC |= IO_PC5; // Impulsstart
sleep(pos); // 1,5ms warten
PORTC &= ~IO_PC5; // Impulsende
sleep(185); // 18,5ms warten
}
wenn ich dann in Servo(pos); werte zwischen 5 und 15 einsetze, dreht der servo gegen den Anschlag, immer gegen den Uhrzeigersinn,
Edit: hab den Fehler selbst bemerkt, unter 10 ist voll sinnlos.
aber auch nach Servo(20); oder Servo(30); dreht der Servo bis zum anschlag gegen den Uhrzeigersinn.
schmiereck
10.01.2010, 18:48
Hallo radbruch,
bezugnehmend auf den Beitrag https://www.roboternetz.de/phpBB2/viewtopic.php?p=432541#432541 will ich fragen, welche Hardware Ausgänge denn mit dem Port-C angesteuert werden.
Ich gebe zu, ich habe mich nich gänzlich in das Thema eingelesen, aber ich denke hierzu "fehlen" die Angaben zu dem Beitrag.
Werden die auch am XBUS abgenommen oder sind das spezielle "Tricks"?
Grüße,
smk
radbruch
10.01.2010, 20:33
Leute, ihr schafft mich ;)
@Virus: Wenn die Servomitte ca. 1,5ms ist (manche Dokus sagen auch 1ms), dann ist 0,5 oder gar 3ms weit außerhalb des Drehbereichs.
@schmiereck: Bei diesem Beispiel mit dem Deltabot verwende ich die LEDs (IO1-4 und BPL/BPR) am Userbus1 den ich bei meinem RP6 so belegt habe:
SL1 1 - 2 GND
SL2 3 - 4 Vcc
SL3 5 - 6 ADC0
SL4 7 - 8 ADC7
SL5 9 - 10 ADC1
SL6 10- 12 Vcc
frei 13- 14 GND
Dabei war mein Ziel möglichst viele Pins für externe Erweiterungen rauszuführen. An 2-4-6 bzw. 14-12-10 kann man direkt ein Servo einstecken. ADC7 ist übrigends der Batterie-ADC den ich auf der Platine unterbrochen und mit Jumper umgerüstet habe.
Hier gibts die USRBUS-Belegung von Dirk die für den Anschluß einer Erweiterungsplatine viel sinnvoller ist:
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=32549
Gruß
mic
aber alle werte zwischen 5 und 30 hauen den servo gegen den linke anschag
radbruch
10.01.2010, 22:17
Hallo Virus,
dass dein Servo immer an der selben Seite anschlägt habe ich leider überlesen. Da sleep() auch beim M32 (es geht doch um ein M32, oder?) mit 10kHz getaktet wird und deshalb ein sleep(1) wohl auch 0,1ms dauert, erscheint mir ein Fehler im aufrufenden Programm von servo() als Ursache für deinen Effekt. Wird initRP6Control() aufgerufen? Wie sieht das restliche Programm (aufs wesentliche reduziert) aus.
Gruß
mic
schmiereck
10.01.2010, 22:27
OK, verstanden, danke vielmals.
Das war das, was mir gefehlt hat.
In der vermaldeteiten (und wirklich nicht schlechten) Doku habe ich nur die Infos auf die zwei freie Analog/Digital Wandler (ADC) Kanäle für eigene Sensoren." auf Seite 10 gefunden. Das irgendwo, bei bei Motorstrom und Batterie (S.90), noch zwei freie Kanäle dokumentiert sind, da muss man erst einmal drauf stossen... (danke für die Hinweisse)
Mein Fazit, erst einmal, ich warte auf mein Erweiteruns-Board. Das "missbrauchen" von "Betriebssystem-Funktionen" ist mir zu krass, ich finde den Grundaufbau nicht schlecht und will ihn nicht unbedingt ändern (ADC7 vierbiegen) - nix gegen basten, ich liebe es - aber das ist mir dann doch alles zu komplex, ich will ja meine Software zum laufen bringen...
Ich weiss schon was radbruch sagen wird: da musst du nur in der baseLib die Zeilen xy rausnhemen, und dann... ;-)
@radbruch: Warum benutzt du nicht so ein Erweiterungs-Borad? Sind das praktische Erwägungen?
Gruß,
smk
Laut den ganzen Beschreibung müsste dieser Code doch eigentlich funktionieren (mit den versch. Werten).
//Beschreibung: imp ist die Impulslänge (0.1ms=1)
void servo_io6 (int imp)
//Kanal als Ausgang
//Impuls für imp . ms
//Pause bis zum nächsten Impuls
{
DDRC |= IO_PC6;
PORTC |= IO_PC6;
sleep(imp);
PORTC &= ~IO_PC6;
sleep(200-imp);
}
Ich habe jetzt schon die verschiedenen Werte probiert (von 10 bis 20).
Das Servo bewegt sich zwar, aber überhaupt nicht weit (ca. 10 Grad, wenn es überhaupt 10 sind).
Die Mittelstellung liegt im Vergleich mit einem Graupner Empfänger bei 13ms.
Soll ich mal andre Werte versuchen, wenn ja welche oder habe ich einen Fehler eingebaut, etc.?
PS: Das Servo soll nach Möglichkeit am Leben bleiben ... ist eigentlich das Höhenruderservo eines Modellfliegers und auch noch eingebaut
radbruch
10.01.2010, 23:48
Hallo
Mal ehrlich, was soll man mit den Codebrocken anfangen die ihr uns hier vorwerft? Wenn die Servo()-Funktion von Virus nicht funktioniert und deine quasi identische Funktion (außer der Mitdenkeränderung sleep(200-imp);) auch nicht funktioniert, woran könnte das dann liegen? Funktionieren meine beiden sleep()-Beispiele von oben wenn ihr sie ans M32 anpasst? Wie sieht das angepasste Programm aus? Ich weiß immer noch nicht, ob ihr überhaupt ein M32 verwendet. Wie und wo ist IO_PCx definiert? Warum ist bei dir imp ein Integer?
@schmiereck: Für's M32 bin ich schlicht zu geizig ;)
Gruß
mic
Ich kann mir nicht vorstellen wo sonst der Fehler liegen sollte ... hoffe ja doch dass ich ne übergabe eines Wertes in ein Nebenprogramm zusammenbringe.
#include "RP6ControlLib.h"
//Beschreibung: imp ist die Impulslänge (0.1ms=1)
//Servo an PC5
void servo_io5 (int imp)
//Kanal als Ausgang
//Impuls für imp . ms
//Pause bis zum nächsten Impuls
{
DDRC |= IO_PC5;
PORTC |= IO_PC5;
mSleep(imp);
PORTC &= ~IO_PC5;
mSleep(200-imp);
}
int main(void)
{
initRP6Control();
initLCD();
showScreenLCD("RP6 Control", "RP6");
clearLCD();
sleep(500);
showScreenLCD("Servobewegung", "");
sleep(1500);
int i=0, x;
while(i<100) //2000ms
{
servo_io5(13);
//i=i+1;
}
i=0;
x=10;
while(true)
{
while(i<10) //200ms
{
servo_io5(x);
}
x=x+1;
if (x>=20)
{
x=10;
}
}
while(true)
{
showScreenLCD("Endlosschleife", "");
}
return 0;
}
Ich verwende die Erweiterungsplatine M32 und IO_PC6 (in diesem Fall) ist in der Library RP6ControlLib.h definiert.
Man kann mit den Befehlen dann die verschiedensten Sachen machen:
Wenn man einen I/O Pin als Ausgang verwenden will, um z.B. eine LED zu schalten,
muss man das entsprechende Bit im DDRx Register auf 1 setzen.
Beispiel:
DDRC |= IO_PC7; // PC7 ist nun Ausgang
DDRC = IO_PC7 | IO_PC6 | IO_PC5; // PC5, PC6, PC7 sind nun Ausgang,
// alle anderen Pins sind Eingänge!
Dann kann man über das PORTx Register den Ausgang auf high oder low pegel schalten.
Beispiel:
PORTC |= IO_PC7; // High
PORTC &= ~IO_PC7; // Low
Ist ein Bit im DDRx Register 0, so ist der zugehörige Pin als Eingang konfiguriert.
Beispiel:
DDRC &= ~IO_PC6; // PC6 ist nun Eingang
Dann kann man über das PINx Register den Zustand des Pins auslesen, also ob high
oder low Pegel am Pin anliegt.
if(PINC & IO_PC6)
writeString_P("PC6 is HIGH!\n");
else
writeString_P("PC6 is LOW!\n");
aus der Beschreibung des M32 Moduls
Ich habe den Code jetzt auf PC5 umgeschrieben. Ich bin mir allerding bei der Sache mit sleep(.ms) mittlerweile nicht mehr sicher.
Wie sollte ich die imp - Variable sonst deklarieren? Müsste doch eigentlich passen.
radbruch
11.01.2010, 08:28
Hallo
Ich vermute, die IO-PCx werden in iom32.h der AVR-Includes definiert, bin aber grad zu faul zu suchen;)
Wie kommt man denn aus diesen Schleifen wieder raus:
while(i<100) //2000ms
{
servo_io5(13);
}
...
while(i<10) //200ms
{
servo_io5(x);
}
i wird nirgends verändert.
Gruß
mic
ich glaub das war noch der code bevor ich es probiert habe ... werds umbessern
... wir sind ja alle nur Menschen :mrgreen:
@radbruch:
Wenn die IO Port in einer anderen Lib definiert sind, wieso funktioniert es dann z.B. einen Tranistor so (fast so) anzusteuern?
edit:
ich hab beim probiern das i++ weggenommen ...
dann bleibt das servo immer in der mittelstellung und ich muss nichts im Code ändern
radbruch
11.01.2010, 13:12
Hallo
Die IO_PCx werden in RP6Control.h als (1<<PCx) definiert, das ist also ok. Sonst würde sich wohl auch gar nichts tun. iom32.h wird automatisch von avr/io.h eingebunden, dieses wiederum steht ebenfalls in RP6Control.h
Gruß
mic
Hallo
Das "der servo ist also ok" interpretiere ich jetzt mal als "die Ansteuerung mit sleep() funktioniert".
ja, die servoansteuerung1 vehält sich wie Du es beschreibst...
Dann wäre jetzt Schritt zwei an der Reihe:
// Servoansteuerung2: Wir erzeugen zwei Impulse im Wechsel 10.1.2010 mic
// sleep(1) verzögert ca. 0,1ms, 50 Impulse dauern ca. 1 Sekunde
// Servo an SDA (PC1) (XBUS Pin 12)
#include "RP6RobotBaseLib.h"
uint8_t c; // allgemeine 8-Bit-Variable (ein Char)
int main(void)
{
initRobotBase();
DDRC |= SDA;
PORTC &= ~SDA;
while(1)
{
for(c=0; c<50; c++) // 50 Impulse senden, das dauert ca. 1 Sekunde
{
PORTC |= SDA; // Impulsstart
sleep(12); // 1,2ms warten = Servoposition 1
PORTC &= ~SDA; // Impulsende
sleep(190); // 19ms warten
}
for(c=0; c<50; c++)
{
PORTC |= SDA;
sleep(18); // 1,8ms warten = Servoposition 2
PORTC &= ~SDA;
sleep(190);
}
}
return(0); // wird nie erreicht
}
Das Servo sollte damit im Wechsel zwei Positonen anfahren, dazu hat es jeweils eine Sekunde Zeit. Wenn du die Werte für die jeweiligen Positionen änderst um den maximalen Drehbereich auszutesten sollte das Servo in der jeweiligen Position kurz verweilen. Wenn es dies nicht macht, muss die Zeit (=maxwert für c in der Schleife) solange erhöht werden, bis die kurze Pause erkennbar ist.
Wie gehts nun weiter? Entweder erhöhen wir die Auflösung der Positionen (und trennen uns damit von der sleep()-Ansteuerung) oder wir fahren die Positionen langsamer an oder wir steuern gleichzeitig ein zweites Servo an. Sag was du willst/brauchst.
Gruß
mic
und hier passiert wieder das schon mal beschriebene:
der servo fährt die mittenposition an (egal ob vorher rechts oder links am mechanischen anschlag) und bleibt stehen. Jeder weiterer versuch mit dem taster neu zu starten wird nur mit einem zucken nach links quitiert...
okay thx dann werde ich das heute mal versuchen
ich habs jetzt zum laufen gebracht
#include "RP6ControlLib.h"
void servo_io5 (uint8_t imp)
{
DDRC |= IO_PC5;
PORTC |= IO_PC5;
sleep(imp);
PORTC &= ~IO_PC5;
sleep(200-imp);
}
int main(void)
{
initRP6Control();
initLCD();
uint8_t i=0, x;
i=0;
x=10;
while(true)
{
while(i<10) //200ms
{
servo_io5(x);
i=i+1;
}
x=x+1;
if (x>=20)
{
x=10;
}
i=0;
}
while(true)
{
showScreenLCD("Endlosschleife", "");
}
return 0;
}
Mit dem Code funktionierts.
... ein Wunder ist geschehen. 8)
hi mic,
war jetzt bei C, habe das servo umgetauscht, eine gute und eine schlechte nachricht:
- das kleine billigservo (4,95€) geht auf XBUS1 & 2 (1,3,12) schön im takt hin und her - damit kann ich jetzt erstmal experimentieren...
- das große geht nicht, nur so, wie bereits beschrieben, auch mit einer externen 6V versorgung und dem steueranschluss an XB1-12 nicht. Wenn das jetzt gegangen wäre (ext power) hätte ich gesat es zieht zuviel strom, aber so?
Wenn das jetzt gegangen wäre (ext power) hätte ich gesat es zieht zuviel strom, aber so?
Eigentlich brauchen auch große Servos nicht wirklich viel Strom ...
Würde es zu viel Strom brauchen, würde man es bemerken, da der Spannungsregler dann warm werden würde.
Bzw. der RP6 würde sich einfach abschalten (er wäre damit nicht kaputt).
Hast du schon mal versucht den Servo an einer normalen Fernsteuerungsanlage (falls du eine hast) zu betreiben?
sowas habe ich leider nicht, sind meine ersten gehversuche mit servos...
was mich stutzig macht ist, dass das kleine servo geht, das große nicht (wie auch das andere große, was ich schon hatte). Ist doch nicht normal...
Vl kennst du jemanden der nen Empfänger und nen Sender hat ...
Das Servos kaputt bzw. nicht od. nicht richtig funktionieren ist eigentlich wirklich sehr selten der Fall.
edit:
jemanden in deiner nähe :cheesy:
hi mic,
habe nun mit der servoansteuerung2 experimentiert, habe jetzt glaube ich verstanden wie die einzelnen positionen am servo mittels sleep bestimmt werden, was mir noch fehlt ist die antwort auf´s warum (siehe fragen im code):
// Servoansteuerung2: Wir erzeugen zwei Impulse im Wechsel 10.1.2010 mic
// sleep(1) verzögert ca. 0,1ms, 50 Impulse dauern ca. 1 Sekunde
// Servo an SDA (PC1) (XBUS Pin 12)
#include "RP6RobotBaseLib.h"
uint8_t c; // allgemeine 8-Bit-Variable (ein Char)
int main(void)
{
initRobotBase();
DDRC |= SDA;
PORTC &= ~SDA;
while(1)
{
for(c=0; c<50; c++) //50 = 50 Impulse senden, das dauert ca. 1 Sekunde
{
PORTC |= SDA; // Impulsstart
sleep(1); // 1 = 0.1ms warten = Servoposition 1
PORTC &= ~SDA; // Impulsende
sleep(150); //150ms warten
}
/* die for/++ schleife wird 50* durchlaufen, d.h. das signal wird 50* in längen von 0,1sec und zwischenpausen von 150ms abgegeben
sleep(1) bestimmt die äußerste linke position am servo - warum eigentlich? ist das so irgendwo festgelegt? die äußerste linke position am servo müsste dem potiwert=0 entsprechen, oder? gibt es einen zusammenhang mit sleep(1)?*/
.
.
.
.
.
for(c=0; c<50; c++)
{
PORTC |= SDA;
sleep(30); //30 = 3ms warten = Servoposition 2
PORTC &= ~SDA;
sleep(150);
}
/* die for/++ schleife wird 50* durchlaufen, d.h. das signal wird 50* in längen von 0,1sec und zwischenpausen von 150ms abgegeben,
sleep(30) bestimmt die äußerste rechte position am servo......*/
.
.
.
.
.
}
return(0); // wird nie erreicht
}
radbruch
13.01.2010, 10:02
http://wiki.rc-network.de/images/2/27/PWM2.jpg (http://wiki.rc-network.de/Servo)
Quelle: http://wiki.rc-network.de/Servo
Das ist eine (nicht masstäbliche!) Darstellung der Servoimpulse. Die Impulslänge für Servomitte dauert ca. 1ms, der Drehbereich geht von 0,5ms bis 1,5ms (Manche Dokus sprechen auch von Mitte=1,5ms und Drehbereich 1-2ms) Die Impulse sollten alle 20ms wiederholt werden, das ist der Abstand zwischen zwei ansteigenden Flanken des Signals, in einer Sekunde werden so ca. 50 Impulse-Pausen gesendet (50Hz). Solange das Servo diese Impulse empfängt, versucht es, auf den entsprechenden Drehwinkel zu fahren bzw. diese Position zu halten. Für eine vernünftige Ansteuerung muss das Signal mindestens solange gesendet werden bis das Servo die entsprechende Position erreicht hat (zeitgesteuert weil keine Lagerückmeldung des Servos zur Verfügung steht).
Ein Sleep(1) dauert beim RP6 ca. 0,1ms weil die Zeit mit dem 10kHz-Timer0 erzeugt wird. Deshalb entsprechen Sleep(10) ca. 1ms und Sleep(200) ca. 20ms. Weil die 20ms-Pause jeweils zwischen den Impulsstarts liegen soll, ist eine Pause von Sleep(190) grundsätzlich nicht schlecht.
Meine Vorgehensweise: Ich markiere ein Servohorn (der Hebelarm auf dem Servo) an einer Stelle und drehe das Servo mit der Hand von Anschlag zu Anschlag. Dabei versetze ich das Servohorn solange in der Vielverzahnung der Antriebswelle bis der Drehbereich symetrisch in Bezug auf das Servogehäuse ist. Dann merke ich mir (oder markiere) die Mitte dieses Drehbereichs und sichere das Horn mit der Befestigungsschraube.
Nun schliese ich das Servo an den RP6 an und starte das Servoprogramm1 für eine Position. Ich beginne mit einer Impulslänge die etwa in der Mittelstellung des Servos liegt (z.B. mit 12 für 1,2ms) und ändere dann diesen Wert solange bis sich die Markierungen für Servomitte decken. (Ja, ich flashe dabei jedesmal neu ;) Anschliessend ermittle ich in gleicher weise die Werte für die Anschläge. Das sind dann die Min- und Maxwerte des Drehbereichs, diese sollten dann aber im Betrieb nicht angefahren werden!
Wenn das erledigt ist, wird man feststellen, dass die Werte für diese besonderen Positionen nicht exakt mit den Dokuwerten übereinstimmen. Das ist eben der Unterschied zwischen Theorie und Praxis :) Außerdem ist der Bereich meist nicht linear aufgeteilt und auch noch abhängig von der Betriebstemperatur.
Noch eine Info zur 20ms-Pause: Sie kann auch länger sein, das Servo wird dann träger und beginnt zu zuckeln. Kleinere Werte machen die Servos aggressiver und erhöhen die Stromaufnahme.
Der Wert des Potis ist für die Ansteuerung ohne Bedeutung. Er dient dem Servo zur Lagerückmeldung der Abtriebswelle. Der Sollwert wird von der Servoelektronik aus der Impulslänge berechnet.
Gruß
mic
Kleinere Werte machen die Servos aggressiver und erhöhen die Stromaufnahme.
Wenn man im Programm bei der Pause statt 200 20 eingibt, was dann einer Pause von 2ms entspricht, beginnt das übrigens zu Rauchen :cheesy: :cheesy: :cheesy:
PS:
Das war nicht ich und auch nicht meiner Servo bzw. mein Roboter ... zum Glück :mrgreen:
Mc Delta
07.03.2010, 11:37
Warm verwendet ihr eigentlich alle servos und keine Motoren?
RobbyMartin
07.03.2010, 14:16
servos sind viel einfacher und preziser anzusteuern als normale motoren
servos kann man vorallem auch sehr leicht (ohne viel verkabelung, etc) in alle 2 richtungen bewegen lassen
hi allerseits,
ich habe mit folgendem programm (stammt glaube ich sogar aus diesem thread)
// Servoansteuerung2: Wir erzeugen zwei Impulse im Wechsel 10.1.2010 mic
// sleep(1) verzögert ca. 0,1ms, 50 Impulse dauern ca. 1 Sekunde
// Servo an SDA (PC1) (XBUS Pin 12)
#include "RP6RobotBaseLib.h"
uint8_t c; // allgemeine 8-Bit-Variable (ein Char)
int main(void)
{
initRobotBase();
DDRC |= SDA;
PORTC &= ~SDA;
while(1)
{
for(c=0; c<50; c++) //50 = 50 Impulse senden, das dauert ca. 1 Sekunde
{
PORTC |= SDA; // Impulsstart servo I
sleep(25); // 1 = 0.1ms warten = Servoposition 1
PORTC &= ~SDA; // Impulsende
sleep(150); //50ms warten
PORTC |= SL1; // Impulsstart servo II
sleep(10); // 1 = 0.1ms warten = Servoposition 1
PORTC &= ~SL1; // Impulsende
sleep(150); //50ms warten
}
for(c=0; c<50; c++)
{
PORTC |= SDA; // Impulsstart servo I
sleep(5); //30 = 3ms warten = Servoposition 2
PORTC &= ~SDA;
sleep(150);
PORTC |= SL1; // Impulsstart servo II
sleep(25); // 1 = 0.1ms warten = Servoposition 2
PORTC &= ~SL1; // Impulsende
sleep(150); //50ms warten
}
for(c=0; c<50; c++)
{
PORTC |= SDA; // Impulsstart servo I
sleep(20); //10 = 1ms warten = Servoposition 3
PORTC &= ~SDA;
sleep(150);
PORTC |= SL1; // Impulsstart servo II
sleep(15); // 1 = 0.1ms warten = Servoposition 3
PORTC &= ~SL1; // Impulsende
sleep(100); //50ms warten
}
for(c=0; c<50; c++)
{
PORTC |= SDA; // Impulsstart servo I
sleep(10); //20 = 2ms warten = Servoposition 4
PORTC &= ~SDA;
sleep(150);
/* PORTC |= SL1; // Impulsstart servo II
sleep(30); // 1 = 0.1ms warten = Servoposition 4
PORTC &= ~SL1; // Impulsende
sleep(100); //50ms warten */
}
for(c=0; c<50; c++)
{
PORTC |= SDA; // Impulsstart servo I
sleep(20); //5 = 0.5ms warten = Servoposition 5
PORTC &= ~SDA;
sleep(150);
/* PORTC |= SL1; // Impulsstart servo II
sleep(10); // 1 = 0.1ms warten = Servoposition 5
PORTC &= ~SL1; // Impulsende
sleep(100); //50ms warten */
}
}
return(0); // wird nie erreicht
}
/*
zweites servo an sl1
PORTC |= SL1)
PORTC &= ~SL1
*/
beide servos am rp6 zum laufen bekommen....
jetzt habe ich einen greifarm für den asuro gebaut, der auf der kleinen platine befestigt ist und die drei servos über die pins INT0, PD3 und PD6 gesteuert werden sollen. Wenn ich das programm auf den asuro umstricke, sieht es im ersten ansatz so aus (viel ist noch auskomentiert)
#include "asuro.h"
uint8_t c; // allgemeine 8-Bit-Variable (ein Char)
int main(void)
{
DDRC |= (1<<PD3);
PORTC &= ~(1<<PD3);
while(1)
{
for(c=0; c<50; c++) //50 = 50 Impulse senden, das dauert ca. 1 Sekunde
{
PORTC |= (1<<PD3); // Impulsstart servo I
Msleep (25); // 1 = 0.1ms warten = Servoposition 1
PORTC &= ~(1<<PD3); // Impulsende
Msleep (150); //50ms warten
}
/* for(c=0; c<50; c++) //50 = 50 Impulse senden, das dauert ca. 1 Sekunde
{
PORTC |= (1<<PD3); // Impulsstart servo I
Msleep (20); // 1 = 0.1ms warten = Servoposition 1
PORTC &= ~(1<<PD3); // Impulsende
Msleep (150); //50ms warten
}*/
/* for(c=0; c<50; c++) //50 = 50 Impulse senden, das dauert ca. 1 Sekunde
{
PORTC |= (1<<PD6); // Impulsstart servo II
Msleep(10); // 1 = 0.1ms warten = Servoposition 1
PORTC &= ~(1<<PD6); // Impulsende
Msleep(150); //50ms warten*/
/* PORTC |= (1<<INT0); // Impulsstart servo III
Msleep(17); // 1 = 0.1ms warten = Servoposition 1
PORTC &= ~(1<<INT0); // Impulsende
Msleep(150); //50ms warten */
/* PORTC |= (1<<PD3); // Impulsstart servo I
Msleep (5); // 1 = 0.1ms warten = Servoposition 2
PORTC &= ~(1<<PD3); // Impulsende
Msleep (170); //50ms warten
}*/
}
return(0); // wird nie erreicht
}
zuckt der erste servo (hängt am PD3) nur unkontrolliert in der gegend herum und auch der dritte macht so komische zuckungen, auch wenn er an keinem signal hängt, bzw. die software noch gar nicht in funktion ist.
habe ich was falsch gemacht, oder bestimmte asuro-eigenheiten vergessen zu berücksichtigen?
die servos liefen bereits einzeln (über kabel mit dem rp6 verbunden) und mit der rp6 software...die hardware scheint also ok zu sein ( die platine mit den servos)...
danke für euere hinfe
Mc Delta
24.06.2010, 16:29
Hast du schon mal geschaut ob mit der Spannungsversorgung alles in Ordnung ist?
M__sleep != sleep
Die eine Funktion in Millisekunden die andere in 100 µs Schritten...
radbruch
24.06.2010, 17:43
Hallo
DDRC |= (1<<PD3);
PORTC &= ~(1<<PD3);
Wenn du Pins von Port D verwenden willst, dann musst du auch das Datenrichtungsregister und den Port von D verwenden.
Bei mehreren Servos werden die Impulse nacheinander erzeugt und dann wird eine gemeinsame Restpause lang gewartet:
for(c=0; c<50; c++) //50 = 50 Impulse senden, das dauert ca. 1 Sekunde
{
PORTD |= (1<<PD3); // Impulsstart servo I
Sleep (servo1);
PORTD &= ~(1<<PD3); // Impulsende
PORTD |= (1<<PD6); // Impulsstart servo 2
Sleep (servo2);
PORTD &= ~(1<<PD6); // Impulsende
Sleep (255); // Pause
Sleep (255);
Sleep (255);
}
Beim asuro wird Sleep() mit dem 36kHz-Timer2 erzeugt, eine Millisekunde ist dann Sleep(36). Mehr Infos zum Servotiming beim asuro findest du z.B. hier:
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=500813#500813
Gruß
mic
hallo zusammen
habe ein m32 erweiterungssatz gekauft. würde gerne ein servo über adc2 ansteuern. wie kann ich den im programmcode aufrufen?
kann man den folgenden code umschreiben?
#include "RP6ControlLib.h"
uint8_t i;
{
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 1;
sleep(8);
PORTA &= ~1;
sleep(200-10);
}
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 1;
sleep(20);
PORTA &= ~1;
sleep(200-20);
}
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 1;
sleep(14);
PORTA &= ~1;
sleep(200-10);
}
}
den code habe ich auf dem mainboard-adc0 angewendet.
thx
radbruch
27.08.2010, 15:33
ADC2 ist ebenfalls am Port A und hat die Wertigkeit 4 (2^2)
Datenrichtung: DDRA |= 4;
Einschalten: PORTA |= 4;
Ausschalten: PORTA &= ~4;
danke für deine antwort. habe es mal ausprobiert.
der code sieht so aus:
#include "RP6ControlLib.h"
uint8_t i;
{
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 4;
sleep(8);
PORTA &= ~4;
sleep(200-10);
}
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 4;
sleep(20);
PORTA &= ~4;
sleep(200-20);
}
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 4;
sleep(14);
PORTA &= ~4;
sleep(200-10);
}
}
das übersetzen funktioniert. leider bewegt sich mein servo nicht. kann das sein das ich auf dem mainboard ein code laden muss damit es korrekt funktioniert? habe m32 eingesteckt und den code draufgeladen.
noch eine kleine frage: wo kann ich nachschauen wenn ich ein adc port ermitteln will d.h wo finde ich die oben genannten daten (4)?
Martinius11
27.08.2010, 18:54
ich schätze der Fehler liegt beim Sleep schreib mal statt 200-20 einfach 180
und auserdem schreibt man Sleep am Anfang groß
radbruch
27.08.2010, 18:59
Hallo
Dass dieser Code so übersetzbar ist kann ich schlichtweg nicht glauben. So könnte es vielleicht funktionieren:
#include "RP6ControlLib.h"
int main(void)
{
uint8_t i;
initRP6Control();
DDRA |= 4; // ADC2 (PA2) auf Ausgang schalten
while(1)
{
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 4;
sleep(8);
PORTA &= ~4;
sleep(200-10);
}
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 4;
sleep(20);
PORTA &= ~4;
sleep(200-20);
}
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 4;
sleep(14);
PORTA &= ~4;
sleep(200-10);
}
}
return(0);
}
Da ich aber kein M32 besitze kann ich es nicht testen...
Die Portbezeichnungen entsprechen den Zweierpotenzen. Pin0 ist 2^0 oder 1, Pin1 ist 2^1 oder 2, weiter gehts dann über 4, 8, 16, 32 und 64 bis 2^7 oder 128 für den achten Pin.
Dass der ADC2 beim Mega32 der Pin PA2 (Port A Pin2) ist, findet man im Datenblatt des Mega32 oder im Schaltplan des M32.
Gruß
mic
[Edit]
200-20 funktioniert natürlich. In der RP6-Library ist sleep() mit kleinem S definiert:
/**
* Delay with the help of the 10kHz timer.
* sleep(10) delays for *about* 1ms! Not exaclty, as we do not use assembly routines
* anywhere in this library!
*
* This is a blocking routine, which means that the processor
* will loop in this routine and (except for interrupts) the
* normal program flow is stopped!
* Thus you should use the Stopwatch functions wherever you can!
*
* Example:
* sleep(1); // delay 1 * 100us = 100us = 0.1ms
* sleep(10); // delay 10 * 100us = 1000us = 1ms
* sleep(100); // delay 100 * 100us = 10000us = 10ms
* // The maximum delay is:
* sleep(255); // delay 255 * 100us = 25500us = 25.5ms
*/
void sleep(uint8_t time)
{
delay_timer = 0;
while (delay_timer <= time+1);
}
(Aus RP6ControlLib.c)
erstmals danke für die genauen portbezeichnungen. denke wird mir helfen bei weitern versuchen einen code selber zu schreiben.
leider funktioniert es noch nicht. das servo macht keine bewegung, zudem ist das horn nicht blockierend. habe den code von radbruch 1:1 übernommen.
also muss man kein code auf die base vom rp6 laden damit m32 läuft?
habe kein selftest ausführen können da ich nicht über ein lcd display verfüge. soweit ich weiss braucht man kein lcd auf dem m32 um servos anzusteuern? habe ein paar codes von den rp6control_examples ausprobiert und die laufen so wie sie sollen.
gruss RAW
thx
Martinius11
27.08.2010, 22:07
du must Sleep und nicht sleep schreiben
radbruch
27.08.2010, 22:39
Ich vermute, die ADC2-Pins am M32 entsprechen nicht der Belegung am Servostecker. VDD (http://de.wikipedia.org/wiki/Spannungsbezeichnung) sollte in der Mitte der Stiftleiste sein:
[Edit]
Nach der Doku des M32 (http://www.produktinfo.conrad.com/datenblaetter/175000-199999/191550-an-01-de-RP6_MEGA32_ERWEITERUNGSSATZ.pdf) Seite 27 ist die Stiftleiste nicht für Servos ausgelegt. Abhilfe schafft ein "Umbau" des Servosteckers:
http://radbruch.roboterbastler.de/rp6/greifer2/servoanschluss1_klein.jpg (http://radbruch.roboterbastler.de/rp6/greifer2/servoanschluss1.jpg)
sleep() mit kleinem S ist schon richtig. Interessanterweise wird die Funktion in der RP6RobotBaseLib.c so definiert:
void sleep(uint8_t time)
{
for (delay_timer = 0; delay_timer < time;);
}
habe das rote kabel in vdd, das schwarze in gnd und das weisse kabel in adc2.
habe sonst das servo bei io1-6 und adc0/adc1/int1 zum bewegen gebracht. nur nicht auf dem m32.
gibt es eine andere möglichkeit das servo zu bewegen d.h mit int2 auf dem m32.
danke
ok habe es jetzt hinbekommen. hatte ein fehler im programmers notepad.
danke an euch.
mfg
RAW
hallo
M32: möchte zwei servos anschliessen. der eine an adc2 der andere adc3.
habe die codes einzeln getestet mit einem servo (acoms as-12) und funktioniert. sobald ich den zweiten servo anschliesse (futaba fp-s148) funktioniert es nicht mer. wenn der (futaba fp-s148) ein kleinen widerstand auf dem horn hat läuft es. frage mich wiso das so ist und was man da machen könnte?
Meine codes lauten:
ADC2: acoms
#include "RP6ControlLib.h"
int main(void)
{
uint8_t i;
initRP6Control();
DDRA |= 4; // ADC2 auf Ausgang schalten
while(true)
{
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 4;
sleep(20);
PORTA &= ~4;
sleep(200);
}
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 4;
sleep(9);
PORTA &= ~4;
sleep(200);
}
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 4;
sleep(0);
PORTA &= ~4;
sleep(200);
}
}
return(0);
}
ADC3: futaba
#include "RP6ControlLib.h"
int main(void)
{
uint8_t i;
initRP6Control();
DDRA |= 8; // ADC3 auf Ausgang schalten
while(true)
{
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 8;
sleep(20);
PORTA &= ~8;
sleep(180);
}
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 8;
sleep(9);
PORTA &= ~8;
sleep(180);
}
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 8;
sleep(0);
PORTA &= ~8;
sleep(180);
}
}
return(0);
}
ziel ist es den acoms an adc2 und den futaba an adc3 in einem code laufen zu lassen. kann mir da jemand helfen?
radbruch
29.08.2010, 23:16
So vielleicht?
#include "RP6ControlLib.h"
int main(void)
{
uint8_t i;
initRP6Control();
DDRA |= 8 + 4; // ADC2 und ADC3 auf Ausgang schalten
PORTA &= ~(8+4); // beide Pins auf Low
while(true)
{
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 4; // Impuls servo 1 an
sleep(20);
PORTA &= ~4; // Impuls servo 1 aus
PORTA |= 8; // Impuls servo 2 an
sleep(20);
PORTA &= ~8; // Impuls servo 2 aus
sleep(200-20-20); // 20ms minus Impuls1 minus Impuls2 ist Impulspause
}
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 4;
sleep(9);
PORTA &= ~4;
PORTA |= 8;
sleep(9);
PORTA &= ~8;
sleep(200-9-9);
}
/* sleep(0) erzeugt einen extrem kurzen Impuls!
for(i=0;i<100; i++) // 100 mal Impuls
{
PORTA |= 4;
sleep(0);
PORTA &= ~4;
PORTA |= 8;
sleep(0);
PORTA &= ~8;
sleep(200-0-0);
}
*/
}
return(0);
}(ungetestet)
der code ist super. danke. nur läuft der eine servo nur kurz und danach bricht das programm ab. wenn ich nur ein servo anschliesse läuft das programm durch, dies geschieht aber nur mit dem acoms ic as-12. das servo (futaba) ist soweit ich das beurteilen kann das problem. habe verschidene servos ausprobiert und nur der acoms läuft. auf der base gehen mehrere servos. thx
kann das sein das man die servos wo ich an dem m32 angeschlossen habe mit zusätzlichen 5v ausstatten muss. habe wie schon geschrieben unterbrüche im ablauf des codes.
gruss RAW
Magelan1979
02.09.2010, 21:02
Ein Abbruch spricht stark dafür. Es ist schon besser, wenn die Servos über eine eigene Spannungsversorgung laufen
also muss man plus von einem 5v akku an vdd (servo) und minus an gnd der (platine) anschliessen?
thx
Die Suchfunktion ist dein Freund und Helfer :)
Es gibt wirklich dutzende Threads zu dem Thema mit vielen vielen Vorschlägen dazu.
Suche einfach aufs RP6 Forum beschränken und nach "Servo" suchen.
MfG,
SlyD
Magelan1979
03.09.2010, 17:37
Gnd an Platine und Akku
he danke an euch. werde es mal testen.
hat bestens geklappt. ohne zusätzliche volt geht das bei mir nicht.
Morpheus1997
10.02.2011, 20:59
hm... hab mal ne frage... ich hab mir nun auch einen servo zugelegt, den ich auch bald mit dem rp6ansteuern möchte.
doch bei den 3 kabeln ist das eine schwarz dann rot und dann orange...
hab keinerlei datenblätter etc für die pinbelegung gefunden :( könnt ihr mir da vielleicht weiterhelfen?
radbruch
10.02.2011, 21:29
Schwarz ist GND (Minus)
Rot ist VDD (Plus)
Orange ist das Signal
http://www.rn-wissen.de/index.php/Servo
Morpheus1997
10.02.2011, 21:32
cool besten dank ^^
Hallo Morpheus,
im RN-Wissen http://www.rn-wissen.de/index.php/Servo findest du die Anschlussbelegungen von den gebräuchlichsten Servos.
Gruß
Thomas
Morpheus1997
11.02.2011, 22:43
jaa... da hatte ich auch schon geguckt... nur ich hab mich gewundert weil meine kontakte waren ja schwarz rot und orange und die waren da nich bei... ;)
Hallo
möchte insgesammt 4 servos an meinen m32 anschliessen. zwei an adc2 und zwei an adc3. habe gedacht dass ich weitere anschlüsse damit sparen kann. nur wen ich mein programm ablaufen lasse, bewegen sich nur drei der vier servos. kann mir jemand sagen wieso dies so ist. ist es möglich das die adc ports überbelegt sind und somit nur drei zum ansteuern bringen.
gruss RAW
habe die servos zusätzlich mit 1x 5V unterstützt. habe sonst immer servos an die möglichen ports angeschlossen ohne sie doppelt zu belegen, hat auch immer gut funktioniert. also am code kann es nicht liegen. zum code selber; es bewegen sich immer nur zwei servos gleichzeitig. hoffe auf unterstützung.
gruss RAW
Hallo,
wenn du 4 Servos separat ansteuern willst brauchst du auch 4 Steuerleitungen, Du sprichst mit ADC2/3 aber nur von 2 Leitungen?
Da wäre vielleicht erst mal die Verschaltung zu klären. Steuerst du mit einem Port tatsächlich 2 Servos oder wie muss man sich das vorstellen?
"es bewegen sich immer nur zwei servos gleichzeitig" ... das wäre dann die logische Folge...
"Am Code kann es nicht liegen" ... was nicht sein kann, das nicht sein darf?
Wenn Du die 4 Servos unabhängig steuern willst - woran sollen die Servos denn erkennen welches jeweils gemeint ist? Man kann schlecht 2 PWM Signale multiplexen und Servos haben keine Möglichkeit sowas zu demultiplexen. Und was hast Du mit der Software angestellt, das 2 Servowerte über eine Steuerleitung ausgegeben werden? Wie muss man sich das PWM Signal dann vorstellen? Aus meiner Sicht müsste sich aus den beiden PWM Werten dann eine Art Mischsignal ergeben... dessen Flanken quasi kaum vorhersagbar sind und sich ggf. sogar ausgleichen/überlagern. Ist dir überhaupt klar, wie Servos angesteuert werden? Was Du versuchst klingt wie 2 LEDs an einem Port zu schalten und Du wunderst dich, warum nur beide entweder an oder aus sind...
Alles sehr suspekt...
LG
"Steuerst du mit einem Port tatsächlich 2 Servos oder wie muss man sich das vorstellen".....
korrekt habe es zuerst mit zwei servos auf einem port(adc2) ausprobiert. dabei sind keine grösseren probleme aufgetreten. danach habe ich das gleiche mit adc3 gemacht in der hoffnung das es funktioniert.
also der grundgedanke ist ganz einfach:
4 sevos insgesammt, dabei bewegen sich 2 immer parallel. (adc2)
die anderen zwei zu einer anderen zeit auch parallel. (adc3)
wollte mir den kabelsalat sparen, solange zwei servos immer die gleichen bewegung ausführen.
naja, somit hat sich das auch erledigt :(
danke für deine antwort.
lg RAW
Martinius11
03.04.2011, 22:56
soweit ich weiß muss man die adc-Ports extra mit Hilfe eines registers frei
schalten das man sie als normale ports nutzen kann.
Servos benötigen continuierliche PWM Impulse, das zeitversetzte Ansteuern wird so eher nicht gehen. Man kann sicherlich 2 Servos an einem Ausgang steuern so wie du es vor hast, die Software muss dann aber die Impulse für ADC2 und ADC3 generieren. Ist zwar eine sehr ungewöhnliche Geschichte was du vor hast aber ok...
Versuch doch mal erst je ein Servo pro adc zu steuern und häng die beiden anderen später rein. Es kann sein das die Eingänge der Servos nicht zusammen gelegt werden können/dürfen oder Du sie mit Optkopplern/Transistorstufen/Gattern trennen musst. Die Ansteuerung von adc2/3 sollte aber deine Servosoftware schon können, sonst muss man da erst mal suchen.
@matrinius11
Ich setze eigentlich voraus, das er per Software prinzipiell mit ADC3 das gleiche anstellt wie mit ADC2 und mit dem scheints ja zu gehen.
Aber es ist richtig, es müssen das Datenrichtungsregister und die Pullups geschaltet werden. Sonst gibt das nix.
LG Rolf
na gut, schau mal was ich da machen kann. zu lange werde ich es nicht mehr versuchen. ich glaube das einfachste währe wenn ich die restlichen pots benutze.
Danke
lg RAW
Fabian E.
04.04.2011, 17:50
Hallo RAW,
erstens brauchst du keine ADCs, du kannst ganz normale I/Os nehmen. Dann musst du die nicht verschwenden.
Zweitens die M32 genug freie Ports, damit du etliche Servos anschließen kannst.
Es gibt dazu hier ja sogar schon eine fertige Library, die bis zu 8 ansteuern kann.
Das funktioniert sehr gut.
Mach dir also erst gar keinen Aufwand irgendwie 2 Servos an eine Leitung zu klemmen, sondern nimm einfach vier ;)
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.