Forum: Mikrocontroller und Digitale Elektronik 89S52 Timer Interrupt zeit berechnen


von Tobias J. (neutronium)


Lesenswert?

Hallo,

wie kann ich für einen Timer interrupt die konkrete Zeit ausrechnen?
Normal ist das ja
Dauer eines Zykluses = 12 / Taktfrequenz => 12 / 11059200 = 1,085µs
Dauer des Timers bis zum Überlauf = 65536 * 1,085µs = 71,11ms

und dann will ich halt, das der Interrupt in 2 Sekunden ausgelöst wird:
also:
2000ms * 10³ / 1,08506944µs = 1843200 und das in Hex sind ja 1C20

also TH0 = 1C
und TL0 = 20

aber das passt ja nicht oder?
Wenn man das wieder in Dauer des Timers bis zum Überlauf einsetzt, dann 
komme ich auf 1,20s also
65536 * 1843200

oder habe ich da irgendeinen Denkfehler?

Naja, ich kann ja auch mal was zu meinem Ziel sagen, was vielleicht eher 
an den Anfang gehört aber naja.

Eine LED soll bei einem Druck auf einen Taster leuchten, die 
eingeschaltete LED soll dann für 2 Sekunden leuchten und dann 
ausgeschaltet werden und dafür eine andere ein.
Wenn während dieses Zeitraums der Taster nochmal gedrückt wird, dann 
soll das Zählen von vorne beginnen.

von Wilhelm F. (Gast)


Lesenswert?

Für 2 Sekunden mußt du in den Timerinterrupt noch mal eine Variable als 
Zähler einbauen, denn der Timer 16 bit schafft die 2 Sekunden von 
alleine gar nicht.

Mit dem 11MHz-Quarz schafft man ungefähr maximal 70ms, wenn der Timer 
von Null an immer auf 10000h über läuft, also wieder auf Null.

von Tobias J. (neutronium)


Lesenswert?

also eine Variable?

Wie kriege ich das denn hin die Variable immer eins hochzuzählen, wenn 
der Timer voll gelaufen ist, bzw. wenn ein Zyklus durch ist?
Ich will ja wie gesagt nach 2 Sekunden eine LED ausschalten und davor 
soll diese angeschaltet sein, also soll die ISR Für den Timer erst 
anlaufen, wenn 28 mal der Timer voll gelaufen ist?

Ich weiß echt nicht, wie ich das realisieren soll ...

Hättest du mal ein Beispiel oder jemand anders?

: Bearbeitet durch User
von Davis (Gast)


Lesenswert?

> ... 1843200 und das in Hex sind ja 1C20 ...

Das sind 1C2000.

Du lässt den Timer z.B. alle 0,1 Sekunden überlaufen. Diese Überläufe 
zählst du dann mit und kommst damit auf längere Zeiten.

von Davis (Gast)


Lesenswert?

Gerade sehe ich, dass 0,1 Sekundenüberläufe nicht gehen, also nimmst du 
z. B. 50 Millisekunden.

von Wilhelm F. (Gast)


Lesenswert?

Tobias J. schrieb:

> Hättest du mal ein Beispiel oder jemand anders?

Mit welcher Programmiersprache arbeitest du? Assembler oder C 
vielleicht?

Den Timer 0 muß man beim 8051 im Interrupt selbst mit einem errechneten 
Wert nach laden, und das ist etwas kniffelig, weil er ja weiter läuft 
und auf den Takt genau 100%-ig präzise sein soll.

Leichter wäre es, den Timer 2 zu benutzen, weil der Autoreload hat. Aber 
das hilft ja nicht, wenn man Timer 2 für was anderes braucht.

Ich benutze aber den Timer 0 selbst immer als Zeitgeber. Ein 8051 hat 
auch gar keinen Timer 2, erst der 8052.

Es gab noch einen Timer-Mode für Timer 0 und 1 mit Autoreload im 8 bit 
Mode, die machen aber eine sehr hohe Interruptbelastung, weil 8 bit sehr 
schnell über laufen, und verwendete diesen Mode deswegen nach 
Möglichkeit nie.

Die Timer-ISR ist aber schon vorhanden, oder? Könntest du da 
Inline-Assemblercode einfügen? Als Zusatzzähler nimmt man ein Byte aus 
dem RAM.

von Wilhelm F. (Gast)


Lesenswert?

So, ich hab mal etwas gerechnet, um einen Timerinterrupt in genau 50ms 
zu bekommen. Dafür muß man den Timer im Interrupt stets mit dem Wert 
65536-46080 laden. 46080 Schritte braucht er für 50ms.

Das wäre der hexadezimale Wert von 4C00h. Den muß man im Interrupt 
irgend wie immer neu in den Timer hinein bekommen.

Da zählst du mit einer Variablen dann für 2 Sekunden die 50ms-Schritte, 
und es sind derer 40. Bei 40 führt man seine Aktion aus, z.B. LED 
blinken, und setzt die Variable wieder auf 0 zurück.

Also mach am Anfang der Interruptroutine mal sowas wie z.B.:

TL0 = 00h
TH0 = 4Ch

Dann gehts mal grob ungefähr. Wie es dann fein geht, da hab ich bestimmt 
auch noch was parat.

Da der Nachladewert für TL0 hier zufällig Null ist, gibt es vielleicht 
noch einen kleinen Trick, und man lädt nur TH0 nach. TL0 steht ja beim 
Interrupteinsprung schon irgend wo, zählt weiter, vielleicht auf 3 oder 
4. In diesem Fall kann man das auch rein nur in C machen, und braucht 
keinen Assembler mit genauer Berechnung.

von Tobias J. (neutronium)


Lesenswert?

