Hallo miteinander! Ich steh vor folgendem Problem: Ich möchte mit einem ATmega16 gleich mehrere Servos ansteuern. Nun stellt sich aber die Frage wie man das mit möglichst wenigen Timern anstellen könnte. Irgendwann hab ich mal was von virtuellen Timern gehört. Weiß jemand vielleicht was ich mir da genau drunter vorstellen soll? Ich mein sind dass dann nur Variablen die in der ISR inkrementiert werden? Wenn ja, wie kann ich dann damit einen Interrupt auslösen (ich mein wenn das nicht geht is das ganze doch fürn Ar... g). Oder versteh ich da überhaupt etwas falsch. Hab leider dazu auch nix im Forum oder Google gefunden. Vielleicht weiß ja jemand von euch weiter. Danke schon mal!
Hi, eine Möglichkeit ist es, einen Timer mit relativ hohem Takt laufen zu lassen - der kleinste gemeinsame Teiler deiner verschiedenen virtuellen Timer. In der ISR des Timers werden dann unterschiedliche Variablen inkrementiert und mit zugehörigen Werten verglichen. Abhängig von diesen Vergleichen führst du dann unterschiedliche Operation der verschiedenen Servos aus. Ich weiss nicht, was du damit meinst, dann mit diesen nochmal einen Interrupt auslösen zu wollen. Das brauchst du doch nicht.
Hmm .... klingt nicht schlecht. Aber heißt dass denn nicht das ich eine sehr hohe Anzahl an Interrupts bekomme. So wie ich das verstehe lass ich also einfach einen Timer z.B. im Output Compare (CTC) und sehr kleinem OCR Wert laufen, damit ich eine sehr kleine Auflösung bekomme. Und in der ISR dieses Timers behandle ich dann die einzelnen Fälle (wenn eine Variable einen bestimmten Wert hat). ... naja funktioniert, aber mit einer großen Menge Interrupts :(.
Ach ja und das mit dem kleinsten gemeinsamen Teiler wird nicht ganz funktionieren. Ich möchte ja Servos ansteueren d.h. die Zeiten variieren zwischen 1-2ms, ein gemeinsamer Timer würde mir somit eine gewisse Auflösung vorschreiben .... :(
Die Servopulse sind doch 1,5..2,5ms lang, danach kann dann eine Pause sein. Und in dieser Pause erzeugt man dann den Impuls für den nächsten Servo. Bei der alten Analogtechnik wurden ja auch alle Servopulse nacheinander über Funk übertragen. Du schaltest also den ersten Servo ein und setzt den Comparewert auf die 1. Pulsdauer. Dann im Interrupt, schaltest Du den ersten Servo aus, den 2. ein und lädst die 2. Pulsdauer in den Comparewert usw. bis alle Servos rum sind. Peter
Wow!! Das ist ja eine gute Idee!! Danke für den großartigen Tipp! Nur noch eine Frage: Wie bekomm ich dann die 20ms Pause mit ins Spiel? Über einen 2. Timer? Danke!!
IMHO wären die Comparewerte schlichtweg Overkill. Man könnte doch einfach den Timer nach dem Anfang des Impulses entsprechend einstellen, dass er genau im richtigen Moment nen Interrupt auslöst, um den Impuls zu beenden. Dann das nächste Servo. Soweit ich weis, sind die Servopulse in Neutralstellung 1.5ms lang, 1.0ms bzw. 2.0ms in beiden Endausschlägen. Das ganze wiederholt sich alle 20ms, aber ich kann mich auch täuschen. - Christoph
nein, nein du das stimmt so. ich denke das war genau peter´s idee mit den Timer auf das Ende zu stellen um schließlich das nächste einzuschalten. Aber wie bekomm ich da noch meinen 20ms Zyklus mit rein? :S
http://www.brummbaerhannes.de/hannes/avr/7ksend/7ksend01.html Ist aber schon etwas älter, würde ich heute eleganter machen. Tut aber hervorragend seinen Dienst. ...
Also du hast 3 Impulse die von jetzt ab jeweils 10, 15, und 35 ms folgen sollen: 1.) Comparewert ist 10 ms, dann ISR 2.) in ISR nun Comparewert auf 15ms - 10ms = 5ms einstellen 3.) nach 5ms die nächste ISR für gesamt 15ms 4.) in ISR Comparewert nun auch 35-15 = 20ms einstellen. So bekommst du nacheinander deine Impules bei 10,15 und 30ms und simmulierst 3 Timer mit einem in sequentiell. Bei solchen Timern benutze ich sehr gerne eine verlinkte und sortierte Liste von Timerwerten. Der kürzeste Timer liegt dabei am Anfang der Liste. In der ISR wird dieser Timer aus der aktiven Liste gelöscht und an das Ende der Liste neu verlinkt. Es entsteht sowas wie ein "Ringbuffer" von Timer-Delays wobei es dann ziemlich einfach wird zb. Oneshot-Timer oder sich wiederholende Timer zu programmieren. Gruß Hagen
Was machst Du bei aperiodischen Timern? Wird die Liste dann dynamisch umsortiert? Von wem?
"Wie bekomm ich dann die 20ms Pause mit ins Spiel?" Soweit ich das weiß, ist das nur ein Minimalwert. Möglichkeiten: 1. Nach dem letzten Servo wird für 20ms eine Pause gemacht. 2. Es werden alle Zeiten addiert und sind sie unter 20ms, wird für die Restzeit eine Pause gemacht. Realisierung: Im Compare-Interrupt eine Variable hochzählen, die bestimmt, welcher Servo gerade dran ist und welcher Comparewert als nächstes zu laden ist. Peter
Hallo Rainer, das mit den 20 ms kannst Du vergessen. Dieser Wert ist völlig unkritisch. Die Servos arbeiten immer nur dann, wenn sie ihren Puls erhalten. In der restlichen Zeit sind die Servos einfach tot. Daraus resultiert zb. auch die Servokraft. Es gibt Schaltungen, welche den letzten Puls einfach kopieren und alle 5 ms wiederholen, bis ein anderer Puls vom Empfänger kommt. Dadurch erhöt sich die Servokraft und vor allem die Stellgeschwindigkeit. Die neuen Digitalservos bekommen alle 2 ms ihren Stellwert zugewiesen. Wenn Du sowieso beim Experiomentieren bist, versuch doch mal die Servowerte nur alle 2s oder so rauszugeben. Dann kannst Du genau beobachten, dass sie nur alle 2s etwas zucken und lange für ihre Zielposition brauchen. Wie oft Du die Pulse an den Servo gibst ist wirklich völlig egal. Mattias
die Impulspause ist völlig unkritisch. Hast du z.B. 8 Servos, ergeben sich Gesamtzykluszeiten von 8-16ms, damit kommen Servos problemlos klar, kannst also völlig ohne zus. Pausenzeit auskommen. Wenn du es ganz genau machen willst, kannst du auch die ausgegebenen Zeiten addieren und die Impulspause dynamisch anpassen, so dass sich eine konstante Zykluszeit ergibt. Ebenso möglich: sowohl den OCR- als auch den OV-Int benutzen. Der Timer startet mit bei OV mit einem Wert -20ms (Takt und Vorteiler so wählen, dass möglichst der gesamte Zählbereich genutzt wird),lädst das OCR mit dem Wert für das 1.Servo und setzt auch diesen Ausgang. Beim OCR-Int löschst du den Ausgang, lädst OCR mit OCR_alt+Servozeit2 usw.
Ich weiss auch nochwas ;-) Meine Messungen mit Graupner-Standard-Analogservos haben folgendes ergeben: Minimalstellung bei ca. 0.8 ms, Mitte bei 1.5 ms, Maximalstellung bei 2.3 ms. Die Werte können bei verschiedenen Herstellern variieren. Die Pause dazwischen ist zwar unkritisch, darf aber nicht kleiner als 12 ms und nicht größer als 40 ms sein. Und sie beeinflußt teilweise den Stellwinkel des Servos. Also besser hält man sich an den Zyklus einer Modellfernsteuerung - der liegt so bei 22.5 ms. Mehrere Servos steuert man sinnvollerweise sequenziell an. Dazu eignet sich der Timer1 des AtMega mit zwei OCRs. OCR1A stellt man auf 20 ms und OCR1B auf die Pulsweite des Servos. In der ISR für OCR1B wird dann die Pulsweite des nächsten Servos aufaddiert und ein Left-Shift auf das Servoregister ausgeführt (schiebt ein Bit durch die Portpins und steuert somit die Servos an). Hier mein Beispiel: http://www.voidpointer.de/avr/servo7.asm Spaß, Achim.
Danke für die vielen Antworten Leute! Sind ein paar wirklich gute Ansätze!! Werde gleich mal was ausprobieren :D :D! Danke nochmal!
@Jürgen: "Was machst Du bei aperiodischen Timern? Wird die Liste dann dynamisch umsortiert? Von wem?" Korrekt sie wird "umsortiert". Nur es ist eine verlinkte Liste und somit fällt während der "Umsortierung" keine Kopieroperationen an,sondern der aktuelle Timereintrag der gerade in der ISR abgearbeitet wurde wird je nach Status entweder in die Liste der Freien und abgelaufenen Timer eingetragen oder er wird einfach sortiert in die verlinkte Liste erneut eingefügt. Dazu beginnt man einfach beim nächsten Eintrag nach dem Timer und durchläuft die verlinkte Liste solange bis der aktuelle Timerwert kleiner ist als der dort stehende. Genau an dieser Stelle wird dann die verlinkte Liste "aufgetrennt" und der aktuelle Timerdatensatz eingelinkt. Aber das ist im Grunde alles programmiertechnisches Grundwissen :) Suche mal nach "verketteten Listen" oder "doppelt verlinkte Listen". Eine "Umsortierung" im wörtlichen Sinne ist also nicht nötig, eher eine "Einsortierung". Es dürfte klar sein das entweder die Timer-Callback zu dem Timereintrag sich um das Neueinfügen künmmern muß, oder bei autom. periodischen Timern macht das die ISR. Stelle dir einfach vor das der absolute Timerwert in einen Zeitoffset zum vorherigen Timerwert umgerechnet wird. D.h. die verlinkte Liste enthält nicht den absoluten Zeitwert sondern den Differenzbetrag zum vorherigen Timerwert. Dieser Wert ist identisch mit dem Comparewert. Ein absoluter Timerwert wird direkt beim "Einsortieren" in den Differnzbetrag umgerechnet. Man iteriert durch die verlinkte Liste und subtrahiert vom Absolutbetrag immer die Differenzbeträge. Das macht man so lange bis der Absolutwert kleiner als der nächstfolgende Differnzbetrag ist, schwups hat man 1.) den nun gültigen Differnzbetrag ermittelt und 2.) die Einfügeposition des Timerdatensatzes in die verlinkte Liste. Nach dem Einfügen muß man nur noch vom nachfolgenden Timerdatensatz den Differenzbetrag durch Subtraktion korregieren. Allerdings, im konkreten Fall gibt es auf Grund der technischen Gegebenheiten der Servos einfachere Lösungen. Gruß Hagen
... Aber das ist im Grunde alles programmiertechnisches Grundwissen :) ... wohl eher nicht ;-)) AxelR.
Hm ja da haste Recht. Ich meinte auch eher damit das es auf kurz oder lang bei jedem Programmierer der Fall sein sollte sich mit solchen Algorithmen und Datenstrukturen beschäftigt zu haben (war also keineswegs herablassend gemeint). Wenn man Programmieren in der Schule oder beim Studium lernt dann sollte das eigentlich Pflichtstoff sein, zumindestens war's bei mir so. Gruß Hagen
übrigens sehe ich gerade das Peter Dannegger hier http://www.mikrocontroller.net/forum/read-4-49709.html#new genau das gepostet hat was ich eigentlich meine. Ich habs jetzt nur überflogen aber er arbeitet eindeutig mit sortierten verlinkten Listen und Deltas==Differenzbeträgen. Gruß Hagen
Hallo, ich würde folgendes machen -- habe auch mehrere PWM's (24!) schon realisiert, um 24 verschiedene Led,s, jede einzeln in der Helligkeit zu steuern über Midisignale -- das schaffen die Dinger (AVR's) spielend. z.B. 8 Ausgänge = 1 Port 1. Port auf Ausgang und auf Null 2. Timer1 einschalten 3. Anfangswert1 für Impulsdauer 1 + gelesene Timerzeit in SRAM_Wert1 speichern 4. Anfangswert2 usw. + ... ... MainLoop: - Vergleiche SRAMWert1 mit momentanen Timer1Wert - wenn abgelaufen rcall und Impulspause1 in Sram laden (Flag setzen für die nächste Abfrage - Controlregister anlegen) und das entsprechende AusgangsportPin setzen/löschen und schnell zurück (rcall) - Vergleich SRAMWert2 mit Timer1 usw. rjmp MainLoop klingt jetzt etwas unübersichtlich, aber das Programm hält nie an, geht immer weiter. Zwischendurch können noch andere Dinge gemacht werden -- Impulsbreite ändern, AD abfragen (über Interrupt) usw. In Assembler dauert eine Schleife nur wenige us Der Timer1 wird nie angehalten, sondern läuft und läuft Die Mainschleif vergleicht und vergleicht und schaltet bei abgelaufener Zeit die entsprechenden Pins um.
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.