... Regelst du die Versorgungsspannung ... oder ... fein abgestufte Servosignale ...
Vermutlich geht es mit veränderter/vermindeter Versorgungsspannung nicht oder zumindest sehr schlecht. Jedenfalls ist meine Erfahrung (praktisch nur mit Billigservos), dass Servos mit veränderter Spannung auch ein verändertes, manchmal ein völlig inakzeptables Signalverhalten haben.
Ich habe theoretisch über 4000 ticks für den vollständigen Servoschwenk zur Verfügung, wie die "absuloute maximum Ratings" (Anschlag bis Anschlag) dabei genau aussehen, weiß ich nicht wirklich aufs Tick genau. Eins weiß ich relativ gut: für das Fahren mit der üblichen Servogeschwindigkeit von 60°/0,15 sec müsste ich rund 190 Ticks pro Servoperiode vorgeben. Die letzten Reserven nutze ich natürlich nicht, ss muss ja in der ISR (TIMER1_COMPB_vect) nach "hinten" etwas Luft bleiben, damit ich keinen Laufzeitcrash der ISRs bekomme. Während die erwähnte ~COMPB~-ISR die zuvor gestartete Rampe beendet, wird (wurde) von der ISR (TIMER1_COMPA_vect) alle fast 2,5 ms diese entsprechende Servorampe gestartet.
Mal nur so als Skizze ein Codeauszug des aktuell laufenden Codes:
Code:
// ============================================================================== =
// ...
#define OCR1AV 6400 // Vorgabewert - wegen prescaler clk/8
#define OCR1BV 3600 // Startwert OCR1B {960 .. 6240}: ca. 1,5 ms Rampe
// ...
#define stdMIN 1600 // Standard Minimum
#define stdMIT 3600 // Standard Mitte
#define stdMAX 5600 // Standard Maximum
// ...
// ============================================================================== =
// ============================================================================== =
// == Timer Aufgabe: Servo mit Soft-PWM ansteuern auf wählbarem Port
// Beispiel: Set TCNT1 to 0x01FF : TCNT1 = 0x1FF;
// Read TCNT1 into i : i = TCNT1;
// - - - - - - - - - - - - - - - -
void TC1TMR_init(void) // Init Timer/Counter 1 für 2 ms Servoperiode
{ //
TCCR1B |= (1<<WGM12); // WGM12 => CTC, TOP = OCR1A S133
TCCR1B |= (1<<CS11); // CS11+10 <=> clk/8 => 2,500 MHz S134
OCR1A = OCR1AV; // 8mal OCR1A = 6800 => alle 20 ms ein Interrupt
OCR1B = OCR1BV; // OCR1B = {???} => 1-2 ms Rampe
// TIMSK1 |= (1<<OCIE1A); // Tmr/Cntr1 Oput CompA Mtch intrrpt enabled
// - - - - - - - - - - - - - - - -
SetBit (PORTC, L1r); // rtLED ein, Kontrolle für Servotimer-init
}
// ============================================================================== =
// ============================================================================== =
// === ISR für TIMER1_COMPA_vect, VECTOR 16 (vgl. S 65) ===================== =
// Zehn Servos umlaufend ansteuern.
// - - - - - - - - - - - - - - - -
ISR (TIMER1_COMPA_vect) // Servo[Svpt] wählen + dessen Rampe starten
{ //
u8 xt; // Kurzschreibweise für Servopointer
// xt = NxtSvpt; //
// - - - - - - - - - - - - - - -
Svpt ++; // Pointer für diesen, aktuellen Servo eins rauf
if ((Svpt < 1) || (Svpt > Svmx)) Svpt = 1; // und eingrenzen auf {1-10}
xt = Svpt + 1; // Pointer auf nächsten Servo berechnen
if ( xt >= 11) xt = 1; // .. Überlauf abfangen
// - - - - - - - - - - - - - - -
// Für den aktuellen Servopointer liegt ein korrekter Wert Srv_tm[nr] vor
// Den aktuellen Srv_tm-Wert auf zulässige Schranken begegrenzen, vgl. kal_0
// und vor dem Setzen Offset draufrechnen. ##########################
if ( SrvCHK ) // Prüfung nur, wenn ServoCHECK aktiv !!
{ //
if ( Srv_tm[Svpt] < SrvMin [Svpt] ) Srv_tm[Svpt] = SrvMin [Svpt];
if ( Srv_tm[Svpt] > SrvMax [Svpt] ) Srv_tm[Svpt] = SrvMax [Svpt];
} //
// - - - - - - - - - - - - - - - -
switch (Svpt) //
{ //
case 1: SetBit ( PC, Servo1 ); break;
case 2: SetBit ( PC, Servo2 ); break;
case 3: SetBit ( PC, Servo3 ); break;
case 4: SetBit ( PC, Servo4 ); break;
case 5: SetBit ( PC, Servo5 ); break;
case 6: SetBit ( PC, Servo6 ); break;
case 7: SetBit ( PA, Servo7 ); break;
case 8: SetBit ( PA, Servo8 ); break;
case 9: SetBit ( PA, Servo9 ); break;
case 10: SetBit ( PA, Servo10); break;
default: break; // hierher würde noch n Fehlerflag passen
} // Ende switch (Svpt)
// - - - - - - - - - - - - - - -
// OCR1B = Srv_tm[Svpt]; // Stellwert ist durch Rampenwert Srv_tm definiert
OCR1B = Srv_tm[Svpt] + Seroff [Svpt]; // Rampenwert ist mit Srv_tm definiert
TIFR1 |= (1<<OCF1B); // ??? Klappt immer wenn dies gesetzt wird
TIMSK1 |= (1<<OCIE1B); // Tmr/Cntr1 CompB Match interrupt enabled
// d.h. Timer1B starten <=> nächster Servo...
// - - - - - - - - - - - - - - -
// - - Wert für nächsten Servo holen wenn möglich
// .... hier folgt die Servo-Vorgabedaten-Umschaufelei - aktuell entfernt
return; //
// - - - - - - - - - - - - - - -
} // Ende ISR (TIMER1_COMPA_vect)
// ============================================================================== =
// ============================================================================== =
// === Nicht unterbrechbare ISR für TIMER1_COMPB_vect ====================== =
ISR (TIMER1_COMPB_vect) // VECTOR 18 S 65
{ //
TIMSK1 &= ~(1<<OCIE1B); // Tmr/Cntr1 CompB Match interrupt disabled
// - - - - - - - - - - - - - - -
switch (Svpt)
{ //
case 1: ClrBit ( PC, Servo1 ); ClrBit ( PA, Servo10); break;
case 2: ClrBit ( PC, Servo2 ); ClrBit ( PC, Servo1 ); break;
case 3: ClrBit ( PC, Servo3 ); ClrBit ( PC, Servo2 ); break;
case 4: ClrBit ( PC, Servo4 ); ClrBit ( PC, Servo3 ); break;
case 5: ClrBit ( PC, Servo5 ); ClrBit ( PC, Servo4 ); break;
case 6: ClrBit ( PC, Servo6 ); ClrBit ( PC, Servo5 ); break;
case 7: ClrBit ( PA, Servo7 ); ClrBit ( PC, Servo6 ); break;
case 8: ClrBit ( PA, Servo8 ); ClrBit ( PA, Servo7 ); break;
case 9: ClrBit ( PA, Servo9 ); ClrBit ( PA, Servo8 ); break;
case 10: ClrBit ( PA, Servo10); ClrBit ( PA, Servo9 ); break;
default: break; // hierher würde noch n Fehlerflag passen
}
// - - - - - - - - - - - - - - -
} // Ende ISR (TIMER1_COMPB_vect)
// ============================================================================== =
Und - psssst - nicht weitersagen - es gibt schon mal den einen oder anderen Fall, in dem deutliches Servozittern auftritt. Das sind dann vermutlich irgendwelche Resonanzfrequenzen, die ich durch ne geänderte Zeit vermeide. Da helfen schon gaaanz wenige Ticks.
Lesezeichen