Die Arbeit mit der seriellen Schnittstelle setzt geeignete Zugänge in einer Programmiersprache voraus. Hier wird sowohl auf die ältere Programmierung unter DOS als auch auf die Windows-Programmierung eingegangen. Viele Sprachen haben eigene Befehle und Funktionen zur seriellen Datenübertragung. In anderen Fällen muss man die direkte Registerprogrammierung der PC-Hardware anwenden. Hier wird zunächst die Hardware der seriellen Schnittstelle erläutert, die man z.B. für die Programmierung in Turbo Pascal kennen muss.
Jeder PC verfügt über eine oder mehrere serielle RS232-Schnittstellen, die entweder bereits fest auf der Hauptplatine integriert oder aber als Steckkarte ausgeführt sind. Jede serielle Schnittstelle besteht im wesentlichen aus dem UART (Universal Asynchronous Receiver/Transmitter) und nachgeschalteten Leitungstreibern und Leitungsempfängern zur Umsetzung zwischen TTL-Pegeln und RS232-Pegeln (+/-12V-Pegeln). Abb. 3.1 zeigt den prinzipiellen Aufbau einer seriellen Schnittstellenkarte.
Abb. 3.1 Prinzipschaltbild einer seriellen Schnittstelle im PC
Die erste bis vierte Schnittstelle (COM1 ... COM4) werden durch ihre Adresslage im IO-Bereich des PCs und ihre Interruptnummer (IRQ) unterschieden. Die erste Adresse eines UART wird als Basisadresse (BA) bezeichnet. Basisadresse und IRQ können meist über Jumper auf der Schnittstellenkarte oder durch Software-Initialisierung eingestellt werden. Die folgende Tabelle zeigt die übliche Zuordnung:
Schnittstelle |
Basisadresse |
IRQ |
COM1 |
3F8h |
IRQ4 |
COM2 |
2F8h |
IRQ3 |
COM3 |
3E8h |
(IRQ4) |
COM4 |
2E8h |
(IRQ3) |
COM1 und COM3 sowie COM2 und COM4 teilen sich oft einen Interruptkanal. Das bedeutet, dass nur jeweils eine Software zu einer Zeit die Interrupts einer der beiden Schnittstellen benutzen kann. Mehr als zwei Schnittstellen können also nur dann gleichzeitig genutzt werden, wenn man auch Programme ohne Interruptnutzung einsetzt. Dies gelingt z.B. mit DOS-Programmen unter Turbo-Pascal (vgl. Kap. 3.3). Der Verarbeitung serieller Zeichen unter Windows ist jedoch immer auf Interrupts angewiesen.
Die Funktion der Schnittstelle wird vollständig über 10 Register des UART gesteuert. Da nur bis zu acht Adressen zur Verfügung stehen, erfolgt eine interne Umschaltung durch das DLAB-Bit (Bit 7 im Leitungs-Steuerregister). Die Adressen der einzelnen Register sind hier mit ihrem Offset, d.h. mit ihrem Abstand zur Basisadresse angegeben.
Registeradressen des UART 8250
Offset |
DLAB |
Schreiben |
Lesen |
0 |
0 |
Sende-Haltegegister |
Empfangs-Haltegergister |
0 |
1 |
Baudratenregister (Lowbyte) |
|
1 |
0 |
Interrupt- Freigaberegister |
|
1 |
1 |
Baudratenregister (Highbyte) |
|
2 |
0 |
|
Interrupt-Erkennungsregister |
3 |
0 |
Leitungs- Steuerregister |
|
4 |
0 |
Modem- Steuerregister |
|
5 |
0 |
|
Leitungs- Statusregister |
6 |
0 |
|
Modem- Statusregister |
7 |
0 |
Scratchpad-Register (nur 16450) |
Scratchpad-Register (nur 16450) |
Vor einer Datenübertragung muss der UART zunächst initialisiert werden, indem die einzelnen Steuerregister entsprechend den gewünschten Übertragungsparametern programmiert werden. Die eigentliche serielle Übertragung erfolgt meist in kleinen Blöcken von acht Bits, also als einzelne Bytes. Um ein Byte zu senden, schreibt man es in das Sende-Halteregister:
Sende-Halteregister (Offset=0, DLAB=0)
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
Beim Empfang eines seriell gesendeten Zeichens gelangt dieses parallel in das Empfangs-Halteregister:
Empfangs-Halteregister (Offset=0, DLAB=0)
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
Für die Übertragung muss man eine Übertragungsgeschwindigkeit wählen, die in Bits pro Sekunde (Baud) angegeben wird. Bei 1200 Baud werden zusammen mit je einem Startbit und einem Stopbit in einer Sekunde bis zu 120 Zeichen mit einer Länge von 8 Bits übertragen. Der UART 8250 verwendet einen Quarz mit 1,8432 MHz. Diese Frequenz wird intern durch einen einstellbaren Teilerfaktor zur 16-fachen Baudrate geteilt. Der erforderliche Teilerfaktor berechnet sich also zu
1843200 115200
Teilerfaktor = ----------------- = -------------
16 x Baudrate Baudrate
Der Teilerfaktor muss in Highbyte und Lowbyte zerlegt und in die entsprechenden Register (Offsetadresse 0 und 1 bei gesetztem DLAB-Bit) geschrieben werden.
Baudratenregister, Lowbyte (Offset=0, DLAB=1)
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
Baudratenregister, Highbyte (Offset=1, DLAB=1)
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
Die folgende Tabelle zeigt die Einstellungen für die wichtigsten Baudraten:
Baudrate |
Teilerfaktor |
Highbyte |
Lowbyte |
50 |
2304 |
9 |
0 |
300 |
384 |
1 |
128 |
1200 |
96 |
0 |
96 |
2400 |
48 |
0 |
48 |
4800 |
24 |
0 |
24 |
9600 |
12 |
0 |
12 |
19200 |
6 |
0 |
6 |
38400 |
3 |
0 |
3 |
57600 |
2 |
0 |
2 |
115200 |
1 |
0 |
1 |
Von der seriellen Schnittstelle können Interrupts ausgelöst werden. Dazu muss das Interrupt-Freigaberegister programmiert werden. Um einen oder mehrere Interrupts zu aktivieren, muss das entsprechende Bit gesetzt werden.
Interrupt-Freigaberegister (Offset=1)
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
0 |
0 |
0 |
0 |
SINP |
ERBK |
TBE |
RXRD |
Interruptfreigabe für:
SINP: Zustandsänderung einer Handshake-Leitung
ERBK: Fehler oder BREAK erkannt
TBE: Sende-Haltepuffer ist leer
RXRD: Ein Zeichen wurde empfangen
Ein Fehler kann beim Empfang eines Zeichens erkannt werden, wenn z.B. die vereinbarte Zeichenlänge oder die Anzahl der Stopbits nicht eingehalten wird. Liegt am Eingang RXD ein anhaltender High-Pegel, dann spricht man von einem BREAK-Zustand. Dieser Zustand würde nach Ablauf der normalen Übertragungszeit der vereinbarten Datenbits wie ein Empfangsfehler gewertet.
Um Interrupts auszulösen, müssen noch zwei weitere Bedingungen erfüllt sein: Zum einen muss der Interruptcontroller entsprechend programmiert sein, zum anderen muss die Leitung OUT2 (Bit 3 im Modem-Steuerregister) gesetzt sein.
Wenn einer von mehreren möglichen Interrupts aufgetreten ist, kann man im Interrupt-Erkennungsregister die Quelle erfahren:
Interrupt-Erkennungsregister (Offset=2)
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
0 |
0 |
0 |
0 |
0 |
ID1 |
ID0 |
PND |
ID1, ID0:Identifizierungsbits:
00: Zustandsänderung einer Handshake-Leitung
01: Sende-Haltepuffer ist leer
10: Ein Zeichen wurde empfangen
11: Fehler oder BREAK-Bedingung erkannt
PND: Interrupt ist aufgetreten
Ein anhängiger Interrupt nach einer Zustandsänderung einer Handshakeleitung wird durch das Lesen des Modem-Statusregisters wieder gelöscht. Ebenso löscht ein Lesen des Empfangs-Haltegegisters einen Empfangs-Interrupt und das Lesen des Leitungs-Statusregisters einen Fehler- oder BREAK-Interrupt. Der Interrupt durch ein leeres Sende-Halteregister kann entweder durch das Lesen des Interrupt-Erkennungsregisters oder durch Schreiben in das Sende-Halteregister gelöscht werden.
Das Leitungs-Steuerregister braucht im Normalfall nur zur Initialisierung der seriellen Schnittstelle beschrieben werden. Es enthält acht Steuerbits zur Festlegung der Übertragungsparameter:
Leitungs-Steuerregister (Offset=3)
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
DLAB |
BRK |
PAR2 |
PAR1 |
PAR0 |
STOP |
DAB1 |
DAB0 |
DLAB: Zugriff auf das Baudratenregister freigeben
BRK: BREAK-Zustand an TXD setzen
PAR2...PAR0: Festlegung des Paritätsbits
000: keins
001: ungerade
011: gerade
101: mark
111: space
STOP: Festlegung von zwei Stopbits
DAB1...DAB0: Anzahl der Datenbits
00: 5 Bits
01: 6 Bits
10: 7 Bits
11: 8 Bits
Das DLAB-Bits bewirkt eine interne Umschaltung der Register mit den Offsetadressen 0 und 1. Es muss gesetzt werden, um die Baudrate einzustellen, im normalen Betrieb muss es dagegen gelöscht sein. Das BRK-Bit kann verwendet werden, um den Zustand der Leitung TXD als Ausgang völlig unabhängig von der normalen Funktion des UARTs zu steuern.
Die übrigen Bits des Registers legen für den Sender und den Empfänger des UART ein eventuelles Paritätsbit, die Anzahl der Stopbits und die Anzahl der Datenbits fest. Ein übliches Format ist z.B. 8 Datenbits, kein Paritätsbit und ein Stopbit. Für diese Einstellung müssen nur die beiden Bits DAB1 und DAB0 gesetzt werden (Registerinhalt = 3). Zusammen mit dem Startbit werden dann insgesamt 10 Bits übertragen, sodass z.B. bei einer Übertragungsgeschwindigkeit von 19200 Baud genau 1920 Zeichen pro Sekunde übertragen werden können.
Das Leitungs-Steuerregister dient im Wesentlichen zum Steuern der zusätzlichen Ausgänge des UART. Während DTR und RTS genutzt werden können, sind OUT2 und OUT1 als zusätzliche Mehrzweckausgänge beim PC nur nicht von außen zugänglich. OUT2 erfüllt aber eine wichtige Funktion beim Aktivieren von Interrupts. Diese Leitung muss nämlich gesetzt werden, um überhaupt einen Interrupt weiterzuleiten. (vgl. Abb. 3.1)
Modem-Steuerregister (Offset=4)
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
0 |
0 |
0 |
LOOP |
OUT2 |
OUT1 |
RTS |
DTR |
LOOP: Interne Rückkopplung zu Testzwecken
OUT2: Leitung OUT2 setzen, um Interrupts zu ermöglichen
OUT1: interne Leitung OUT1 setzen
RTS: Leitung RTS setzen
DTR: Leitung DTR setzen
Das LOOP-Bit kann sinnvoll eingesetzt werden, um eigene Programme zu testen. Die ausgesendeten Zeichen werden empfangen, ohne dass eine externe Verbindung zwischen TXD und RXD hergestellt werden muss.
Das Leitungs-Statusregister enthält sieben Statusbits, über die sich aktuelle Zustände des seriellen Senders und des seriellen Empfängers auslesen lassen.
Leitungs-Statusregister (Offset=5)
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
0 |
TXE |
TBE |
BREK |
FRMF |
PARF |
ÜBLF |
RXRD |
TXE: Letztes Zeichen ist vollständig ausgesendet
TBE: Sende-Halteregister ist leer
BREK: BREAK-Zustand an RXD erkannt
FRMF: Format-Fehler erkannt
RARF: Paritätsfehler erkannt
ÜBLF: Überlauffehler erkannt
RXRD: Ein Zeichen wurde empfangen
Sobald das TBE-Bit gesetzt ist, darf ein neues Zeichen in das Sende-Haltegegister geschrieben werden. Dieses Zeichen wird aber erst dann in das Schieberegister des seriellen Senders übernommen, wenn kein anderes Zeichen mehr bearbeitet werden muss. Sobald das zuletzt in das Sende-Halteregister geschriebene Zeichen in das Schieberegister übernommen wird, setzt der UART sein TBE-Bit. Das TXE-Bit wird dagegen erst dann gesetzt, wenn das letzte Zeichen vollständig übertragen wurde.
Für die Empfangsrichtung zeigt das RXRD-Bit, dass ein Zeichen zur Verfügung steht. Es muss rechtzeitig aus dem Empfangs-Halteregister ausgelesen werden, um nicht von einem folgenden Zeichen überschrieben zu werden. Im letzteren Fall würde durch das ÜBLF-Bit ein Überlauf angezeigt. Überläufe lassen sich verhindern, indem entweder durch ein genügend schnelles "Polling" das RXRD-Bit laufend beobachtet wird, oder indem eine Interruptroutine benutzt wird, die ein ankommendes Zeichen verarbeitet.
Der Zustand der Eingänge DCD, RI, DSR und CTS kann über das Modem-Statusregister gelesen werden. Während die oberen vier Bits den Zustand der vier Leitungen direkt widerspiegeln, zeigen die unteren vier Bits an, ob seit dem letzten Lesezugriff auf das Modem-Statusregister eine Zustandsänderung an einer Leitung aufgetreten ist.
Modem-Statusregister (Offset=6)
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
DCD |
RI |
DSR |
CTS |
DDCD |
DRI |
DDSR |
DCTS |
DCD: Leitung DCD ist gesetzt
RI: Leitung RI ist gesetzt
DSR: Leitung DSR ist gesetzt
CTS: Leitung CTS ist gesetzt
DDCD: Zustandsänderung an DCD
DRI: Zustandsänderung an RI
DDSR: Zustandsänderung an DSR
DCTS: Zustandsänderung an CTS
Der UART 8250 und sein Nachfolgetyp 16450 verarbeiten jeweils nur ein Zeichen. Wird ein neues Zeichen empfangen, bevor das alte ausgelesen wurde, kommt es zum Datenverlust. Um auch bei höheren Übertragungsgeschwindigkeiten andere Aufgaben parallel durchführen zu können, setzt man den neueren 16550 mit zwei 16-Byte Zwischenspeichern für die Sende- und Empfangsrichtung ein. Die zusätzlichen Speicher sind als sog. FIFO-Speicher (first in first out) ausgelegt, sodass die ausgelesenen Zeichen immer in der Reihenfolge des Empfangs erscheinen.
Der FIFO-Speicher ist nach einem Reset zunächst inaktiv und muss bei Bedarf durch das FIFO-Controllregister eingeschaltet werden. Dieses Register ist nur in Schreibrichtung unter der Offsetadresse 2 zugänglich, beim Auslesen befindet sich hier das Interrupt-Erkennungsregister, das ebenfalls eine erweiterte Funktion hat:
FIFO-Steuerregister (Offset=2, schreiben)
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
TL1 |
TL0 |
0 |
0 |
0 |
FRT |
FRR |
FEN |
TL1, TL0: Triggerlevel, Interrupt nach:
00: 1 Byte
01: 4 Bytes
10: 8 Bytes
11: 14 Bytes
FRT: FIFO-Reset für Sender
FRR: FIFO-Reset für Empfänger
FEN: FIFO einschalten
Will man ohne FIFO arbeiten und eine Kompatibilität zum 8050 erzwingen, dann sollte der Wert 6 in das Register geschrieben werden. Damit erfolgt zugleich ein Reset und ein Ausschalten des FIFO. Ob der FIFO-Modus eingeschaltet wurde, lässt sich an einem zusätzlichen Statusbit des Interrupt-Erkennungsregisters ablesen.
Interrupt-Erkennungsregister (Offset=2, lesen)
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
FON |
FON2 |
0 |
0 |
ID2 |
ID1 |
ID0 |
PND |
FON: FIFO eingeschaltet
FON2: FIFO eingeschaltet (nur 16550A)
ID2: Identifizierungsbit: Timeout
ID1, ID0: Identifizierungsbits:
00: Zustansänderung einer Handshake-Leitung
01: Sende-Haltepuffer ist leer
10: Ein Zeichen wurde empfangen
11: Fehler oder BREAK-Bedingung erkannt
PND: Interrupt ist aufgetreten