Hallo, ich möchte den Timer 0 eines 8052 Controllers im 16-Bit Modus betreiben. Der Timer 0 unterstützt keinen Autoreload Mechanismus wie der Timer 2 es tut, diesen kann ich leider nicht verwenden, der ist zwingend mit etwas anderem belegt. Also muss ich das Nachladen manuell machen. Bin mir jetzt nicht ganz im klaren darüber, wie ich den Reloadwert berechne. Ich hab mir folgenden Gedankengang gemacht: Am Interrupt-Vektor des Timer 0 steht ein LJMP auf die eigentliche ISR. Dort erfolgen zunächst ein paar PUSH Befehle, und dann geht meine eigentliche ISR los. An exakt diesem Punkt müsste ich m.E. folgendes machen: - Den Timer stoppen - TH0,TL0 neu initialisieren - Timer starten Da der Reload demnach erst ein paar Zyklen nach dem Einsprung in den Interrupt erfolgt, habe ich ja quasi einen Versatz im Timing, also muss der Reload-Wert korrigiert werden. Ist es richtig, dass ich dazu die Dauer des LJMP-Befehls, der PUSH-Befehle sowie der Befehle fürs stoppen/Nachladen/starten des Timers vom eigentlichen Reload-Wert abziehen muss? Es soll eben so genau wie möglich sein. Dass es nicht ganz exakt wird, wenn der Controller einen 2- oder 4-Zyklen-Befehl abarbeitet, wenn der Interrupt kommt, ist klar. Danke für eure Hilfe. Ralf
Ralf wrote: > Es soll eben so genau wie möglich sein. Dass es nicht ganz exakt wird, > wenn der Controller einen 2- oder 4-Zyklen-Befehl abarbeitet, wenn der > Interrupt kommt, ist klar. Irrtum, es geht ganz exakt auf den Zyklus genau. Der Trick ist, daß der Timer ja nach dem Überlauf weiter zählt. D.h. der Timer selbst enthält die genaue Zeitdauer für den Interrupteintritt usw. Der Reload erfolgt also durch Addition des Verkürzungswertes zu der aktuellen Zeit: http://home.tiscali.de/peterd/appl/soft/clock/index.htm Peter
Genau so wie du dir das denkst, muss das auch gemacht werden. Mir gefällt dabei nur eine Sache nicht: die Reihenfolge. Als allererstes muss dein ljmp ausgeführt werden. In der eigentlichen ISR würde ich erst den Timer stoppen, neuladen und starten und anschließend erst den Rest der Routine mit Registersichern,... ausführen lassen. Somit hast du einen kleineren Versatz zwischen Auslösen des Interrupts und Neustart des Timers. Den Reloadwert berechnest du einfach aus deinem Startwert minus der Zeit zum Springen, Stoppen, Neuladen und Starten.
@Peter Dannegger: Danke für den Link, die Methode kannte ich auch noch nicht. Man lernt nie aus! Bis jetzt hatte ich immer die von Ralf beschriebene Methode genommen.
@MC: An der Reihenfolge kann ich nix ändern, ich bin von Assembler auf C umgestiegen :) Aber immerhin weiss ich jetzt, dass ich auf dem richtigen Weg war. Danke. @Peter Dannegger: Ich stimme MC zu, super Sache, deine Homepage. Besten Dank, ich werd mir das mal ansehen. Ralf
@Peter Danneger: Hallo Peter, ich hab mir deinen Code mal angesehen, eine Frage hätte ich noch. Du ziehst den Reloadwert von acht ab, das ist die Anzahl der Maschinenzyklen für die Addition bzw. zwischen Stoppen und Starten des Timers, soweit klar. Was micht jetzt etwas verwirrt, du setzt EA vor dem Starten des Timers. Wenn nun ein höher priorisierter Interrupt bereits ansteht, wird das Starten des Timers für die Dauer der entsprechenden ISR verzögert, ist das so gewollt oder versteh ich was falsch? Ralf
Hallo Peter, wäre schön wenn du mir bei Gelegenheit antworten könntest, ich kann im Hardware Manual nix finden, was meine Frage (s.o.) beantwortet :( Ralf
Ralf wrote: > Was micht jetzt etwas verwirrt, du setzt EA vor dem > Starten des Timers. Wenn nun ein höher priorisierter Interrupt bereits > ansteht, wird das Starten des Timers für die Dauer der entsprechenden > ISR verzögert Nein, das kann nicht passieren. Das Ausführen eines Interrupts wird um einen weiteren Befehl verzögert, wenn: "3. The instruction in progress is RETI or any access to the IE or IP registers." Peter
Ah, okay, hatte vermutet, dass im Interrupt-Kapitel steht (wo sonst), aber ich muss erstmal das Englisch dort entziffern. Danke! Ralf
Hallo, ich bin's nochmal. Ich hätte auch noch ein paar Fragen zu dem Code. Warum addiert man zu dem Inhalt von tl0 etwas hinzu? Müsste man diesen Inhalt nicht vom Reloadwert abziehen? Code von Peter Dannegger:
1 | clr tr0 ;no overflow during addition |
2 | xch a, tl0 |
3 | add a, #low(8-T0_reload) ;stop for 8 cycle |
4 | xch a, tl0 |
5 | xch a, th0 |
6 | addc a, #high(8-T0_reload) |
7 | xch a, th0 |
8 | setb ea ;other interrupts enabled after next setb tr0 |
9 | reti |
Danke schon mal, MC
Hi MC, nein, ist meiner Meinung nach schon richtig so. Ich geh mal davon aus,dass du die Funktionsweise von Peters Code an sich verstanden hast (so wie ich letztendlich auch :) Nimm als Beispiel mal einen berechneten Reloadwert von 0x7FF0. Der Timer Überlauf trat ein, bis zu den Befehlen für die Neuberechnung sind noch ein paar Zyklen vergangen, sagen wir 20 -> 0x14. Die musst du ja auch beachten, daher werden die in die Addition miteinbezogen. Wenn du abziehen würdest, dann würde es länger dauern, bis der Interrupt wieder dran kommt (der Timer zählt ja hoch(!)). Ralf
Stimmt, du hast recht. Ich muss die nach dem Überlauf vergangenen Zyklen nicht abziehen, sondern aufaddieren. Aber dann sollte man doch auch die Zyklen, die bei der Neuberechnung des Reloadwertes vergehen, hinzuaddieren und nicht abziehen, wie im Code von Peter!?
> ...Aber dann sollte man doch auch die Zyklen, die bei der Neuberechnung des > Reloadwertes vergehen, hinzuaddieren und nicht abziehen, wie im Code von > Peter!? Natürlich musst du dazuaddieren, weil der Zähler doch HOCHzählt! Wenn du addierst, verkürzt du die Zeit, bis der nächste Interrupt kommt, und da du ja eine Berechnung machst und diese Zeit kompensieren willst, musst du draufaddieren. Der Wert acht entspricht der Zeit, die die Berechnung braucht. Wird jetzt ein bisschen klarer? :) Wenn nicht, nochmal fragen. Ralf
Ach so, nochwas, wenn du den Reloadwert von acht abziehst, hast du übrigens das gleiche Ergebnis, wie wenn du auf den Reloadwert acht addierst. Beachte, dass du immer von 16 Bit relevanten Bits ausgehen musst, und da der Timer hochzählt, von 0 abziehen musst, wobei ab dem 17.Bit alles ignoriert wird. Beispiel: Reloadwert = 0 - 6144 = 0xE800 "8 - Reload": 8 - 6144 = 0xE808 "Reloadwert + 8": 0 - 6144 + 8 = 0xE808 In dem Beispiel fehlt jetzt eben noch die Addition auf den aktuellen Zählerstand. Ralf
Ja okay, jetzt ist alles klar. Das Missverständnis lag darin, dass ich
von einem anderen Reloadwert ausgegangen bin. Für mich ist der
Reloadwert der fertig berechnete Wert, der in den Timer geladen wird.
Wenn ich das an deinem Beispiel richtig verstanden habe, dann meinst du
mit Reloadwert, die Anzahl Zählschritte bis zum nächsten Überlauf, oder
nicht?
>Reloadwert = 0 - 6144 = 0xE800
Danke für deine Hilfe, jetzt ist alles klar.
MC
Hi MC, okay, sorry für das Missverständnis. > Wenn ich das an deinem Beispiel richtig verstanden habe, dann meinst du > mit Reloadwert, die Anzahl Zählschritte bis zum nächsten Überlauf, oder > nicht? Nach deiner Beschreibung ist das der endgültige Reloadwert, basierend auf dem Reloadwert, welcher das Intervall erzeugt, plus die Zeit für die Addition, plus die Zeit, die von Auslösen des Interrupts bis zur Addition vergeht. "Plus" ist hier so zu verstehen, dass es ja tatsächlich aufaddiert werden muss, weil der Timer ja hochzählt, wohingegen ja in der "normalen" Logik abgezogen werden muss, damit man schneller bei 0 ist(weiss der Geier warum die Timer hochzählen :). Ich grübel grad, wie man die Begrifflichkeiten besser voneinander unterscheiden kann... Hm... Ich würde sagen, wir nehmen den Begriff "Intervall"-Reloadwert für die eigentlich gewünschte Zeitbasis, und den "einfachen" Reloadwert als den endgültigen, fertig durchgerechneten Wert, mit dem der Timer geladen werden muss. Also, um das ganze einigermaßen übersichtlich zu verpacken: Reload = Intervall_Reload + Dauer_Addition + aktueller Zählerstand Ralf
>weiss der Geier warum die Timer hochzählen Die Zählrichtung ist weniger das verwirrende. Würde der Timer von 0 an hochzählen und nicht erst beim Überlauf, sondern beim erreichen eines vorgegebenen Wertes einen Interrupt auslösen, wäre das ja auch ohne schierigkeiten veständlich. >Reloadwert = 0 - 6144 = 0xE800 Diese verwirrende Herangehensweise wäre damit überflüssig. Man könnte einfach die 6144 Zählschritte in ein Vergleichsregister laden und beim Auslösen des Interrupt sind dann genau 6144 Zählschritte vergangen. Warum die Timer beim 8051 nicht so aufgebaut sind, weiß ich nicht. Ich vermute mal, dass dies aufwendiger ist als einfach bei jedem Zählerüberlauf einen Interrupt auszulösen. Die AVRs haben meineswissens einen solchen Timeraufau.
Auf 0 prüfen ist wesentlich schneller als auf einen 16-Bit Zahlenwert, was nach jedem Timer-Tick passieren müsste. Das Problem mit dem Reload-Wert in der ISR nachladen und ggf. die Laufzeiten einkorrigieren kann man umgehen, indem man z.B. den AT89LP4052 nimmt. Dieser hat auch im Mode 1 (16-bit Timer) ein automatisches Relaod durch zwei zusätzliche SFR (RL0, RH0 bzw. RL1, RH1). Damit lässt sich eine sehr genaue Zeitbasis realisieren.
Ich bin's nochmal. Der Threat ist zwar schon was älter, aber ich glaub, mir kann trotzdem geholfen werden.
1 | clr tr0 |
2 | xch a, tl0 |
3 | add a, #low(8-T0_reload) ;stop for 8 cycle |
4 | xch a, tl0 |
5 | xch a, th0 |
6 | addc a, #high(8-T0_reload) |
7 | xch a, th0 |
8 | setb ea |
9 | setb tr0 |
Der Code zur Reloadberechnung dauert genau neun Zyklen. Es werden aber nur acht Zyklen Verzögerung berücksichtigt. Meine Frage: Wofür wird kein Zyklus berücksichtig? Zum Stoppen oder zum Starten des Timers? Danke schon mal, MC
Hi MC, es sind ja alles Befehle mit einem Zyklus. Wenn du davon ausgehst, dass nach CLR TR0 der Timer gestoppt ist, ist der erste Zyklus nach XCH A,TL0 und der letzte nach SETB TR0 abgelaufen, und er nach diesem Befehl läuft der Timer ja wieder. Also: clr tr0 [0] <- Hier ist der Timer gestoppt xch a, tl0 [1] add a, #low(8-T0_reload) ;stop for 8 cycle [2] xch a, tl0 [3] xch a, th0 [4] addc a, #high(8-T0_reload) [5] xch a, th0 [6] setb ea [7] setb tr0 [8] <- Hier startet der Timer wieder Das heisst, ignoriert wird quasi CLR TR0. Ralf
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.