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.
Sehr schöner Code, der mir auch beim Lernen von C weitergeholfen hat. Danke !
Wow, das ging runter wie Öl. :-) Bitte schön, gern geschehen.
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
ein servo kann fast so langsam gepulst sein wie er will, aber bei 40ms verliert er schon deutlich an stärke.
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? mfg, Stefan.
> @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
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
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.
Hier mal mein Code dazu ATMega8 (leicht portierbar) max 20 Servos mit 20ms gepulst ca 2/3 Leistungsreserve Ausgangspins frei einstellbar.
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.