Serial Ports: Difference between revisions

Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content deleted Content added
Kenny (talk | contribs)
Kenny (talk | contribs)
Line 32: Line 32:
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.
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.


===Initialization===
===Port Addresses===
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.
The baud rate is set by writing a divisor value to the UART. The divisor is the fraction of 115200 that makes the baud rate. For instance, the baud rate 57600 would have a divisor of 2.


For the most part, the first two COM ports will be at the addresses specified, the addresses for further COM ports is less reliable.
The number of bits in a character is variable. Having fewer bits is, of course, faster, but they store less information. If you are only sending ASCII text, you probably only need 7 bits. These days you could consider 8N1 (8 bits, no parity, one stop bit) pretty much the default.
{| class="wikitable" border="1"
|-
! COM Port
! IO Port
|-
| COM1
| 3F8h
|-
| COM2
| 2F8h
|-
| COM3
| 3E8h
|-
| COM4
| 2E8h
|}


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.
The parity type can be even, odd, or none.


{| class="wikitable" border="1"
|-
! IO Port Offset
! Setting of DLAB
! Register mapped to this port
|-
| +0
| 0
| Data register. Reading this registers read from the Receive buffer. Writing to this register writes to the Transmit buffer.
|-
| +1
| 0
| 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.
|-
| +1
| 1
| With DLAB set to 1, this is the most significant byte of the divisor value.
|-
| +2
| -
| Interrupt Identification and FIFO control registers
|-
| +3
| -
| Line Control Register. The most significant bit of this register is the DLAB.
|-
| +4
| -
| Modem Control Register.
|-
| +5
| -
| Line Status Register.
|-
| +6
| -
| Modem Status Register.
|-
| +7
| -
| Scratch Register.
|}

===Line Protocol===
The serial data transmitted across the wire can have a number of different parameters set. As a rule, the sending device and the receiving device require the same protocol parameter values written to each serial controller in order for communication to be successful.

These days you could consider 8N1 (8 bits, no parity, one stop bit) pretty much the default.

====Baud Rate====
The serial controller (UART) has an internal clock which runs at 115200 ticks per second and a clock divisor which is used to control the baud rate. This is exactly the same type of system used by the Programmable Interrupt Timer (PIT).

In order to set the speed of the port, calculate the divisor required for the given baud rate and program that in to the divisor register. For example, a divisor of 1 will give a of 115200 baud, a divisor of 2 will give 57600 baud, 3 will give 38400 baud, etc.

To set the divisor to the controller:
# Set the most significant bit of the Line Control Register. This is the DLAB bit, and allows access to the divisor registers.
# Send the least significant byte of the divisor value to [PORT + 0].
# Send the most significant byte of the divisor value to [PORT + 1].
# Clear the most significant bit of the Line Control Register.

====Data Bits====
The number of bits in a character is variable. Having fewer bits is, of course, faster, but they store less information. If you are only sending ASCII text, you probably only need 7 bits.

Set this value by writing to the two least significant bits of the Line Control Register [PORT + 3].
{| class="wikitable" border="1"
|-
! Bit 1
! Bit 0
! Character Length (bits)
|-
| 0
| 0
| 5
|-
| 0
| 1
| 6
|-
| 1
| 0
| 7
|-
| 1
| 1
| 8
|}

====Stop bits====
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.

The stops bits can be set to 1, 1.5 or 2. 1.5 stop bits is used instead of 2 if and only if the word length is 5 bits.

To set the number of stop bits, set bit 2 of the Line Control Register [PORT + 3].

{| class="wikitable" border="1"
|-
! Bit 2
! Stop bits
|-
| 0
| 1
|-
| 1
| 1.5 / 2
|}

====Parity====
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.

If parity is set to NONE, no parity bit will be added and none will be expected. If one is sent by the transmitter and not expected by the receiver, it will likely cause an error.

If the parity is MARK or SPACE, the parity bit will be expected to be always set to 1 or 0 respectively.

If the parity is set to EVEN or ODD, the controller calculates the accuracy of the parity by adding together the values of all the data bits and the parity bit. If the port is set to have EVEN parity, the result must be even. If it is set to have ODD parity, the result must be odd.

To set the port parity, set bits 3, 4 and 5 of the Line Control Register [PORT + 3].

{| class="wikitable" border="1"
|-
! Bit 5
! Bit 4
! Bit 3
! Parity
|-
| -
| -
| 0
| NONE
|-
| 0
| 0
| 1
| ODD
|-
| 0
| 1
| 1
| EVEN
|-
| 1
| 0
| 1
| MARK
|-
| 1
| 1
| 1
| SPACE
|}


==Example Code==
===Initialization===
<pre>
<pre>
#define PORT 0x3f8 /* COM1 */
#define PORT 0x3f8 /* COM1 */
Line 53: Line 226:
</pre>
</pre>


Notice that the initialization code above writes to ports [PORT + 0] and [PORT + 1] twice with different values. The reason for this is because these two ports have different meanings based on the setting of the DLAB (Divisor Latch Access Bit), the most significant bit in [PORT + 3]. When this bit is set, the serial controller maps [PORT + 0] and [PORT + 1] onto the low and high bytes of the Baud Rate Divisor register, and when it is clear they are mapped onto their normal registers.
Notice that the initialization code above writes to [PORT + 1] twice with different values. This 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.

In the code above, the second write to [PORT + 3] clears the DLAB again as well as setting various other bits.


===Receiving data===
===Receiving data===