Forum: Mikrocontroller und Digitale Elektronik 8052, Timer 0, 16-Bit -> Reloadwert berechnen


von Ralf (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von MC (Gast)


Lesenswert?

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.

von MC (Gast)


Lesenswert?

@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.

von Ralf (Gast)


Lesenswert?

@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

von Ralf (Gast)


Lesenswert?

@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

von Ralf (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Ralf (Gast)


Lesenswert?

Ah, okay, hatte vermutet, dass im Interrupt-Kapitel steht (wo sonst), 
aber ich muss erstmal das Englisch dort entziffern.

Danke!

Ralf

von MC (Gast)


Angehängte Dateien:

Lesenswert?

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

von Ralf (Gast)


Lesenswert?

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

von MC (Gast)


Lesenswert?

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

von Ralf (Gast)


Lesenswert?

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

von Ralf (Gast)


Lesenswert?

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

von MC (Gast)


Lesenswert?

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

von Ralf (Gast)


Lesenswert?

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

von MC (Gast)


Lesenswert?

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

von Matthias (Gast)


Lesenswert?

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.

von MC (Gast)


Lesenswert?

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

von Ralf (Gast)


Lesenswert?

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

von MC (Gast)


Lesenswert?

Danke, damit wäre alles klar.

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.