Hallo,
http://www.4finger.net/~smay/avr/servomaster/http://www.4finger.net/~smay/avr/servotester/
ermöglicht die Steuerung von bis zu 20 Servos über ein TWI-Interface.
Programmiert wurde das auf einem ATmega8 mit internem RC-Oszillator bei
8 MHz. Die Funktion ist ähnlich dem SD20 Servocontroller auf PIC-Basis.
Dazu gibt es auch gleich ein Testprogramm, welches auch mit dem SD20
zusammenarbeitet.
Viel Spaß.
mfg, Stefan.
Hallo, ich hätte da mal de frage zur funktionsweise.
Hab ich das so richtig verstnden:
im Interrupt SIG_OVERFLOW1 wird zuerst der gerade aktive Servo bzw. der
entsprechende Pin ausgeschaltet, dann in einer schleife gesucht bis der
nächste servo kommt dessen Registerwert != 0 ist, der Timer auf die
entprechende zeit gestellt.
Dann den Pin einschalten, und warten bis der Interrupt wieder kommt und
ihn ausschaltet und alles von vorne losgeht.
wenn er mit allen servos durch ist, wird Timer1 gestoppt, und durch den
timer2 alle 33ms (wenn ich mich nicht verrechnet habe) reaktiviert.
Ich habe im Internet bis jetzt nix genaues dazu gefunden, wie lang die
Zeit zwichen den Pulsen sein darf, es wird immer nur gesagt, dass es ca
20ms sein sollen, aber bei 20 servos kommt mal ja auf (im schlimmsten
fall) 40ms. Kennt jemand zufällug eine Quelle, wo das Präzise
dokumentiert ist?
Und noch eine frage:
wenn der Hauptinterrupt wieder aktiviert wird (servo-rollover = true)
wird ja die variable which auf eins gesetzt, und die entsprechende zeit
für servo1 geladen. was ist nun aber wenn der servo1 deaktiviert ist
(servo_register[1] == 0)
wäre es nicht geschikter das so zu machen:
/* nächsten servo bestimmen */
if (servo_rollover) {
which = 0;
servo_rollover = false;
}
for (which++; which <= SERVO_MAX; which++) {
if (servo_register[which] != 0)
goto next;
}
mfg
Dominic Rathje
Das ist richtig, wenn alle 20 Servos verwendet werden, dann ist das
Interval bei maximal 40 ms. Dasselbe Problem hat der SD20 auch, ich
schätze, daß ich den Algorithmus des SD20 ganz gut "nachempfunden "
habe.
Zur Update-Zeit wird immer behauptet, daß es 20ms sein müssen. Das ist
so nicht richtig. Entscheidend ist die Pulslänge! Allerdings, wie von
lordludwig schon geschrieben, je länger die Update-Zeit, desto mehr
Kraft verliert der Servo. Die meisten Servos schalten nach dem Puls den
Motor ab und warten dann auf einen weiteren Puls.
@Dominic: Deinen letzten Einwand verstehe ich nicht ganz. Vielleicht
kläre ich nochmal die Funktion der beiden Timer auf. Timer2
(SIG_OVERFLOW2) sorgt dafür, daß das 20ms-Intervall eingehalten wird.
Timer1 (SIG_OVERFLOW1) sorgt für die richtige Pulsdauer. Die Pulsdauer
wird innerhalt der Interrupt-Routine für Timer1 gesetzt. Wenn kein
Servo aktiviert ist, oder bei wenigen Servos alle Pulslängen
abgearbeitet wurden, dann wird Timer1 deaktiviert. Aktiviert wird
Timer1 durch die Interrupt-Routine von Timer2.
Noch ein Einwand: Wenn es ums verstehen der Funktion geht, schaut Euch
gern den Code an, ansonsten flasht das lieber in einen Mega8 und
konzentriert Euch auf die Ansteuerung über den I2C-bus.
mfg, Stefan.
Hi,
was ich mit dem Codeschnipsel sagen wollte:
Wenn der Timer1 durch den Timer2 wieder gestartet wird, wird ja auch
servo_rollover auf true gesetzt, was zur folge hat das er in der timer1
routine den code in der if bedingung ausführt, und which auf 1 setzt,
richtig?
anschliessend macht er ganz normal weiter und setzt den timer auf die
zeit von Servo 1 (weil which ja auf eins gesetzt wurde).
Was passiert nun aber wenn der Servo 1 deaktiviert ist, und der erste
serve der überhaupt beachtet werden soll Servo2 ist?
Normalerweise wird ja geprüft, ob das register für den entsprechenden
Servo einen Wert ungleich 0 hat, und nur dann wird die entsprechende
Zeit in den timer geladen.
Deshald habe ich mir gedacht, dass man die Überprüfung auf rollover vor
die abfrage nach dem nächsten gültigen Servo setzt.
mfg
Dominic Rathje
> anschliessend macht er ganz normal weiter und setzt den timer auf die> zeit von Servo 1 (weil which ja auf eins gesetzt wurde).> Was passiert nun aber wenn der Servo 1 deaktiviert ist, und der
erste
> serve der überhaupt beachtet werden soll Servo2 ist?
Öhm, ich glaube Du hast recht. Ich muß mir das nochmal durch den Kopf
gehen lassen, was dann überhaupt passiert. Das ist mir bisher noch
nicht aufgefallen, Danke für den Hinweis.
mfg, Stefan.
Man kann die Servosignale sich überschneiden lassen. Also Servo 1 an,
nach einer Millisekunde Servo2 an, dann nach der passenden Zeit Servo 1
aus. Wenn die nächste Millisekunde rum ist, Servo 3 an, dann Servo 2
nach der passenden Zeit aus, u.s.w.
Also ein Timer, der einmal pro Millisekunde den nächsten Puls
einschaltet und einer, der nach entsprechender Zeit den anderen Puls
wieder ausschaltet. Damit sollten also 20 Servos in 20 Millisekunden
möglich sein.
Was die Kraft betriff, so lässt bei zu großer Pausenlänge bei
Analogservos durchaus die Kraft und die Reaktionsgeschwindigkeit nach.
Bei Digitalservos sollte es keine Rolle spielen.
Hi,
@Rolf Gute Idee, danke
Ich habe mir noch folgende Lösung überlegt:
Man Lässt den 16-Bit timer einfach durchlaufen, und lässt ihn im Output
Compare Modus laufen, somit hat man 2 Interrupts aus dem Timer.
Dann Teilt man die Servos quasi auf, und lässt jede Interrupt routine
10 Steuern und dann müsste es auch passen.
Also quasi in der Interrupt Routine einach den entsprechenden Zeitwert
vom Servo auf den aktuellen Zählerstand aufaddieren und das ganze in
das compare register laden. Damit die Interrupts sich nicht gegenseitig
blockieren, hab ich mir überlegt, dass man Direkt nach dem Ausschalten
des Pins die Interrupts wieder frei gibt. Man müsste natürlich
irgendwie sicherstellen, dass der selbe interrupt in der zeit nicht
mehr auftritt, aber das geht schon irgendwie.
Die Interrupt Routinen sehen im prinzip so aus wie die von Sefan, nur
eben 2.
Naja, ich werd das mal diese oder nächste woche machen, dann seht ihr
schon was ich meine.
Man kann es auch so organisieren, dass Timer0 alle 21ms (20ms geht
schlecht, da 10 Servoimpulse mit 2ms schon 20ms sind und etwas
Sicherheit vorhanden sein sollte) beide "Kreise" anschubst und jeder
Kreis 10 Servos nacheinander durchklappert und am Ende auf den Schubs
von Timer0 wartet. Jeder Kreis benutzt dann einen Compare-Interrupt des
Timer1.
Eine ähnliche Schaltung für 7 Servos am Mega48 ist hier zu finden:
http://www.hanneslux.de/avr/mobau/7ksend/7ksend02.html
Sie ist aber für Handbetrieb (Steuerknüppel) ausgelegt und in ASM
programmiert und ist in der Lage, ein Modellbau-Sendemodul anzusteuern.
Man kann am Quelltext aber sehen, was ich mit "Kreis" gemeint hatte.
...
genau so hab ich mir das vorgestellt.
in asm muss man es wohl prgrammieren, um auszuschließen, dass siech die
beiden kreise gegenseitig blockieren (die ISR muss möglichst kurz sein)
allerdings würde ich etwas mehr als 21ms, so eher gegen 25ms nehmen, da
man die Servos mit einer impulslänge von 0.75ms bis 2.25ms ansteuern
muss, um 180° Drehwinkel zu erreichen.
> @Hannes, ich nehme mal an, das ist Deine Webseite. Wo hast Du> diesen> Mini-Joystick her?
Den gibt's bei http://www.micromotor.de/elektronikzubehoer.htm, aber
wohl auch beim großen blauen chr$(67)...
> genau so hab ich mir das vorgestellt.> in asm muss man es wohl prgrammieren, um auszuschließen, dass siech> die> beiden kreise gegenseitig blockieren (die ISR muss möglichst kurz> sein)
Nunja, in ASM ist das nicht weiter tragisch (da blocliert sich nix), in
C oder BASCOM hätte ich Probleme. Das liegt aber nicht an C oder BASCOM
sondern an mir und meiner Erfahrung mit C oder BASCOM...
Die ISRs sollen kurz sein, aber so tragisch ist das nun auch wieder
nicht, es ist genügend Rechenzeit vorhanden. Ich hatte sogar schon
überlegt, den Mega48 mit 128kHz laufen zu lassen, damit schafft der das
immernoch.
> allerdings würde ich etwas mehr als 21ms, so eher gegen 25ms> nehmen, da> man die Servos mit einer impulslänge von 0.75ms bis 2.25ms> ansteuern> muss, um 180° Drehwinkel zu erreichen.
Das ist ja klar. Wenn die Impulse länger sein sollen, dann dauert das
alles auch etwas länger. Man kann aber auch auf die Erholpause
verzichten und jeden Kreis reiherum klappern lassen.
Besser wäre es allerdings, drei Kreise zu machen, denn da ist ja noch
ein Timer übrig. Ich kann dazu aber jetzt nix Genaueres sagen weil ich
seine Features nicht im Kopf habe.
Weiterhin wäre denkbar, mit freilaufendem Timer1 und 1MHz Takt mit dem
Überlauf alle 15,259ms das Spiel anzuschubsen und die Servos auf 4
Kreise aufzuteilen (Comp1a, Comp1b, Timer0, Timer2). Die Auflösung wird
dabei wegen der 8-Bit-Timer etwas gröber (10µs pro Zahlenwert), aber
damit kann man sicher leben. Die ISRs bleiben auch kurz, sie müssen
lediglich
- mitzählen, welches Servo dran ist,
- den zugehörigen Impulsbreitenwert aus dem SRAM holen,
- den Timer auf den nächsten "Termin" setzen,
- den alten Portpin abschalten und
- den neuen Portpin einschalten.
Das ist bestimmt mit unter 50 Takten zu machen.
Interessantes Projekt, nur habe ich dafür keinen Bedarf...
...
Das mit dem gegenseitigen blockieren habe ich mir so gedacht, dass wenn
eine ISR gerade aktiv ist und im selben moment auch eine andere kommt,
dann wird diese verzögert, bis die erste fertig ist, und somit wird das
sugnal etwas zu lang.
deshalb habe ich mir gedacht dass man die interrupts wieder frei gibt
sobald man den entsprechenden pin Low gesetzt hat.
Dafür sollte man nicht länger als 32 Takte brachen, dann bleiben die
abweichungen unter einer Abstufung bei 8 Bit auflösung (wenn das ganze
mit 8MHz läuft)
ich hatte mir zur Ansteuerung von 16 Servos mal einen Algorithmus
überlegt der mir sehr übersichtlich erscheint (und je übersichtlicher
desto weniger Fehlermöglichkeiten):
eine Tabelle mit max. 16 Einträgen mit je 3 Byte
das erste Byte enthält einen Timervergleichswert, die anderen beiden
Bytes die Bits für die Ausgabe an 2 Ports mit den Servos.
jetzt erzeugt ein Timer alle 1/256 ms einen Interrupt
in diesem wird wird ein mitlaufender Softwarezähler mit dem nächsten
Tabellenwert verglichen und bei Übereinstimmung die beiden Bytes an den
Port ausgegeben.
Wenn man durch ist noch die Ruhezeit abwarten und wieder von vorn
anfangen.
Schöne Ostern
Walter
Gast wrote:
> Hi alle beisammen,> der post ist zwar schon alt (xcus fürs aufwärmen).> Aber was 20 Servos und ATmega8 betrifft hab ich da was für euch.>> http://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=38160&highlight=hopix
Hmmm... - Muss man sich da erst anmelden, um sich den Code mal
anzusehen? Dazu fehlt mir irgendwo die Lust, ist mir zu klickibunti.
Wenn Du willst, dass Andere was davon haben, dann poste es hier.
...
Hallo zusammen
Hat schonmal jemand versucht den Code auf einen PIC zu übersetzen?
Währe es auch möglich den ablauf des Codes mal genau zu erläutern?
Bin gerade auch mit Servos am experimentieren und suche genau sowas.
Nur möchte ich es nicht einfach nur brennen, sondern auch noch verstehen
und da ich sowieso mit PIC's arbeite komme ich ums umschreiben
(verstehen) nicht herum.
freundliche Grüsse
Remo
Hallo Remo,
Mit PIC kenne ich mich zwar nicht aus, aber das Grundprinzip solltest Du
auch ohne AVR-Kenntnisse verstehen können.
Was Du Dir anschauen solltest sind die Routinen SIGNAL(SIG_OVERFLOW1)
und SIGNAL(SIG_OVERFLOW2). Beides sind Timer-Interrupts, welche mit
einer festen Rate aufgerufen werden. Die Funktionen sbi() und cbi()
setzen bzw. löschen ein Bit im Port-Register. Den Rest, insbesondere
SIGNAL(SIG_2WIRE_SERIAL) kannst Du ignorieren.
Wenn Du spezifische Fragen hast, dann stelle sie einfach in diesen
Thread, ich beantworte sie Dir gern.
Hallo Stefan
Danke für deine angebotene Hilfe.
Dann werde ich dies doch gleich mal nützen und dir ein loch in den Bauch
fragen. ;-)
1. Was genau bedeutet das "ul" bei: (CLK / 256000ul)
2. Was haben dein Timer für eintellungen?
freundliche Grüsse
Remo
Zu 1) Schau mal in ein C-Buch. ul bedeutet, daß die Konstante als
unsigned long betrachtet wird.
Zu 2) CLK ist 8MHz, d.h. 8.000.000. Timer 2 ist ein 8-bit Zähler, Timer
1 ist ein 16-bit Zähler. Von da aus kannst Du die Interrupt-Frequenz
leicht berechnen. Timer 2 ist für die Wiederholrate der Impulse
verantwortlich (sollte idealerweise 20ms sein) und Timer 1 legt die
Impulsbreite fest (idealerweise zwischen 1 und 2ms). Der Rest ist ein
bisschen Rechnerei mit den Registern REPEAT, OFFSET und DELTA.
Hallo Stefan
Also ich bin noch nicht der Chef was C-Code angeht.
Ich meinte bei 2. ob du irgendwelche Vorteier verwendest.
Aber ich hab da immer noch ein Knopf.
Du stellst doch mit "TCCR2 = 5" den Presaler von
TIMER2 auf CLKt2s / 128 ein.
Was aber meinst du im Code mit "CK1024" (Kommentar)?
Was ist eigentlich CLKt2s?
Kannst du mir bitte die Timer i deinem Projekt genauer erklären?
freundliche Grüsse
Remo
Du solltest aber schon mal ins Datenblatt schaun, da ist jedes Bit der
Timersteuerung exakt erklärt. Und nimm nicht irgendein Datenblatt,
sondern das zum Controller (für den der Code geschrieben wurde)
passende, denn die Ausstattung der Hardware-Einheiten ist sehr
unterschiedlich.
Hallo Remo,
Dein Blick in das Datenblatt war scheinbar richtig. Der Vorteiler ist
auf 128 gestellt, sollte aber 1024 sein. Der Kommentar suggeriert dies
zumindest.
CLKt2s ist der Takt für den Timer 2. Den gibt es, weil Timer 2 auch
durch einen externen Quarz asynchron getaktet werden kann. CLKt2s kann
man gut in dem Übersichtsschaltbild des Timer 2 im Datenblatt vom Mega8
sehen. Ist ein paar Seiten vor der Tabelle mit den Prescaler-Werten.
Ich kann im Moment nicht genau sagen, warum der Code so funktioniert,
wie er da steht. Hab bitte Geduld bis heute Abend. Schick mir doch mal
eine Mail, dann kann man das per Chat klären.
mfg, Stefan.
@Gast: Remo scheint das Datenblatt gelesen zu haben.
Ich frage mich immer, warum das Berechnen der Timerwerte so große
(Verständnis-)Probleme bereitet. So ein Timer ist doch erstmal nur ein
stinknormaler Zähler, der den über Vorteiler verminderten CPU-Takt zählt
(ja, es gibt auch andere Betriebsarten). Was interessiert nun, wenn man
Servoimpulse generieren will?
- Telegramm-Wiederhol-Abstand (etwa 20 ms, nicht allzu kritisch)
- Impulsbreite eines Impulses (0,7 ms bis 2,3 ms, meist enger)
Ein 16-Bit Timer kann bis 65535 zählen, legt man als Zeitraster 1 µs
fest (Zählfrequenz 1 MHz), dann ergibt das Werte von 20000 für den
Telegrammabstand und 700 bis 2300 für die Impulsbreite. Der minimale
Zeitabstand zwischen zwei Interrupts beträgt somit 700 Timerticks, bei
Vorteiler 1 zu 1 und CPU-Takt von 1 MHz also 700 Takte (da kann eine CPU
schon ganz schön was zusammenrechnen...).
Muss ein 8-Bit-Timer verwendet werden, so bietet sich das Zeitraster von
10 µs an, das ergibt Werte von 70 bis 230 für die Impulsbreite, der
Telegrammstart muss dann anderweitig ermittelt werden.
Der korrekte Telegrammabstand ist eigentlich nur dann wichtig, wenn alle
Impulse zusammengefasst werden sollen, um sie z.B. über einen Sender zum
Empfänger zu schicken (der Empfänger bzw. Decoder braucht eine Lücke zur
Synchronisation). Werden die Impulse nicht zusammengefasst, so ist ein
korrekter Telegrammabstand nicht erforderlich. Wenn das letzte Servo
fertig ist, wird eben mit dem ersten Servo begonnen, so als Ring, immer
im Kreis der Reihe nach. Das erspart nämlich den Timer für den
Telegrammabstand, wodurch eine Interrupt-Einheit eines Timers für eine
Impuls-Sequenz frei wird.
Sollte UART im Spiel sein, sollte man auch einen Baudratenquarz
verwenden. Dann sind die oben genannten Teiler und Zeiten natürlich neu
zu berechnen. Dabei ist ein Kompromiss zwischen voller Ausnutzung des
Zählumfangs der 8-Bit-Timer (also der Auflösung) und der Querzfrequenz
unter Beachtung der Vorteiler-Möglichkeiten zu finden. Der schnellste
mögliche Quarz muss nicht immer das Optimum sein, der
UART-Standard-Quarz meist auch nicht.
Die eigentliche Rechnerei hat dann Unterstufen-Niveau und ist (beim
Evaluieren mehrerer Möglichkeiten) eine reine Fleißaufgabe.