Naja, ich mache das ganze in C und derzeit sieht das so aus:
Die Taster und die LED's sind übrigens Low-aktiv

#include <stdio.h>
#include <reg52.h>
#include <irq52.h>

void timer_isr(void) interrupt;

near int counter = 0;
near int presscounter = 0;

IRQ_VECTOR(timer_isr, TIMER0)

void timer_isr(void) interrupt
{
    TH0 = 4C;
    if(counter < 40)
    {
        P3_B4 = 1; // Ist eine LED
        P3_B6 = 0;
    }
    counter++;
}

void main(void)
{
    TMOD = 0x01;
    TF0 = 0;    // Überlaufflag löschen
    TR0 = 1;    // Timer 0 starten
    ET0 = 1;    // Interrupt für Timer 0 freigeben
    EA = 1;     // alle Interrupts freigeben

    P3_B3 = 0; // Ebenfalls LED

    if(P3_B2 == 0)
    {
        P3_B3 = 1;
        P3_B4 = 0;
        presscounter++;
        if(presscounter == 2)
        {
            counter = 0;
            presscounter = 0;
        }
    }

    while(1);
}

So ist das doch dann sowet richtig oder ?
Wenn 40 Überläufe fertig sind, dann sind das 2 Sekunden oder? 50ms * 40 
= 2000ms = 2 Sekunden

So ist das doch richtig oder?
P3_B2 wird beim Start eingeschaltet.
Wenn der Taster P3_B2 betätigt wird, dann geht die LED P3_B4 an und wenn 
dann 2 Sekunden vorbei sind, dann geht die LED P3_B4 an und die LED 
P3_B6 leuchtet.

: Bearbeitet durch User
von Tobias J. (neutronium)


Lesenswert?

naja, ich habe mich ja noch einmal korrigiert.
Eigentlich müsste es ja 40 sein, weil
50 * 20 erst 2 Sekunden machen, bzw. 2000ms => 2sek

Du hast auch übrigens Recht, ja es müsste >= sein.

Sonst ist das ganze aber doch jetzt richtig oder?
Wenn 2 sekunden um sind, dann geht die eine LED aus und die andere an 
und wenn währenddessen der Taster gedrückt wird, wie setze ich dann 
beginnt das ganze ja auch von vorne oder muss ich dafür auch TL0 und TH0 
= 00 setzen?

: Bearbeitet durch User
von Wilhelm F. (Gast)


Lesenswert?

Tobias J. schrieb:

> So ist das doch richtig oder?

Was sagt denn der Test?

Tobias J. schrieb:

> Du hast auch übrigens Recht, ja es müsste >= sein.

Nein, ich war mir nicht ganz sicher, und habe darum wieder meinen 
Beitrag gelöscht.

Du machtest es etwas anders, als ich selbst es gemacht hätte. Aber das 
ist ja egal, viele Wege führen nach Rom.

von Wilhelm F. (Gast)


Lesenswert?

Was ist eigentlich mit der Main-Schleife while(1)?

Sollte nicht dort der if-Block hinein?

von Wilhelm F. (Gast)


Lesenswert?

Also die Vorgehensweise wäre ja grundsätzlich:

In Main die gedrückte Taste erkennen.

Von Entprellung muß man da bei 2 Sekunden auch gar nicht groß reden.

Entweder setzt man dann in Main ein Flag, was dem Interrupt mit teilt, 
daß jetzt eine Taste gedrückt wurde. Von da ab zählt der Zähler im 
Interrupt bis 40 hoch. Wenn er diese erreicht hat, wird er wieder 
gelöscht, und auch das Flag, und die LED betätigt.

Oder wenn man es genauer haben möchte, gibt man bei erkannter Taste erst 
den Timer frei, und hat den Timer schon mit dem Nachladewert vor 
geladen. Beim Endwert 40 löscht der Interrupt die LED wieder, und sperrt 
auch den Interrupt und stoppt den Timer.

Also es gibt da hunderttausend Varianten. Zur absolut exakten 
Tastendruckerkennung müßte diese auch einen externen Interrupt auslösen, 
und in diesem Interrupt erst die Zählung initialisiert werden.

von Tobias J. (neutronium)


Lesenswert?

Sooo .. ich habe das ganze noch einmal überarbeitet aber muss der Timer 
dann erst beim Tastendrücken gestartet werden, wenn ich das erst haben 
will, das die 2 Sekunden erst beim Drück gezählt werden soll?

So sieht das ganze jetzt aus.

#include <stdio.h>
#include <reg52.h>
#include <irq52.h>

void timer_isr(void) interrupt;

near int counter = 0;
near int presscounter = 0;

IRQ_VECTOR(timer_isr, TIMER0)

void timer_isr(void) interrupt
{
    TH0 = 4C;
    if(counter >= 40)
    {
        P3_B4 = 1; // Ist eine LED
        P3_B6 = 0;
    }
    counter++;
}

void main(void)
{
    TMOD = 0x01;
    TF0 = 0;    // Überlaufflag löschen
    ET0 = 1;    // Interrupt für Timer 0 freigeben
    EA = 1;     // alle Interrupts freigeben

    P3_B3 = 0; // Ebenfalls LED

    while(1)
    {
        if(P3_B2 == 0)
        {
            P3_B3 = 1;
            P3_B4 = 0;
            if(presscounter == 1)
            {
                TR0 = 1; // Timer 0 starten
            }
            else if(presscounter == 2)
            {
                counter = 0;
                presscounter = 0;
                TF0 = 0;
            }
            presscounter++;
        }
        _wait_ms(1); // wegen Tastenprellen
    }
}

bzw. mit ^= kann man ja invertieren aber das nützt ja nichts? (also bei 
TR0 invertieren?)

: Bearbeitet durch User
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.