Salut,
momentan entwickle ich einen Servocontroller, der auf einem Hexapod zum
Einsatz kommen soll. Leider zeigt dieser seltsame Fehler, bei denen mir
auch im Roboternetz niemand helfen konnte, deshalb frage ich hier
einmal nach.
Problem ist, dass sich die Servos nicht separat regeln lassen;
stattdessen wird der (gemeinsame) Puls einfach fuer alle Werte, die im
SRAM hintereinander liegend == 255 sind, etwas laenger, in einer Art
exponentiellen Kurve. Der Code an sich ist simpel, weshalb ich mir den
Fehler nicht erklaeren kann; hier der ASM-Code zur Erzeugung der
variablen Komponente des Signals:
clr lowmask
clr highmask
clr ptr
ldi counter,127 ; count down from 127 to -128
sec ; c is to be rotated into ptr
ldi ZH,4 ; RAM pointer is at 1024, address of first servo value
; repeat this once every ( 1.35 / 255 ) ms
cycle:
clr ZL ; point Z to first servo
compare_next_motor:
rol ptr ; rotate servo ptr to next value
brcs write ; if all values compared: break
ldd servo,z+8 ; load servo in higher byte
cp servo,counter ; and compare w/ counter
brne second ; if not equal: leave high mask blank at position
or highmask,ptr ; else start pulse at end of cycle
second: ld servo,z+ ; compare lower servo respectively
cp servo,counter
brne compare_next_motor
or lowmask,ptr
rjmp compare_next_motor ; and loop
write:
out _SFR_IO_ADDR(PORTA),lowmask
out _SFR_IO_ADDR(PORTD),highmask
dec counter ; count comparision value for servos down
brvc cycle ; if not transition -128 -> 127: repeat
Der Code liegt in einem .S-File, umgeben von den noetigen Pushs und
Pops, und wird als Funktion aus C in einer ISR alle 20ms aufgerufen.
Grundidee, falls nicht ersichtlich, ist ein von 127 abwaerts zaehlender
int8_t-Wert, der laufend mit saemtlichen Servowerten, die sequentiell ab
1024 im SRAM liegen, abgeglichen wird; bei gleichem Wert wird der Servo
aktiviert, je hoeher der Wert, je frueher findet das statt. Soweit ganz
einfach - gerade deshalb komme ich nicht weiter... Kann vielleicht
jemand helfen?
Gruss,
David
PS: Fuer Optimierungsvorschlaege, was die Geschwindigkeit angeht, waere
ich ebenfalls sehr dankbar; bin zwar momentan auf 0-1.34 ms
Variationsbreite runter, das reicht aber nur fuer die mir vorliegenden
Conrad-Servos und entspricht nicht der Norm (zu lang - die liegt bei
1-2 ms AFAIK?).
Also so richtig verstehe ich nicht, was du da vor hast. Unter Servocontroller stelle ich mir das Teil vor, was die Nachlaufsteuerung im Servo realisiert, also die Servo-Elektronik. Solltest du aber einen Controller meinen, der Servoimpulse generiert, dann schau mal hier: http://www.hanneslux.de/avr/mobau/7ksend/7ksend02.html Du kannst ja die ADC-Abfrage rauswerfen und dafür eine alternative Bereitstellung der Servosollwerte einbauen. ...
Hallo, an dieser Begriffsverwirrung mag auch teilweise die geringe Resonanz bei einem so einfachen Problem liegen ;) Jedenfalls plane ich zweiteres, naemlich eine Ansteuerung von 16 Servos aus einem uC. Bei obigem Code beeinflussen sich die Werte jedoch gegenseitig, und ich weiss nicht, warum... Nun brauche ich den uC allerdings noch fuer andere Aufgaben als die Servoansteuerung, sonst waere die Sache kein Problem - haette es dann genauso implementiert wie Du bei der RC-Sache, naemlich mit (wenn ich es ohne RC-Wissen richtig verstanden habe) sequentieller Abfrage der Werte und Erzeugung der Impulse fuer jeden Kanal. Dabei ist der Controller leider voll ausgelastet, das faellt also weg; gibt auch Probleme mit der Kommunikation ueber z. B. UART, wenn wg. Interrupt ploetzlich der Puls ausbleibt... Darum ist mein Ansatz eben, alle 16 Servos zur gleichen Zeit anzusteuern - dazu muss ich allerdings in der variablem ms des Pulses alle 255 moeglichen Positionen fuer alle Motoren abfragen und regeln, was zwar auf nur etwa 5% Auslastung insgesamt hinauslaeuft, aber nicht leicht zu implementieren ist. Graphisch: statt: will ich: |"|______ ___|"|___ Servo1 __|"|____ ___|"|___ Servo2 ____|"|__ ___|"|___ Servo3 ... Zwischen den Pulsen ist also voellig ungenutzte Zeit, sollte also das Restprogramm nicht stoeren. Ich hoffe, die Sache ist jetzt etwas verstaendlicher - waere nicht schlecht, ich steig naemlich echt nicht dahinter, was das Problem sein sollte. Leider hab ich kaum ASM-Erfahrung, bin GCC-Fan und versuche mich das erste mal an geschwindigkeitsoptimiertem Code. Gruss und Danke, David
> Dabei ist der > Controller leider voll ausgelastet, Das ist aber nicht der Fall. Der Controller verbringt die meiste Zeit im Sleep-Mode Idle. Es sind also noch enorme Reserven an Rechenzeit vorhanden, die Mainloop darf also noch drastisch wachsen. Zeitkritische Sachen synchronisiert man vorteilhaft mittels Timer-Interrupt. Das vermeidet rechenzeitfressende Warteschleifen. Mein Vorschlag: Nimm den Interrupt von Timer0, um alle 20ms eine Sequenz anzustoßen und eventuelle mechanische Kontakte (z.B. Taster zu entprellen). Dann hast du beide Compare-Interrupts von Timer1 frei. Nun teilst du deine 16 Impulse in zwei Gruppen auf, von denen die eine von Compare1A und die andere von Compare1B sequentiell abgearbeitet wird. Die Sollwerte für die Impulsbreite entnimmst du dabei dem SRAM. Somit läuft die gesamte Impulserzeugung im Interrupt und dein Hauptprogramm kann sich um die Generierung der Sollwerte kümmern. Ich denke mal, dass der Mega16 das mit 1MHz Takt locker schafft und mehr als die Hälfte seiner Zeit im Sleepmode vertrödelt. Wenn du C wirklich beherrscht (ich kann es nicht), dann kannst du das in C genauso effizient programmieren wie ich in ASM. ...
// Timer 1 output compare interrupt service routine
interrupt [TIM1_COMP] void timer1_comp_isr(void)
{static unsigned char servo_nr;
PORTB=output_mask[servo_nr]; //alten Impulsausgang löschen, neuen
setzen
OCR1=servo_zeit[servo_nr]; //neue Impulszeit
if (servo_nr<9) servo_nr++;
else servo_nr=0;
}
Das ist im Prinzip schon alles und läuft im Hintergrund, du merkst kaum
was davon. Ist für 8 Servos am PortB. Die Zeiten stehen in
servo_zeit[0..7], die Restzeit zu 20ms in servo_zeit[8].
Und noch Timer1_init:
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 1000,000 kHz
// Mode: Output Compare
// OC1 output: Discon.
// Timer 1 is cleared on compare match
// Noise Canceler: Off
// Input Capture on Falling Edge
TCCR1A=0x00;
TCCR1B=0x0A;
TCNT1H=0x00;
TCNT1L=0x00;
OCR1H=0xf0;
OCR1L=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0xC0;
Ham' wir wieder was gelernt, diesen Betriebsmodus kannte ich noch gar nicht :) Kenne leider niemanden, der Ahnung von uCs hat, und bin noch nicht selbst drueber gestolpert (sollte mich wohl mehr hier aufhalten statt im RN). Dann hat das Projekt schonmal seinen Zweck erfuellt. Danke, David
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.