Serial Ports: Difference between revisions

m
Bot: Replace main article with main template
[unchecked revision][unchecked revision]
m (Nuke repeated newlines)
m (Bot: Replace main article with main template)
 
(26 intermediate revisions by 18 users not shown)
Line 1:
Serial ports are a legacy communications port common on IBM-PC compatible computers. Use of serial ports for connecting peripherals has largely been deprecated in favor of [[USB]] and other modern peripheral interfaces, however it is still commonly used in certain industries for interfacing with industrial hardware such as CNC machines or commercial devices such as POS terminals. Historically it was common for many dial-up modems to be connected via a computer's serial port, and the design of the underlying [[UART]] hardware itself reflects this.
Serial ports are a legacy communications port which has pretty much been succeeded by [[USB]] and other modern communications technology. However, it is much easier to program than USB, and it is still found in a lot of computers (especially older ones such as the ones the financially limited amateur OS writer might use for [[Testing|testing]]). Also, a lot of phone modems (which are still used to access the Internet quite often, even though broadband is very accessible and affordable) connect via the serial interface. Furthermore it is possible to use the serial ports for debbuging, since a lot of emulators allow the redirection of their output into a file.
 
Serial ports are typically controlled by [[UART]] hardware. This is the hardware chip responsible for encoding and decoding the data sent over the serial interface. Modern serial ports typically implement the [[wikipedia:RS-232|RS-232]] standard, and can use a variety of different connector interfaces. The DE-9 interface is the one most commonly used connector for serial ports in modern systems.
 
Serial ports are of particular interest to operating-system developers since they are much easier to implement drivers for than USB, and are still commonly found in many x86 systems. It is common for operating-system developers to use a system's serial ports for debugging purposes, since they do not require sophisticated hardware setups and are useful for transmitting information in the early stages of an operating-system's initialization. Many emulators such as [[QEMU]] and [[Bochs]] allow the redirection of serial output to either stdio or a file on the host computer.
 
== Wires, Pins, Connectors and the like ==
 
