Forum: Mikrocontroller und Digitale Elektronik Virtuelle Timer .... huh??


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Rainer (Gast)


Lesenswert?

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!

von Thomas Burkhardt (Gast)


Lesenswert?

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.

von Rainer (Gast)


Lesenswert?

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 :(.

von Peter D. (peda)


Lesenswert?

Du kannst doch die Impulse einfach nacheinander erzeugen.


Peter

von Rainer (Gast)


Lesenswert?

Das versteh ich jetzt nicht ganz. :S

von Rainer (Gast)


Lesenswert?

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 .... :(

von Peter D. (peda)


Lesenswert?

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

von Rainer (Gast)


Lesenswert?

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!!

von C. Lechner (Gast)


Lesenswert?

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

von Rainer (Gast)


Lesenswert?

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

von ...HanneS... (Gast)


Lesenswert?

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.

...

von Hagen (Gast)


Lesenswert?

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

von Jürgen Schuhmacher (Gast)


Lesenswert?

Was machst Du bei aperiodischen Timern? Wird die Liste dann dynamisch
umsortiert?  Von wem?

von peter dannegger (Gast)


Lesenswert?

"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

von Mattias (Gast)


Lesenswert?

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

von crazy horse (Gast)


Lesenswert?

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.

von AxelR. (Gast)


Lesenswert?


von Achim Walther (Gast)


Lesenswert?

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.

von Rainer (Gast)


Lesenswert?

Danke für die vielen Antworten Leute! Sind ein paar wirklich gute
Ansätze!! Werde gleich mal was ausprobieren :D :D!

Danke nochmal!

von Hagen (Gast)


Lesenswert?

@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

von AxelR. (Gast)


Lesenswert?

...
Aber das ist im Grunde
alles programmiertechnisches Grundwissen :)
...
wohl eher nicht ;-))
AxelR.

von Hagen (Gast)


Lesenswert?

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

von Hagen (Gast)


Lesenswert?

ü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

von Jens Stache (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.