| Zeile 1: | Zeile 1: | ||
| − | Die serielle Schnittstelle (Abkürzung: COM) scheint veraltet ist aber sehr nützlich um z.B den Kernel im frühen Entwicklungstand zu debuggen, denn der Bildschirm kann nur begrenzt Text anzeigen und | + | Die serielle Schnittstelle (Abkürzung: COM) scheint veraltet ist aber sehr nützlich um z.B. den [[Kernel]] im frühen Entwicklungstand zu [[Debugging | debuggen]], denn der Bildschirm kann nur begrenzt Text anzeigen und in keinem der Emulatoren kann man mittels Copy'n'Paste den Bildschirminhalt kopieren. Außerdem sollte bei keinem Betriebssystem ein Treiber für die serielle Schnittstelle fehlen, da sie heute auch noch oft Anwendung findet und leicht zu programmieren ist. |
| − | == | + | ==Basis-I/O-Ports finden== |
| − | Normalerweise haben die COM-Ports folgende | + | Normalerweise haben die COM-Ports folgende Basis-I/O-Ports: |
{| border="1" | {| border="1" | ||
| − | ! Name || | + | ! Name || I/O-Port || IRQ |
|- | |- | ||
| COM1 || 0x3F8 || 4 | | COM1 || 0x3F8 || 4 | ||
| Zeile 14: | Zeile 14: | ||
| COM4 || 0x2E8 || 3 | | COM4 || 0x2E8 || 3 | ||
|} | |} | ||
| − | Man | + | Man sollte die Basis-I/O-Ports aber aus der [[BIOS Data Area]] auslesen. |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | == | + | ==Offsets der einzelnen Register== |
| − | Da ein COM-Port mehrere Register benutzt, braucht er auch mehrere | + | Da ein COM-Port mehrere Register benutzt, braucht er auch mehrere I/O-Ports. Die oben angegebenen I/O-Ports sind nur die Basis-I/O-Ports. Man muss also nachher noch das Offset der einzelnen Register addieren. Folgende Register verbergen sich hinter den Offsets: |
{| border="1" | {| border="1" | ||
| Zeile 57: | Zeile 46: | ||
===Baudrate einstellen=== | ===Baudrate einstellen=== | ||
| − | Um die Baudrate einzustellen muss man erstmal das DLAB-Bit setzen, es ist eine Art Umschaltbit um 12 Register über 8 | + | Um die Baudrate einzustellen muss man erstmal das DLAB-Bit setzen, es ist eine Art Umschaltbit um 12 Register über 8 I/O-Port-Adressen zu benutzen. Dafür muss man im LineControl-Register das 8. Bit setzen. |
| − | Die Baudrate wird aber nicht direkt gespeichert. Es wird immer nur ein Teiler gespeichert. Diesen kann man | + | Die Baudrate wird aber nicht direkt gespeichert. Es wird immer nur ein Teiler gespeichert. Diesen kann man wie folgt berechnen:\ |
''t = 115200/b''\ | ''t = 115200/b''\ | ||
Wobei ''t'' der Teiler und ''b'' die Baudrate ist. | Wobei ''t'' der Teiler und ''b'' die Baudrate ist. | ||
| Zeile 87: | Zeile 76: | ||
Die Bytelänge bestimmt wie viel Bits ein Byte ergeben. Heutzutage werden eigentlich immer 8 Bits zu einem Byte zusammengefasst. Ein Byte kann 5 bis 8 Bits haben. Zum Setzen der Anzahl wird das 1. und 2. Byte im LineControl-Register benutzt. 00b entspricht 5 Bits 01b 6 usw. Also einfach die Anzahl an Bits minus 5 und in einen zwei-Bit-Wert wandeln. | Die Bytelänge bestimmt wie viel Bits ein Byte ergeben. Heutzutage werden eigentlich immer 8 Bits zu einem Byte zusammengefasst. Ein Byte kann 5 bis 8 Bits haben. Zum Setzen der Anzahl wird das 1. und 2. Byte im LineControl-Register benutzt. 00b entspricht 5 Bits 01b 6 usw. Also einfach die Anzahl an Bits minus 5 und in einen zwei-Bit-Wert wandeln. | ||
| − | ===Anzahl | + | ===Anzahl Stoppbits setzen=== |
| − | Die Anzahl an | + | Die Anzahl an Stoppbits wird im 3 Bit des LineControl-Registers gesetzt. 0b entspricht einem Stoppbit und 1b zwei Stoppbits (für Bytes mit 5 Bits 1.5 Stoppbits). |
===Senden=== | ===Senden=== | ||
| Zeile 108: | Zeile 97: | ||
#define LSR 5 | #define LSR 5 | ||
#define MSR 6 | #define MSR 6 | ||
| − | |||
| − | |||
| − | |||
// Funktion zum initialisieren eines COM-Ports | // Funktion zum initialisieren eines COM-Ports | ||
| − | void init_com( | + | void init_com(uint16_t base, uint32_t baud, uint8_t parity, uint8_t bits) { |
// Teiler berechnen | // Teiler berechnen | ||
union { | union { | ||
| − | + | uint8_t b[2]; | |
| − | + | uint16_t w; | |
} divisor; | } divisor; | ||
divisor.w = 115200/baud;; | divisor.w = 115200/baud;; | ||
| Zeile 123: | Zeile 109: | ||
// Interrupt ausschalten | // Interrupt ausschalten | ||
outb(base+IER,0x00); | outb(base+IER,0x00); | ||
| + | |||
// DLAB-Bit setzen | // DLAB-Bit setzen | ||
outb(base+LCR,0x80); | outb(base+LCR,0x80); | ||
| + | |||
// Teiler (low) setzen | // Teiler (low) setzen | ||
outb(base+0,divisor.b[0]); | outb(base+0,divisor.b[0]); | ||
| + | |||
// Teiler (high) setzen | // Teiler (high) setzen | ||
outb(base+1,divisor.b[1]); | outb(base+1,divisor.b[1]); | ||
| + | |||
// Anzahl Bits, Parität, usw setzen (DLAB zurücksetzen) | // Anzahl Bits, Parität, usw setzen (DLAB zurücksetzen) | ||
outb(base+LCR,((parity&0x7)<<3)|((bits-5)&0x3)); | outb(base+LCR,((parity&0x7)<<3)|((bits-5)&0x3)); | ||
| + | |||
// Initialisierung abschließen | // Initialisierung abschließen | ||
outb(base+FCR,0xC7); | outb(base+FCR,0xC7); | ||
outb(base+MCR,0x0B); | outb(base+MCR,0x0B); | ||
} | } | ||
| + | |||
// Ob man schreiben kann | // Ob man schreiben kann | ||
| − | + | uint8_t is_transmit_empty(u16 base) { | |
return inb(base+LSR)&0x20; | return inb(base+LSR)&0x20; | ||
} | } | ||
| + | |||
// Byte senden | // Byte senden | ||
| − | void write_com( | + | void write_com(uint16_t base, uint8_t chr) { |
while (is_transmit_empty(base)==0); | while (is_transmit_empty(base)==0); | ||
outb(base,chr); | outb(base,chr); | ||
} | } | ||
| + | |||
// Ob man lesen kann | // Ob man lesen kann | ||
| − | + | uint8_t serial_received(uint16_t base) { | |
return inb(base+LSR)&1; | return inb(base+LSR)&1; | ||
} | } | ||
| + | |||
// Byte empfangen | // Byte empfangen | ||
| − | + | uint8_t read_serial(uint16_t base) { | |
while (!serial_received(base)); | while (!serial_received(base)); | ||
return inb(base); | return inb(base); | ||