📄 interfacing the serial - rs-232 port part 3-4.htm
字号:
outportb(0x20,0x20);
}
</PRE></FONT>
<P>From the example above we check to see if there is a character to
receive and if their is we remove it from the UART and place it in a
buffer contained in memory. We keep on checking the UART, in case FIFO's
are enabled, so we can get all data available at the time of interrupt.
</P>
<P>The last line contains the instruction <FONT
color=#400080><TT>outportb(0x20,0x20);</TT></FONT> which tells the
Programmable Interrupt Controller that the interrupt has finished. The
Programmable Interrupt Controller (PIC) is what we must go into now. All
of the routines above, we have assumed that everything is set up ready
to go. That is all the UART's registers are set correctly and that the
Programmable Interrupt Controller is set. </P>
<P>The Programmable Interrupt Controller handles hardware interrupts.
Most PC's will have two of them located at different addresses. One
handles IRQ's 0 to 7 and the other IRQ's 8 to 15. Mainly Serial
communications interrupts reside on IRQ's under 7, thus PIC1 is used,
which is located at 0020 Hex. </P>
<CENTER>
<TABLE border=1 width="80%">
<TBODY>
<TR>
<TD>
<CENTER><B>Bit</B></CENTER></TD>
<TD>
<CENTER><B>Disable IRQ</B></CENTER></TD>
<TD>
<CENTER><B>Function</B></CENTER></TD></TR>
<TR>
<TD>
<CENTER>7</CENTER></TD>
<TD>
<CENTER>IRQ7</CENTER></TD>
<TD>Parallel Port</TD></TR>
<TR>
<TD>
<CENTER>6</CENTER></TD>
<TD>
<CENTER>IRQ6</CENTER></TD>
<TD>Floppy Disk Controller</TD></TR>
<TR>
<TD>
<CENTER>5</CENTER></TD>
<TD>
<CENTER>IRQ5</CENTER></TD>
<TD>Reserved/Sound Card</TD></TR>
<TR>
<TD>
<CENTER>4</CENTER></TD>
<TD>
<CENTER>IRQ4</CENTER></TD>
<TD>Serial Port</TD></TR>
<TR>
<TD>
<CENTER>3</CENTER></TD>
<TD>
<CENTER>IRQ3</CENTER></TD>
<TD>Serial Port</TD></TR>
<TR>
<TD>
<CENTER>2</CENTER></TD>
<TD>
<CENTER>IRQ2</CENTER></TD>
<TD>PIC2</TD></TR>
<TR>
<TD>
<CENTER>1</CENTER></TD>
<TD>
<CENTER>IRQ1</CENTER></TD>
<TD>Keyboard</TD></TR>
<TR>
<TD>
<CENTER>0</CENTER></TD>
<TD>
<CENTER>IRQ0</CENTER></TD>
<TD>System Timer</TD></TR></TBODY></TABLE><FONT size=-1>Table 15 : PIC1
Control Word (0x21)</FONT> </CENTER>
<P>Multi-Comm ports are getting quite common, thus table 16 includes
data for PIC2 which is located at 0xA0. PIC2 is responsible for IRQ's 8
to 15. It operates in exactly the same way than PIC1 except that EOI's
(End of Interrupt) goes to port 0xA0 while the disabling (Masking) of
IRQ's are done using port 0xA1. </P>
<CENTER>
<TABLE border=1 width="80%">
<TBODY>
<TR>
<TD>
<CENTER><B>Bit</B></CENTER></TD>
<TD>
<CENTER><B>Disable IRQ</B></CENTER></TD>
<TD>
<CENTER><B>Function</B></CENTER></TD></TR>
<TR>
<TD>
<CENTER>7</CENTER></TD>
<TD>
<CENTER>IRQ15</CENTER></TD>
<TD>Reserved</TD></TR>
<TR>
<TD>
<CENTER>6</CENTER></TD>
<TD>
<CENTER>IRQ14</CENTER></TD>
<TD>Hard Disk Drive</TD></TR>
<TR>
<TD>
<CENTER>5</CENTER></TD>
<TD>
<CENTER>IRQ13</CENTER></TD>
<TD>Maths Co-Processor</TD></TR>
<TR>
<TD>
<CENTER>4</CENTER></TD>
<TD>
<CENTER>IRQ12</CENTER></TD>
<TD>PS/2 Mouse</TD></TR>
<TR>
<TD>
<CENTER>3</CENTER></TD>
<TD>
<CENTER>IRQ11</CENTER></TD>
<TD>Reserved</TD></TR>
<TR>
<TD>
<CENTER>2</CENTER></TD>
<TD>
<CENTER>IRQ10</CENTER></TD>
<TD>Reserved</TD></TR>
<TR>
<TD>
<CENTER>1</CENTER></TD>
<TD>
<CENTER>IRQ9</CENTER></TD>
<TD>IRQ2</TD></TR>
<TR>
<TD>
<CENTER>0</CENTER></TD>
<TD>
<CENTER>IRQ8</CENTER></TD>
<TD>Real Time Clock</TD></TR></TBODY></TABLE><FONT size=-1>Table 16 :
PIC2 Control Word (0xA1) </FONT></CENTER>
<P>Most of the PIC's initiation is done by BIOS. All we have to worry
about is two instructions. The first one is <FONT
color=#400080><TT>outportb(0x21,(inportb(0x21) & 0xEF);</TT></FONT>
which selects which interrupts we want to Disable (Mask). So if we want
to enable IRQ4 we would have to take 0x10 (16) from 0xFF (255) to come
up with 0xEF (239). That means we want to disable IRQ's 7,6,5,3,2,1 and
0, thus enabling IRQ 4. </P>
<P>But what happens if one of these IRQs are already enabled and then we
come along and disable it? Therefore we input the value of the register
and using the & function output the byte back to the register with
our changes using the instruction <FONT
color=#400080><TT>outportb(0x21,(inportb(0x21) & 0xEF);</TT></FONT>.
For example if IRQ5 is already enabled before we come along, it will
enable both IRQ4 and IRQ5 so we don't make any changes which may affect
other programs or TSR's. </P>
<P>The other instruction is <FONT
color=#400080><TT>outportb(0x20,0x20);</TT></FONT> which signals an end
of interrupt to the PIC. You use this command at the end of your
interrupt service routine, so that interrupts of a lower priority will
be accepted. </P><A name=33><FONT size=+1>UART Configuration</FONT><BR>
<HR>
</A>
<P>Now we get to the UART settings (Finally) </P>
<P>It's a good idea to turn off the interrupt generation on the UART as
the first instruction. Therefore your initialization can't get
interrupted by the UART. I've then chosen to set up our interrupt
vectors at this point. The next step is to set the speed at which you
wish to communicate at. If you remember the process, we have to set bit
7 (The DLAB) of the LCR so we can access the Divisor Latch High and Low
Bytes. We have decided to set the speed to 38,400 Bits per second which
should be find for 16450's and 16550's. This requires a divisor of 3,
thus our divisor latch high byte will be 0x00 and a divisor latch low
byte, 0x03. </P>
<P>In today's standards the divisor low latch byte is rarely used but it
still pays us to write 0x00 to the register just in case the program
before us just happened to set the UART at a very very low speed. BIOS
will normally set UARTs at 2400 BPS when the computer is first booted up
which still doesn't require the Divisor Latch Low byte. </P>
<P>The next step would be to turn off the Divisor latch access bit so we
can get to the Interrupt Enable Register and the receiver/transmitter
buffers. What we could do is just write a 0x00 to the register clearing
it all, but considering we have to set up our word length, parity as so
forth in the line control register we can do this at the same time. We
have decided to set up 8 bits, no parity and 1 stop bit which is
normally used today. Therefore we write 0x03 to the line control
register which will also turn off the DLAB for us saving one more I/O
instruction. </P>
<P>The next line of code turns on the FIFO buffers. We have made the
trigger level at 14 bytes, thus bits 6 and 7 are on. We have also
enabled the FIFO's (bit 0). It's also good practice to clear out the
FIFO buffers on initialization. This will remove any rubbish which the
last program may of left in the FIFO buffers. Due to the fact that these
two bits are self resetting, we don't have to go any further and turn
off these bits. If my arithmetic is correct all these bits add up to
0xC7 or 199 for those people which still work in decimal. </P>
<P>Then DTR, RTS and OUT 2 is taken active by the instruction <FONT
color=#400080><TT>outportb(PORT1 + 4,0x0B);</TT></FONT>. Some cards
(Both of Mine) require OUT2 active for interrupt requests thus I'm
normally always take it high. All that is left now is to set up our
interrupts which has be deliberately left to last as to not interrupt
our initialization. Our interrupt handler is only interested in new data
being available so we have only set the UART to interrupt when data is
received. </P><A name=34><FONT size=+1>Main Routine (Loop)</FONT><BR>
<HR>
</A>
<P>Now we are left with, </P><FONT color=#400080><PRE>do {
if (bufferin != bufferout){
ch = buffer[bufferout];
bufferout++;
if (bufferout == 1024) bufferout = 0;
printf("%c",ch);
}
if (kbhit()){
c = getch();
outportb(PORT1, c);
}
} while (c !=27);
</PRE></FONT>
<P>which keeps repeating until c = 27. This occurs when the ESC key is
hit. </P>
<P>The next <I>if</I> statement checks to see if a key has been hit.
(<FONT color=#400080><TT>kbhit()</TT></FONT>) If so, it gets the
character using the <FONT color=#400080><TT>getch()</TT></FONT>
statement and outputs it to the receiver buffer. The UART then transmits
the character to the modem. What we have assumed here, is that the
person using the Communications Program can't type as fast as the UART
can send. However if the program wishes to send something, then a check
should be made to see if BIT 5 of the Line Status Register is set before
attempting to send a byte to the transmitter register. </P>
<P>
<CENTER><I>For more information on Interrupts, try <A
href="http://www.beyondlogic.org/interrupts/interupt.htm">"Interfacing
the PC : Using Interrupts"</A></I></CENTER>
<P></P>
<P><A name=35></A><FONT size=+1>Determining the type of UART via
software</FONT><BR>
<HR>
<P></P>
<P>The type of UART you have installed in your system can be determined
without even needing a screwdriver in most cases. As you can see from <A
href="http://www.beyondlogic.org/serial/serial.htm#9">Types of
UART's</A> each UART has minor differences, all we have to do it test
these. </P>
<P>The first procedure we do is to set bit 0 to '1' in the FIFO control
register. This tries to enable the FIFO buffers. Then we read bits 6 and
7 from the interrupt identification register. If both bits are '1' then
the FIFO buffers are enabled. This would mean the UART is a 16550a. If
the FIFO's were enabled but not usable then it would be a 16550. If
there is no FIFO buffer enabled it is most likely to be a 16450 UART,
but could be a 8250, 8250A or 8250B on very old systems. </P>
<P>AT's have a fast bus speed which the 8250 series of UART can't handle
to well thus it is very unlikely to be found in any AT. However if you
wish to test for them as well you can follow the same test as above to
distinguish 16550's or 16550A's from the rest. If no FIFOs are enabled
then a possible UART is the 16450, 8250, 8250A or 8250B. Once it is
established the it could be one of these four chips, try writing a byte
to the scratch register and then read it back and compare the results.
If the results match then you must have a scratch register, if they
don't you either don't have a scratch register, or it doesn't work to
well. </P>
<P>From the descriptions of the UART above if you read back your byte
from the scratch register then the UART must be a 16450 or 8250A. (Both
have scratch registers) If you don't read back your byte then it's
either a 8250 or 8250B. </P>
<P>The 16750 has 64 byte FIFO's, thus the easiest way to test for it's
presence is to enable the 64 byte buffer using the FIFO Control Register
and then read back the status of the Interrupt Identification Register.
However I have never tested this. </P></UL><A name=Part4></A>
<HR>
<I><FONT size=+2>Part 4 : Interfacing Devices to RS-232 Ports</FONT></I>
<HR>
<P></P>
<UL><A name=40><FONT size=+1>RS-232 Waveforms</FONT><BR>
<HR>
</A>
<P>So far we have introduced RS-232 Communications in relation to the
PC. RS-232 communication is asynchronous. That is a clock signal is not
sent with the data. Each word is synchronized using it's start bit, and
an internal clock on each side, keeps tabs on the timing. </P>
<CENTER>
<P><IMG alt="Serial Waveforms - Logic Levels" border=0
src="Interfacing The Serial - RS-232 Port Part 3-4_files/serwave1.gif">
<BR><FONT size=-1>Figure 4 : TTL/CMOS Serial Logic Waveform</FONT>
</CENTER>
<P>The diagram above, shows the expected waveform from the UART when
using the common 8N1 format. 8N1 signifies 8 Data bits, No Parity and 1
Stop Bit. The RS-232 line, when idle is in the Mark State (Logic 1). A
transmission starts with a start bit which is (Logic 0). Then each bit
is sent down the line, one at a time. The LSB (Least Significant Bit) is
sent first. A Stop Bit (Logic 1) is then appended to the signal to make
up the transmission. </P>
<P>The diagram, shows the next bit after the Stop Bit to be Logic 0.
This must mean another word is following, and this is it's Start Bit. If
there is no more data coming then the receive line will stay in it's
idle state(logic 1). We have encountered something called a "Break"
Signal. This is when the data line is held in a Logic 0 state for a time
long enough to send an entire word. Therefore if you don't put the line
back into an idle state, then the receiving end will interpret this as a
break signal. </P>
<P>The data sent using this method, is said to be <I>framed</I>. That is
the data is <I>framed</I> between a Start and Stop Bit. Should the Stop
Bit be received as a Logic 0, then a framing error will occur. This is
common, when both sides are communicating at different speeds. </P>
<P>The above diagram is only relevant for the signal immediately at the
UART. RS-232 logic levels uses +3 to +25 volts to signify a "Space"
(Logic 0) and -3 to -25 volts for a "Mark" (logic 1). Any voltage in
between these regions (ie between +3 and -3 Volts) is undefined.
Therefore this signal is put through a "RS-232 Level Converter". This is
the signal present on the RS-232 Port of your computer, shown below.
</P>
<CENTER>
<P><IMG alt="RS-232 Waveforms" border=0
src="Interfacing The Serial - RS-232 Port Part 3-4_files/serwave2.gif">
<BR><FONT size=-1>Figure 5 : RS-232 Logic Waveform</FONT> </CENTER>
<P>The above waveform applies to the Transmit and Receive lines on the
RS-232 port. These lines carry serial data, hence the name Serial Port.
There are other lines on the RS-232 port which, in essence are
<I>Parallel</I> lines. These lines (RTS, CTS, DCD, DSR, DTR, RTS and RI)
are also at RS-232 Logic Levels. </P>
<P><A name=41><FONT size=+1>RS-232 Level Converters</FONT><BR>
<HR>
</A>
<P></P>
<P>Almost all digital devices which we use require either TTL or CMOS
logic levels. Therefore the first step to connecting a device to the
RS-232 port is to transform the RS-232 levels back into 0 and 5 Volts.
As we have already covered, this is done by RS-232 Level Converters.
</P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -