Hallo Leute,
ich möchte gern in C für eine gewisse Zeit einen Port über I2C
ansteuern. Die Ansteuerung an sich ist kein Problem, verstehe mich nur
nicht mit Interrupts, Timern u.ä.
Wie kann ich z.B. für 40s über "i2C_setbyte" einen Zustand setzen und
ihn danach wieder auf "LOW" setzen?
Vielen Dank im Voraus.
MfG,
Bastian
Bastian schrieb:> Hallo Leute,>> ich möchte gern in C für eine gewisse Zeit einen Port über I2C> ansteuern. Die Ansteuerung an sich ist kein Problem, verstehe mich nur> nicht mit Interrupts, Timern u.ä.>> Wie kann ich z.B. für 40s über "i2C_setbyte" einen Zustand setzen und> ihn danach wieder auf "LOW" setzen?>> Vielen Dank im Voraus.>> MfG,> Bastian
1. i2C_setbyte(1);
2. 40s "warten" oder was anderes tun
3. i2c-setbyte(0);
Um so eine Wartezeit zu generieren nimmt man üblicherweise einen Timer.
Wenn du nicht weißt wie der funktioniert solltest du das hier im
Tutorial durchlesen.
Andererseits kann man auch eine Warteschleife erstellen, nur wäre dann
der Controller für 40 s in dieser gefangen.
Vielen Dank erst einmal für Deine Antwort. Das Problem ist, dass der
Controller kontinuierlich Werte vom A/D-Umsetzer aufnimmt (natürlich
auch innerhalb dieser 40s). Es muss also unabhängig von der
Messwertaufnahme funktionieren. Nach 40s soll der Baustein, den ich über
I2C anspreche, für eine gewisse Zeit deaktiviert werden, um dann nach
einer bestimmten Zeit wieder eingeschaltet zu werden. Im Vordergrund
läuft grundsätzlich die Messwertaufnahme.
Dann wäre die einfachste Lösung vermutlich einen Timer so einzustellen,
dass dieser nach 40 Sekunden einen Interrupt auslöst, während derer du
dann immer noch deine Messwerte zwischenspeichern könntest.
Die ganzen Grundlagen dazu stehen im Tutorial. Also zumindest die
Prinzipien, da es ja eigentlich für AVRs gedacht ist.
Bitte nicht das AVR-Tutorial lesen, wenn Du einen 8051 programmieren
musst. Da passen die ganzen Register und Einstellungen überhaupt nicht.
Timer beim 8051 nimmst Du am Anfang nur Timer0/1 im 16-Bit Mode.
TMOD = 0x11; setzt beide Timer in den Mode. Nur wenn du serielle
Schnittstelle verwendest, dann brauchst Du evtl. den Timer 1 für die
Baudrate.
Wenn Du keinen Startwert und keinen Reload-Wert setzt, dann läuft der
Timer 65536 Maschinenzyklen (also bei 12MHz ohne X2-Mode 65,5ms). Bei
jedem Timerüberlauf musst Du das Timer-Flag wieder löschen (entfällt im
Interrupt-Betrieb, aber erstmal ohne) und zählst eine Variable hoch.
Nimm mal unsigned int Ticks;
Bei jedem Überlauf wird Ticks erhöht und wenn die benötigte Anzahl
(=Zeit in s / 65,5ms) erreicht ist, machst Du was und setzt Ticks wieder
auf 0.
1
TMOD=0x11;
2
//TH0 = (65536 - Benoetigte_Zeit)/256; // für Startwert oder Reload
3
//TL0 = (65536 - Benoetigte_Zeit)%256; // für Startwert oder Reload
4
TR0=1;// Timer 0 starten - TR1 für Timer 1
5
6
while(1)// Endlosschleife
7
{
8
if(TF0==1)// Timer 0 ist überglaufen
9
{Ticks++;// sonst braucht man erstmal nix.
10
TF0=0;// Flag löschen
11
}
12
if(Ticks==0)
13
{i2c_setbyte(1);//einschalten -> wird mehrfach ausgeführt
14
// oder du fügst ein Merker-Flag ein
15
}
16
if(Ticks==Ausschaltzeit)
17
{i2c_setbyte(0);// ausschalten -> wird nur einmal ausgeführt
18
Ticks=0;// wenn Ticks gelöscht wird
19
}
20
}
Damit kannst Du zunächst einen Vorgang realisieren. Wenn Du die Ticks
einfach weiterlaufen lässt, dann kann man auch sowas machen:
1
if(Taster1==1)
2
{Ausschaltzeit1=Ticks+Dauer1;//zur aktuellen Zeit die Dauer addieren
3
Einschalten(1);
4
}
5
if(Ticks==Ausschaltzeit1)
6
{Ausschalten(1);
7
}
8
if(Taster2==1)
9
{Ausschaltzeit2=Ticks+Dauer2;//zur aktuellen Zeit die Dauer addieren
10
Einschalten(2);
11
}
12
if(Ticks==Ausschaltzeit2)
13
{Ausschalten(2);
14
}
Das funktioniert mit beliebig vielen Signalen und es ist auch kein
Problem, wenn es einen Überlauf gibt. Wenn Ticks z.B. auf 65000 steht
und du willst eine Dauer von 60 Sekunden (= 60000ms/65,536ms = 915
Ticks), dann ergibt die Addition für die Ausschaltzeit 379 (gleicher
Datentyp für alle Schaltzeiten!).
tschuessle
Bernhard
Teil 2: jetzt mit Reload für kurze Zeiten
=========================================
Die Timer zählen nach dem Überlauf sofort ab 0x0000 weiter. Also muss
man wieder einen Startwert (Reload) einstellen, so dass der Timer nach
einer kürzeren Zeit überläuft. Die Formeln im Quelltext kann man
problemlos mit Konstanten füllen, die Division und Modulo wird dabei
nicht wirklich berechnet, sondern der Compiler fügt die Ergebniswerte in
den Code ein. (anders sieht es für variable Zeiten aus, dazu später
mehr)
1
TH0=(65536-Benoetigte_Zeit)/256;// für Startwert oder Reload
2
TL0=(65536-Benoetigte_Zeit)%256;// für Startwert oder Reload
Die Benoetigte_Zeit ist ein Wert in Maschinenzyklen (je nach System, oft
aber 1µs). Für eine gewünschte Timer-Zeit von 1ms schreibt man dann also
einfach
1
TH0=(65536-1000)/256;// 1ms = 1000MZ, davon das High-Byte in TH0
2
TL0=(65536-1000)%256;// das Low-Byte in TL0
Da der 8051 bei den Timern 0 und 1 keine getrennten Zähl- und
Reload-Register hat, muss man diesen Wert nach jedem Überlauf möglichst
schnell wieder in die Register schreiben (daher nimmt man da dann meist
Interrupts).
1
while(1)// Endlosschleife
2
{
3
if(TF0==1)// Timer 0 ist überglaufen
4
{TH0=(65536-1000)/256;// 1ms = 1000MZ, Hih-Byte
5
TL0=(65536-1000)%256;// Low-Byte in TL0
6
TF0=0;// Flag löschen
7
Ticks++;// sonst braucht man erstmal nix.
8
}
9
// Rest des Programms
Bei einer Millisekunde läuft die Variable Ticks bereits nach 65536ms,
also etwa einer Minute über. Wenn man längere Zeiten benötigt, dann
entweder die Ticks alle 10, 20 oder 50ms einstellen oder (in C ja kein
echtes Problem) unsigned long Ticks; verwenden. Damit kannst Du in
ms-Auflösung Zeiten bis 1193 Stunden erzeugen, bevor der Zähler
überläuft... Braucht aber mehr Speicher (auch für die Schaltzeiten!).
Ohne Interrupts kann es aber passieren, dass die Bearbeitung des
restlichen Programms länger als die Timer-Zeit dauert. Dann werden die
Zeiten sehr ungenau. Also erstmal nicht wundern...
Teil 3: Reload mit variablen Zeiten
===================================
Für variable Zeiten (z.B. PWM für Modellbau-Servo mit 1-2ms Impuls und
20ms Periodendauer) darf man die (16Bit) Variablen nicht einfach in die
obige Zeile einsetzen. Dabei wird sonst die Division und die
Modulo-Operation wirklich ausgeführt (die sich ergebenden Zeiten sind
dann für die Tonne...). Hier muss man (am besten eine Phase vorher, wenn
der Timer gerade übergelaufen war) die Variable berechnen.
Teil 4: Timer-Interrupt ohne Schrecken
======================================
Mit Interrupt vermeidet man die Ungenauigkeiten beim Timer-Reload.
Sobald der Timer bei aktiviertem Interrupt überläuft, springt der
Prozessor in die Interrupt-Service-Routine und führt den dortigen Code
aus. Danach geht es an der alten Stelle im Hauptprogramm weiter. (der
sollte daher möglichst kurz sein)
Interrupts aktiviert man erstmal einzeln für jede Quelle, danach noch
den Hauptschalter EA.
1
TMOD=0x11;
2
TH0=(65536-1000)/256;// für Startwert
3
TL0=(65536-1000)%256;// für Startwert
4
TR0=1;// Timer 0 starten - TR1 für Timer 1
5
ET0=1;// Interrupt für Timer 0 aktivieren
6
EA=1;// Globalen Interrupt aktivieren -- ab jetzt geht's rund
die Interrupt-Service-Routine ersetzt die if(TF0)-Abfrage aus dem
Hauptprogramm. Ist kein Hexenwerk, es kommt nur ein Schlüsselwort und
eine Nummer, die die Quelle angibt.
1
voidISR_Timer0(void)interrupt1
2
{
3
TH0=(65536-1000)/256;// Reload
4
TL0=(65536-1000)%256;// Reload
5
// TF0 = 0; // Flag löschen geht jetzt automatisch! Der reine Luxus!
6
Ticks++;// sonst braucht man erstmal nix.
7
}
Ticks muss jetzt als globale Variable deklariert werden, damit sowohl
die ISR als auch main() darauf zugreifen können.
Erbsenzähler rechnen natürlich noch aus, wieviele µs es bis zum Relaod
dauert und korrigieren damit den Reload-Wert:
1
// wenn es z.B. 6us bis zum Sprung in die ISR dauert:
2
TH0=(65536-1000+6)/256;// korrigiert um 6us
3
TL0=(65536-1000+6)%256;// korrigiert
Es ist aber nicht immer möglich, exakt auf 1µs genau zu arbeiten (ein
laufender ASM-Befehl kann nicht unterbrochen werden und dauert 1, 2 oder
4 MZ). Aber zuviel überlegungen nutzen eh nichts, weil der Quarztakt ja
auch nicht exakt ist. (eine Uhr mit Timer-Interrupt läuft aber trotzdem
mit einer Abweichung von unter 1s/Woche)
tschuessle
Bernhard