The Wikipedia page on [[wikipedia:Serial_port|Serial ports]] has a lot of information, and it is summarised here. The serial interface is very simple. There are actually two kinds of serial port: 25-pin and 9-pin. 25-pin ports are not any better, they just have more pins (most unused) and are bigger. 9-pin is smaller and is used more often though in the past the 25-pin ones were used more often. The 9-pin ones are called DE-9 (or more commonly, DB-9 even though DE-9 is it'sits technical name) and the 25-pin ones are called DB-25. They plug in to your computer using a female plug (unless your computer is odd and has a female port, in which case your cable will need a male plug). [[wikipedia:D-subminiature|This Wikipedia page]] has more information on the plug used.
 
Both have the same basic types of pins. A DB-25 has most of the pins as ground pins or simply unconnected, whereas a DE-9 has only a fewone ground pinspin. There is a transmitting pin (for sending information away) and a receiving pin (for getting information). SomeMost serial ports canrun havein a duplex mode--that is, they can send and receive simultaneously. There are a few other pins, used for hardware handshaking. In the past, there was no duplex mode, so if a computer wanted to send something it had to tell the other device or computer that it was about to transmit, using one of the hardware handshaking pins. The other thingdevice would then use another handshaking pin to tell it to send whatever it wanted to send. Today there is duplex mode, but the handshaking pins are still used.
 
If you want to connect two computers, you need two things in your cable:
 
# The cable needs to have two female plugs so it can plug into both computers.
# The cable needs to have it'sits transmit-receive wires and it's handshaking wires switched. This can be done in the cable itself, or as an extension called a [[Null Modem]]
 
For serial devices, you don't need to setup the cable this way. The receiving end of the device has the wires switched and it has a female port, which means you can plug a male plug into it.
Line 17 ⟶ 21:
During the early stages of kernel development, you might wonder why you would bother writing a serial driver. There are several reasons why you might:
;[[GDB]] debugging
:You can use the serial port to connect to a host computer, and use the GDB debugger to debug your operating system. This involves writing a stub for GDB within your OS. Helpful information might be found at http://developer.apple.com/documentation/DeveloperTools/gdb/gdb/gdb_18.html.
;Headless console
:You can operate the computer without a monitor, keyboard or mouse and instead use the serial port as a console using a protocol such as TTY or VT100.
Line 28 ⟶ 32:
If you want to use the serial port for communications, you first have to initialize it. You tell it how fast your connection speed between the other computer or device will be (this is called the baud rate)--you must have the same speed as the other device or computer is setup to use, or you will have problems. It is probably safer to use the slower speeds unless you need the faster speeds for some reason, for example if you are playing a multi-player game over a serial connection. You also need to setup the parity type and the number of bits in a character. Once again, your computer must be setup with the same values for these things as the other computer or device has, or communication will not work.
 
Once you have setup these things, you still need to setup the interrupt handlers. You can poll the port to see if any new things have arrived, or if it's time to send another character, but this slows things down and will not work very well in most real-time applications or multithreadedmulti-threaded environments. In the case of a game, this is not a good idea at all.
 
You use IRQ #4 for COM ports 1 or 3, and IRQ #3 for COM ports 2 or 4 (you can tell which port sent the interrupt when you receive the interrupt). The IRQ handlers check if you are receiving something, and if so they receive the character and handle it somehow, such as placing it into a buffer. They also check if the other side is ready to receive something from you, and if you have something to send, it is sent.
Line 35 ⟶ 39:
The addresses for COM ports can vary depending on how they are connected to the machine and how the BIOS is configured. Some BIOS configuration utilities allow you to see and set what these are, so if you in doubt for a test machine, this might be a good place to look to get you started.
 
For the most part, the first two COM ports will be at the addresses specified, the addresses for further COM ports isare less reliable.
{| {{wikitable}}
! COM Port
! IO Port
|-
| COM1 || 3F8h0x3F8
|-
| COM2 || 2F8h0x2F8
|-
| COM3 || 3E8h0x3E8
|-
| COM4 || 2E8h0x2E8
|-
| COM5 || 0x5F8
|-
| COM6 || 0x4F8
|-
| COM7 || 0x5E8
|-
| COM8 || 0x4E8
|}
 
You might be able to find the IO port addresses of the COM ports in the [[Memory Map (x86)#BIOS_Data_Area_.28BDA.29|BIOS Data Area]]; however be warned that this won't work on modern/UEFI systems, can tell you about serial ports that only exist in the chipset (and lack any kind of connector that anything can be plugged into), won't tell you about any additional serial ports (e.g. on expansion cards, etc) that firmware doesn't/can't know about, and will make your OS susceptible to "BIOS quirks/bugs". Because the serial ports have relatively standard IO ports it's far more effective to use manual probing techniques instead; specifically, see if the scratch pad register can store a value, then try the loopback test (that you should use to determine if the serial port is faulty anyway).
You should be able to find the IO port addresses of the COM ports in the [[Memory Map (x86)#BIOS_Data_Area_.28BDA.29|BIOS Data Area]]
 
Once you have the base address of your COM port, you add an offset value to get to one of the data registers. One of the registers hold what is termed the DLAB or Divisor Latch Access Bit. When this bit is set, offsets 0 and 1 are mapped to the low and high bytes of the Divisor register for setting the baud rate of the port. When this bit is clear, offsets 0 and 1 are mapped to their normal registers. The DLAB bit only affects port offsets 0 and 1, the other offsets ignore this setting.
Line 56 ⟶ 68:
! IO Port Offset
! Setting of DLAB
! I/O Access
! Register mapped to this port
|-
| +0 || 0 || DataRead register. Reading this registers read from the|| Receive buffer. Writing to this register writes to the Transmit buffer.
|-
| +10 || 0 || InterruptWrite || EnableTransmit Registerbuffer.
|-
| +1 || 0 || Read/Write || Interrupt Enable Register.
| +0 || 1 || With DLAB set to 1, this is the least significant byte of the divisor value for setting the baud rate.
|-
| +10 || 1 || Read/Write || With DLAB set to 1, this is the mostleast significant byte of the divisor value for setting the baud rate.
|-
| +1 || 1 || Read/Write || With DLAB set to 1, this is the most significant byte of the divisor value.
| +2 || - || Interrupt Identification and FIFO control registers
|-
| +2 || - || Read || Interrupt Identification
| +3 || - || Line Control Register. The most significant bit of this register is the DLAB.
|-
| +42 || - || ModemWrite Control|| FIFO control Register.registers
|-
| +53 || - || Read/Write || Line StatusControl Register. The most significant bit of this register is the DLAB.
|-
| +64 || - || Read/Write || Modem StatusControl Register.
|-
| +75 || - || ScratchRead || Line Status Register.
|-
| +6 || - || Read || Modem Status Register.
|-
| +7 || - || Read/Write || Scratch Register.
|}
 
Line 96 ⟶ 113:
# Send the most significant byte of the divisor value to [PORT + 1].
# Clear the most significant bit of the Line Control Register.
 
====Line Control Register====
The Line Control register sets the general connection parameters.
 
{| class="wikitable"
|-
! Bit 7
! Bit 6
! Bits 5-3
! Bit 2
! Bits 1-0
|-
| Divisor Latch Access Bit
| Break Enable Bit
| Parity Bits
| Stop Bits
| Data Bits
|}
 
====Data Bits====
Line 115 ⟶ 150:
|}
 
====Stop bitsBits====
The serial controller can be configured to send a number of bits after each character of data. These reliable bits can be used to by the controller to verify that the sending and receiving devices are in phase.
 
Line 131 ⟶ 166:
|}
 
====Parity Bits====
The controller can be made to add or expect a parity bit at the end of each character of data transmitted. With this parity bit, if a single bit of data is inverted by interference, a parity error can be raised. The parity type can be NONE, EVEN, ODD, MARK or SPACE.
 
Line 141 ⟶ 176:
 
To set the port parity, set bits 3, 4 and 5 of the Line Control Register [PORT + 3].
 
{| {{wikitable}}
! Bit 5
Line 162 ⟶ 196:
To communicate with a serial port in interrupt mode, the interrupt-enable-register (see table above) must be set correctly. To determine which interrupts should be enabled, a value with the following bits (0 = disabled, 1 = enabled) must be written to the interrupt-enable-register:
{| {{wikitable}}
! Bit 7-4
! Bit 3
! Bit 2
! Bit 1
! Bit 0
|-
| Reserved
| Modem Status
| Receiver Line Status
| Transmitter Holding Register Empty
| Received Data Available
|}
 
===First In First Out Control Register===
The First In / First Out Control Register (FCR) is for controlling the FIFO buffers. Access this register by writing to port offset +2.
{| class="wikitable"
|-
! Bits 7-6
! Bits 5-4
! Bit 3
! Bit 2
! Bit 1
! Bit 0
|-
| Interrupt Trigger Level
| Reserved
| DMA Mode Select
| Clear Transmit FIFO
| Clear Receive FIFO
| Enable FIFO's
|}
 
====Clear Transmit FIFO and Clear Receive FIFO====
Bit 2 being set clears the Transmit FIFO buffer while Bit 1 being set clears the Receive FIFO buffer. Both bits will set themselves back to 0 after they are done being cleared.
 
====Interrupt Trigger Level====
The Interrupt Trigger Level is used to configure how much data must be received in the FIFO Receive buffer before triggering a Received Data Available Interrupt.
{| {{wikitable}}
! Bit 7
! Bit 6
! Trigger Level
|-
| 0 || 0 || 1 Byte
|-
| 0 || 1 || 4 Bytes
|-
| 1 || 0 || 8 Bytes
|-
| 1 || 1 || 14 Bytes
|}
 
===Interrupt Identification Register===
The Interrupt Identification Register (IIR) is for identifying pending interrupts. Access this register by reading from port offset +2.
{| class="wikitable"
|-
! Bits 7-6
! Bits 5-4
! Bit 3
! Bit 2-1
! Bit 0
|-
| FIFO Buffer State
| Reserved
| Timeout Interrupt Pending (UART 16550) or Reserved
| Interrupt State
| Interrupt Pending
|}
 
====Interrupt State====
After Interrupt Pending is set, the Interrupt State shows the interrupt that has occurred. They have varying levels of priority, with high-value interrupts handled first, and low-value interrupts being handled last.
{| {{wikitable}}
! Bit 2
! Bit 1
! Interrupt
! Priority
|-
| 0 || 0 || Modem Status || 4 (Lowest)
| 0 || Data available
|-
| 0 || 1 || Transmitter emptyHolding Register Empty || 3
|-
| 1 || 0 || Received Data Available || 2
| 2 || Break/error
|-
| 31 || 1 || Receiver Line Status change|| 1 (Highest)
|}
 
====FIFO Buffer State====
{| {{wikitable}}
! Bit 7
! Bit 6
! State
|-
| 0 || 0 || No FIFO
|-
| 0 || 1 || FIFO Enabled but Unusable
|-
| 4-71 || Unused0 || FIFO Enabled
|}
 
===Modem Control Register===
The Modem Control Register is one half of the hardware handshaking registers.
While most serial devices no longer use hardware handshaking, The lines are still included in all 16550 compatible UARTS.
These can be used as general purpose output ports, or to actually perform handshaking.
By writing to the Modem Control Register, it will set those lines active.
{| {{wikitable}}
! Bit
! Name
! Meaning
|-
| 0 || Data Terminal Ready (DTR) || Controls the Data Terminal Ready Pin
|-
| 1 || Request to Send (RTS) || Controls the Request to Send Pin
|-
| 2 || Out 1 || Controls a hardware pin (OUT1) which is unused in PC implementations
|-
| 3 || Out 2 || Controls a hardware pin (OUT2) which is used to enable the IRQ in PC implementations
|-
| 4 || Loop || Provides a local loopback feature for diagnostic testing of the UART
|-
| 5 || 0 || Unused
|-
| 6 || 0 || Unused
|-
| 7 || 0 || Unused
|}
 
Most PC serial ports use OUT2 to control a circuit that disconnects (tristates) the IRQ line. This makes it possible for multiple serial ports to share a single IRQ line, as long as only one port is enabled at a time.
Loopback mode is a diagnostic feature. When bit 4 is set to logic 1,
the following occur the transmitter Serial Output (SOUT) is set to the Marking (logic 1) state; the receiver Serial
Input (SIN) is disconnected; the output of the Transmitter Shift Register is ‘‘looped back’’ into the Receiver Shift
Register input; the four MODEM Control inputs (DSR, CTS, RI, and DCD) are disconnected; and the four
MODEM Control outputs (DTR, RTS, OUT 1, and OUT 2) are internally connected to the four MODEM Control
inputs, and the MODEM Control output pins are forced to their inactive state (high). In the loopback mode, data
that is transmitted is immediately received. This feature allows the processor to verify the transmit-and received-
data paths of the UART. In the loopback mode, the receiver and transmitter interrupts are fully operational. Their sources are external to
the part. The MODEM Control Interrupts are also operational, but the interrupts’ sources are now the lower four
bits of the MODEM Control Register instead of the four MODEM Control inputs. The interrupts are still controlled
by the Interrupt Enable Register.
 
===Line Status Register===
The line status register is useful to check for errors and enable polling.
{| {{wikitable}}
! Bit
! Name
! Meaning
|-
| 0 || Data ready (DR) || Set if there is data that can be read
|-
| 1 || Overrun error (OE) || Set if there has been data lost
|-
| 2 || Parity error (PE) || Set if there was an error in the transmission as detected by parity
|-
| 3 || Framing error (FE) || Set if a stop bit was missing
|-
| 4 || Break indicator (BI) || Set if there is a break in data input
|-
| 5 || Transmitter holding register empty (THRE) || Set if the transmission buffer is empty (i.e. data can be sent)
|-
| 6 || Transmitter empty (TEMT) || Set if the transmitter is not doing anything
|-
| 7 || Impending Error || Set if there is an error with a word in the input buffer
|}
 
===Modem Status Register===
This register provides the current state of the control lines from a peripheral device.
In addition to this current-state information, four bits of the MODEM Status Register provide change information.
These bits are set to a logic 1 whenever a control input from the MODEM changes state. They are reset to logic
0 whenever the CPU reads the MODEM Status Register
{| {{wikitable}}
! Bit
! Name
! Meaning
|-
| 0 || Delta Clear to Send (DCTS) || Indicates that CTS input has changed state since the last time it was read
|-
| 1 || Delta Data Set Ready (DDSR) || Indicates that DSR input has changed state since the last time it was read
|-
| 2 || Trailing Edge of Ring Indicator (TERI) || Indicates that RI input to the chip has changed from a low to a high state
|-
| 3 || Delta Data Carrier Detect (DDCD) || Indicates that DCD input has changed state since the last time it ware read
|-
| 4 || Clear to Send (CTS) || Inverted CTS Signal
|-
| 5 || Data Set Ready (DSR) || Inverted DSR Signal
|-
| 6 || Ring Indicator (RI) || Inverted RI Signal
|-
| 7 || Data Carrier Detect (DCD) || Inverted DCD Signal
|}
 
If Bit 4 of the MCR (LOOP bit) is set, the upper 4 bits will mirror the 4 status output lines set in the Modem Control Register.
 
===Terminals===
{{Main|Terminals}}
 
Once you can send and receive bytes with confidence, you probably want to connect the serial port to a terminal (or more likely a terminal emulator these days). Those send specific byte sequences when a key is pressed, and can interpret codes to move the cursor on the screen and change color for example.
 
 
==Example Code==
===Initialization===
<sourcesyntaxhighlight lang="C">
#define PORT 0x3f8 /* COM1 * // COM1
 
voidstatic int init_serial() {
outb(PORT + 1, 0x00); // Disable all interrupts
outb(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
Line 189 ⟶ 406:
outb(PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
outb(PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
outb(PORT + 4, 0x1E); // Set in loopback mode, test the serial chip
outb(PORT + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte)
 
// Check if serial is faulty (i.e: not same byte as sent)
if(inb(PORT + 0) != 0xAE) {
return 1;
}
 
// If serial is not faulty set it in normal operation mode
// (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled)
outb(PORT + 4, 0x0F);
return 0;
}
</syntaxhighlight>
</source>
 
Notice that the initialization code above writes to [PORT + 1] twice with different values. This is once to write to the Divisor register along with [PORT + 0] and once to write to the Interrupt register as detailed in the previous section. The second write to the Line Control register [PORT + 3] clears the DLAB again as well as setting various other bits.
 
===Receiving data===
<sourcesyntaxhighlight lang="C">
int serial_received() {
return inb(PORT + 5) & 1;
Line 205 ⟶ 434:
return inb(PORT);
}
</syntaxhighlight>
</source>
 
===Sending data===
 
<sourcesyntaxhighlight lang="C">
int is_transmit_empty() {
return inb(PORT + 5) & 0x20;
Line 219 ⟶ 448:
outb(PORT,a);
}
</syntaxhighlight>
</source>
 
==Glossary==
 
;Baud Rate
:is theThe speed at which the serial line switches between it's two states. This is not equivalent to bps, due to the fact there are start and stop bits. On an 8/N/1 line, 10 baud = 1 byte. Modems are more complex than plain serial lines due to having multiple waveforms, but for the purposes of OSDev this is irrelevant.
:The fastest baud rate a serial port can reliably run at is generally 115200 baud.
;baud rate divisor
;Baud Rate Divisor
:fastest rate a serial port can run, the number 115200.
:The value the is used by the UART to divide its internal clock by in order to get the actual intended baud rate.
;stop bits
;Stop Bits
:the NULL bit(s) sent between each character to synchronize the transmitter and the receiver.
:The NULL bit(s) sent between each character to synchronize the transmitter and the receiver.
;UART
:forFor Universal Asynchronous Receiver/Transceiver: the chip that picks a byte a send it bit per bit on the serial line and vice versa.
 
== Related Links ==
Line 239 ⟶ 469:
[[Category:Network Hardware]]
[[Category:Common Devices]]
[[de:Serielle_Schnittstelle]]
40

edits