|
Zähler
Der 68HC11 hat eine recht komplexe Timer-Struktur, die mit Abstand die meisten
Register im E/A-Bereich belegt. Entsprechend komplex ist seine Programmierung. Basis des Timersystems ist ein 16-Bit-Zähler mit programmierbarem Vorteiler. Die beiden Vorteiler werden durch die Bits PR0/PR1 im Register TMSK2 und RTR0/RTR1im Register PACTL programmiert.Nach einem RESET steht hier jeweils ein Wert von 0, so daß die höchste Taktrateausgewählt ist. Diese beiden Bits können nur direkt nach einem RESETverändert werden, danach ist jeder weitere Zugriff gesperrt. Diese besondereEigenschaft des Mikroprozessors 68HC11 verhindert ungewollte Veränderungen an derTimer-Auflösung durch unzulässige Zugriffe, die während des Betriebs durch Stöhrimpulse von außen ausgelöst werden könnten.
PRn 1 0 | division factor | output period | 0 0 | 1 | 32.77 ms |
0 1 |
4 |
131.07 ms |
1 0 |
8 |
262.14 ms |
1 1 |
16 |
524.29 ms |
|
RTRn 1 0 |
division factor |
output period |
0 0 |
1 |
4.096 ms |
0 1 |
2 |
8.192 ms |
1 0 |
4 |
16.384 ms |
1 1 |
8 |
32.768 ms |
|
TOF | timer overflow flag |
Wird gesetzt, wenn der frei laufende 16-Bit-Zähler von
FFFF auf 0 wechselt (Zählerüberlauf). Wird zurückgesetzt
durch Schreiben einer 1 in TOF. |
TOI | timer overflow interrupt enable |
Interruptfreigabe für "Timer Overflow Interrupt", 0 = Interrupt gesperrt,
1 = Interrupt freigegeben. |
RTIF | real time interrupt flag |
Wird gesetzt, wenn der Realtime-Interrupt-Zähler auf 0 wechselt
(Zählerüberlauf). Wird zurückgesetzt
durch Schreiben einer 1 in RTIF. |
RTII | real time interrupt enable |
Interruptfreigabe für "Realtime Interrupt", 0 = Interrupt gesperrt,
1 = Interrupt freigegeben. |
Der Timer
Der Timer des 68HC11 besitzt eine Länge von 16 Bit. Aus diesem Grund muß
er mit einem 16-Bit-Ladebefehl gelesen werden, da nur dann die beiden Bytes des
Ergebnisses wirklich zusammen gehören. Würden das obere und das untere
Byte getrennt voneinander gelesen, bestände kein eindeutiger Zusammenhang.
Nach einem Reset der CPU ist der Zählerstand des Timers 0. Es gibt keine
Möglichkeit, den Zählerstand des Timers auf einen bestimmten Wert zu
setzen. Direkt nach dem Reset beginnt der Timer aufwärts zu zählen. Bei
jedem Überlauf von $FFFF auf $0000 wird das Überlauf-Bit TOF (Timer Overflow)
im Register TFLG2 gesetzt, um diesen Zustand anzuzeigen. Die Anwendersoftware kann so
auf einfache Weise eine Kaskadierung des Timers auf beliebige Wortlänge
durchführen, da dieses Bit auch einen Interrupt auslösen kann.
Für die meisten Anwendungsfälle ist es sinnvoll, die längste zu
messende Zeit zu bestimmen und dann den Vorteiler so zu definieren, daß
innerhalb dieser Zeit kein Timer-Oberlauf auftreten kann. Alle Zeitpunktberechnungen
können dann mit einfacher 16-Bit-Arithmetik durchgeführt werden. Man darf
nicht übersehen, daß das Timer-System mit 16 Bit auflöst,
der Fehler bezogen auf eine Periode ist 1/65536 entsprechend 0,0015 %. Diese
Genauigkeit ist für fast alle Anwendungen ausreichend.
Erhöht man die Taktrate des Timer-Systems und arbeitet mit höherer
Auflösung, dann addieren sich Software-Reaktionszeiten, was häufig zu
wesentlich größeren Fehlern führen.
Bei jedem Überlauf des Timers von $FFFF auf $0000 wird das Überlauf-Bit (TOF = Timer
Overflow Flag) im TFLG2-Register gesetzt. Das TOF-Bit muß vom Anwenderprogramm gezielt
wieder gelöscht werden. Wird das Auslösen eines Interrupts bei jedem Timer-Oberlauf
gewünscht, dann kann das zum TOF-Bit korrespondierende TOI-Bit (Timer Overflow Interrupt)
im TFLG2-Register auf 1 gesetzt werden.
Real-Time-Interrupt
Häufig wird in einem Mikroprozessorprogramm eine feste Zeitbasis zur Steuerung bestimmter
Abläufe benötigt. Dafür besitzt der 68HC11 einen speziellen Real-Time-Interrupt,
der von den Bits RTII im Register TMSK2 und RTIF im Register TFLG2 gesteuert wird. Nach Ablauf
einer vordefinierten Zeitspanne wird das Bit RTIF im TFLG2-Register gesetzt. Zur Auslösung
eines Interrupts muß auch hier das korrespondierende RTII-Bit im TMSK2-Register auf 1
gesetzt sein. Das RTIF-Bit muß zwingend vom Interrupt-Programm durch Schreiben einer 1
an diese Stelle wieder gelöscht werden, da sonst sofort der nächste Interrupt
ausgelöst würde. Die Periodendauer des Real-Time-Interrupts wird über
zwei Bits im PACTL-Register vorgesehen (RTRO und RTR1) eingestellt.
Beispiel: Uhrentakt mittels RTI
(Quelle: Wallrabe: Mikrocontrollerpraxis, Hanser Verlag)
Dazu gehen wir von der kürzesten Grundperiode 4,096 ms aus, da sie die feinste
Stufung erlaubt. Als Anfangsstand des Zählers wählen wir 244. Die so erzeugte
Periode dauert somit 244 - 4,096 ins = 999,424 ms. An der genauen Sekunde fehlen 0,576 ms,
ein Fehler, der toleriert werden kann. Nach diesen Vorgaben ist das folgende Beispiel
aufgebaut, das, vom Anfangswert 244 ausgehend, nach jedem RTI-Interrupt die Variable
RTIZaehl herunterzählt und bei Erreichen von 0 einen Sekunden-, Minuten- und
Stundenzähler hochzählt.
Die Zeitwerte sollen natürlich dezimal und nicht binär (hexadezimal) ausgegeben
werden. Daher wird nach jedem Inkrementieren eines Zählers dessen Wert mit dem Befehl
DAA sofort in einen BCD-Wert umgewandelt und auch so abgespeichert.
Voraussetzung ist, daß im Hauptprogramm folgende RAM-Variablen vorab definiert und
initialisiert worden sind:
RTIZaehl = Adresse des RTI-Zählers
SekZaehl = Adresse des Sekunden-Zählers
MinZaehl = Adresse des Minuten-Zählers
StdZaehl = Adresse des Stunden-Zählers
Sek_alt = Zwischenspeicher
UHR DEC RTIZaehl RTI-Zähler dekrementieren.
BNE UHR_ENDE
LDAA #244 Wenn RTI-Zähler = 0:
STAA RTIZaehl Neu mit Ausgangswert laden.
LDAA <SekZaehl Nach 1 Sekunde:
INCA Sekundenzähler hochzählen,
DAA in BCD-Zahl wandeln.
STAA <SekZaehl
CMPA #$60
BNE UHR_ENDE
CLRB Nach 60 Sekunden:
STAB <SekZaehl Sekundenzähler auf 0 setzen,
LDAA <MinZaehl Minutenzähler hochzählen,
INCA
DAA
STAA <MinZaehl
CMPA #$60
BNE UHR_ENDE Nach 60 Minuten:
STAB <MinZaehl Minutenzähler auf 0 setzen,
LDAA <StdZaehl Stundenzähler hochzählen,
INCA
DAA
STAA <StdZaehl
CMPA #$24
BNE UHR_ENDE Nach 24 Stunden:
STAB <StdZaehl Stundenzähler auf 0.
UHR_ENDE LDAA #$40 Rücksetzen des RTI-Flags.
STAA TFLG2
RTI
Diese Uhr tickt unsichtbar und lautlos im Inneren des 68HC11. Daher wird
das Unterprogramm zu einem kompletten lauffähigen Beispielprogramm erweitert,
bei dem die Zeit über die serielle Schnittstelle an den PC gesendet und
Monitor sichtbar gemacht wird. Das Programm beginnt mit einem Initialisierungsteil,
in dem die serielle Schnittstelle und der RTI-Interrupt vorbereitet sowie die Variablen
auf ihre Ausgangswerte gesetzt werden.
VECRTI equ $FFEB Real Time Interrupt
TMSK2 equ $1024 Timer Interrupt Mask Reg.2
SCSR equ $102e SCI Status Reg.
SCDR equ $102f SCI Data Reg.
BSR INIT_SCI Schnittstelle Init, siehe 26.html
LDD #UHR
STD VECRTI ...RTI-Serviceroutine "UHR".
LDAA #$40 RTI-Interrupt freigeben.
STAA TMSK2 (4,096 ms ungeaendert wie Vorgabe).
LDAA #244 RTI-Zaehler
STAA <RTIZaehl auf Ausgangswert setzen.
CLRB Ebenso...
STAB <SekZaehl ...Sekunden-Zaehler
STAB <Sek_alt ...Sekunden-Vergleichszaehler
STAB <MinZaehl ...Minuten-Zaehler
STAB <StdZaehl ...Stunden-Zaehler
CLI Interrupts aktivieren
UhrStart LDAA <SekZaehl Warten, bis Sekunde einen neuen...
CMPA <Sek_alt
BEQ UhrStart ...Wert hat.
STAA <Sek_alt Neuen Wert in Vergleichszaehler
LDAA <StdZaehl Stunden ausgeben
JSR Hex_Tx
LDAA #":" Trennzeichen ":" ausgeben
JSR Byte_Tx
LDAA <MinZaehl Minuten ausgeben
JSR Hex_Tx
LDAA #":"
JSR Byte_Tx
LDAA <SekZaehl Sekunden ausgeben
JSR Hex_Tx
LDAA #$0D Carriage Return ausgeben
JSR Byte_Tx
BRA UhrStart
* Ausgabe eines Bytes hexadezimal ueber die serielle Schnittstelle
* Das zu sendende Byte muî im Akku A vorliegen.
Hex_Tx PSHA Zu sendendes Byte retten.
ANDA #$F0 Hw.-Nibble abtrennen
LSRA und 4 Stellen nach rechts schieben,
LSRA dabei Nullen nachziehen.
LSRA
LSRA
CMPA #9 0 bis 9 oder A bis F ?
BHI Buchst1
Ziff1 ADDA #"0" 0 bis 9.
JSR Byte_Tx Zum Sender.
BRA Nibble2
Buchst1 ADDA #"A" A bis F.
JSR Byte_Tx
Nibble2 PULA Byte wieder holen.
ANDA #$0F Nw.-Nibble abtrennen.
CMPA #9
BHI Buchst2
Ziff2 ADDA #"0"
JSR Byte_Tx
RTS
Buchst2 ADDA #"A"
JSR Byte_Tx
RTS
* Ausgabe eines Bytes ueber die serielle Schnittstelle
* Das zu sendende Byte muî im Akku A vorliegen.
Byte_Tx LDAB SCSR Statusregister TDRE abfragen, ob leer.
ANDB #$80
BEQ Byte_Tx Abfrage wiederholen, wenn nicht.
STAA SCDR Byte ausgeben.
RTS
Der Zählerstand kann von drei Input-Capture-Registern zur exakten Bestimmung
des Zeitpunktes einer extern erkannten Flanke und von fünf Output-Compare-Registern
zur Erzeugung exakter Impulse und Kurvenformen ausgewertet werden.
- Input Capture: Der Zählerstand des
freilaufenden 16-Bit-Zählers wird als Echtzeitreferenz betrachtet, jede
von der entsprechend programmierten Prozessorhardware erkannte Flanke eines
extemen Signals speichert den aktuellen Stand dieses Zählers und damit
den genauen Zeitpunkt in einem der Input-Capture-Register. Auf diese
Weise ist es möglich, aus zwei aufeinanderfolgenden Impulsflanken
eines Signals die Periodendauer als Differenz der Zeitpunkte zu bestimmen.
Impulsbreiten können durch Verändern der aktiven Flanke nach
Eintreten des ersten Ereignisses bestimmt werden.
Jede einzelne Input-Capture-Funktion beinhaltet ein 16-Bit-Latch, in dem der Zeitpunkt eines
Ereignisses festgehalten werden kann. Die notwendige Synchronisation zwischen asynchronem
externen Ereignis und internem E-Takt wird von der Prozessor-Hardware unsichtbar für
den Anwender vorgenommen. Der hierdurch entstehende Fehler ist klein gegen die
Timer-Auflösung.
Der Transfer des Timer-Inhalts in eines der Input-Capture-Register erfolgt bei der
nichtzählenden Flanke des Timer-Signals, so daß ein sicherer Wert auf jeden
Fall garantiert werden kann. Die Hardware zur Übertragung das Timer-Inhaltes ist
für jedes Input-Capture-Register getrennt vorhanden, so daß es erlaubt ist,
alle drei Input-Capture-Eingänge gleichzeitig zu triggern. Das Auslesen eines der
Input-Capture-Register T1C1 bis T1C3 muß mit einem 116-Bit-Zugriff
erfolgen, da nur so gewährleistet ist, daß die beiden Bytes des Ergebnisses
wirklich zusammengehören. Wird der Inhalt eines der Input-Capture-Register nicht
gelesen und tritt dann ein weiteres Ereignis ein, so wird der alte Wert überschrieben.
Der Inhalt dieser Register repräsentiert also immer das letzte aufgetretene Ereignis.
Je ein Bit im TMSK1- und TFLG1-Register sind als Schnittstelle zum
Anwenderprogramm vorgesehen.
Die Bits IC1F bis IC3F werden vom Mikroprozessor gesetzt, wenn einer der drei
Input-Capture-Kanäle ein Ereignis registriert. Die zugehörigen Bits IC1I bis IC3I
sind jeweils zur Freigabe eines Interrupts als Reaktion vorgesehen und müssen vom
Anwenderprogramm definiert werden.
Jeder der drei Kanäle kann auf verschiedene Flanken programmiert werden. Dazu sind im
Register TCTL2 jeweils 2 Bit pro Capture-Kanal vorgesehen:
EDGxB | EDGxA | Aktivierte Flanke |
0 | 0 | Keine, Kanal ausgeschaltet |
0 | 1 | Ansteigende Flanke |
1 | 0 | Abfallende Flanke |
1 | 1 | Beide Flanken |
- Output Compare: Jedes der fünf Output-Compare-Register
wird laufend mit dem Zählerstand des freilaufenden Zählers
verglichen. Bei Übereinstimmung wird dann exakt in diesem Moment die
programmierte Reaktion ausgelöst, zum Beispiel die Ausgabe eines
bestimmten Signals an einem Port. Auf diese Weise ist es möglich, ein
Ausgangssignal mit der Genauigkeit und Auflösung des Timer-Systems zu
einem gewünschten Zeitpunkt zu erzeugen, ohne dabei Fehler durch
Software-Reaktionszeiten oder auch Verzögerungen durch Interrupt-Antwortzeiten
zu haben.
Insgesamt sind fünf Output-Compare-Funktionen vorhanden, die jeweils
ein 16-Bit-Vergleichsregister besitzen. Der Registerinhalt wird ständig mit dem
Zählerstand verglichen. Bei Übereinstimmung wird das vorprogrammierte
Ereignis ausgelöst. Auch hier ist je ein Bit im TMSK1- und TFLG1-Register als
Schnittstelle zum Anwenderprogramm vorgesehen. Das jeweiling OCxF-Bit wird gesetzt,
wenn Register und Zähler übereinstimmen. Mit den zugehörigen OCxI-Bit
kann der Interrupt gesperrt oder freigegeben werden.
Für die Kanäle 2 bis 5 kann im TCTL1-Register festgelegt werden, was mit
dem zugehörigen Pin beim Eintreten eines Ereignisses geschehen soll:
OMx | OLx | Reaktion |
0 | 0 | Keine |
0 | 1 | Polarität ändern (toggle) |
1 | 0 | auf 0 setzen |
1 | 1 | auf 1 setzen |
Mit dem Output Compare Register 1 kann man alle OC-Pins gleichzeitig manipulieren.
Der OC-Pn 1 hat dabei Priorität über die anderen. Der OC1-Funktion sind
zwei spezielle Register zugeordnet, OC1M und OC1D. Die Bits 3 bis 7 dieser Register
entsprechen direkt den Leitungen von Port A, auf denen die OC-Werte ausgegeben werden.
Für jede Leitung von Port A, die durch die OC-Funktion beinflußt werden soll,
muß das entsprechende Bit in OC1M auf 1 gesetzt werden. Das Bitmuster für die
Ausgabe wird dann in OC1D eingetragen. Auf diese Weise lassen sich fünf Pins exakt
zeitgleich mit einem einzigen Output-Compare-Register beeinflussen.
Pulse-Akkumulator
Der Pulse-Akkumulator des 68HC1 ist ein 8-Bit-Zähler, der wahlweise als
Ereigniszähler oder als Zeitzähler mit externer Gate-Funktion verwendet werden
kann. Für beide Fälle wird der Anschluß Port A Bit 7 als Eingang für
das externe Signal benutzt. Es sind zwei Möglichkeiten vorgesehen, einen Interrupt
auszulösen: bei jeder aktiven Flanke dieses Eingangs oder bei Überlauf des
Zählerstandes von $FF auf $00.
Wird der Pulse-Akkumulator als Zeitzähler eingesetzt, dann verarbeitet er das durch
64 geteilte Signal des internen E-Takts. Die Auflösung beträgt damit bei einer
Quarzfrequenz von 8 MHz exakt 32 Mikrosekunden, die längste meßbare Periode
somit 256 * 0,032 ms = 8,192 ms. Beim Einsatz als Ereigniszähler können maximal
256 Flanken gezählt werden, die zu verarbeitende Frequenz muß niedriger als
der E-Takt des Mikroprozessors sein. Die für die Interruptauslösung des
Pulse-Akkumulatorsystems zuständigen Konfigurations- und Flagbits sind zusammen
mit anderen Funktionen im TMSK2- und TFLG2-Register untergebracht.
Das PAOVF-Bit im TFLG2-Register wird immer dann gesetzt, wenn ein Überlauf des
Zählers von $FF auf $00 eintritt. Durch Setzen des korrespondierenden
Interrupt-Bits PAOVI im TMSK2-Register kann der Anwender bestimmen, ob durch dieses
Ereignis ein Interrupt ausgelöst werden soll (0 = kein Interrupt). Der gleiche
Mechanismus steuert mit den Bits PAIF und PAII einen möglichen Prozessor-Interrupt
bei jeder aktiven Flanke am Eingang des Pulse-Akkumulators.
- Damit das Pulse-Akkumulator-System überhaupt arbeiten kann, muß der externe
Pin 7 von Port A als Eingang konfiguriert werden. Hierzu muß das Bit DDRA7 im
PACTL-Register auf 0 gesetzt werden.
- Das Bit PAEN dieses Registers schaltet den Pulse-Akkumulator generell ein und aus,
wobei der Zählerstand nach dem Ausschalten des Systems weiter erhalten bleibt und
ausgewertet werden kann.
- Das Bit PAMOD definiert die Betriebsart des Zählers: eine O macht ihn zum
Ereigniszähler, eine 1 wählt die Zeitmessung.
- PEDGE erlaubt die Wahl der aktiven Flanke. Eine 0 läßt den Zähler
auf abfallende Flanken reagieren, in der Betriebsart Zeitzähler stoppt ein Low-Signal
die Messung, eine 1 in PEDGE wählt die ansteigende Flanke und ein High-Signal
als Sperre für die Betriebsart Zeitzähler.
|
|
|