PIC-based APRS Weather Station |
PIC
APRS ~ Part 2 Files ~
All
Software Files
|
Part 2 - Communications Last
time we got started with the basics of PIC circuits and PIC programming.
Hopefully, you now have a PIC and a programmer, and you know how to use
them. In this installment we’ll begin to add useful functionality to our
circuit by teaching it how to communicate with a PC via serial
communications. There
are some PIC chips that have serial communication capability built-in, but
the PIC16F84 isn’t one of them. When I first began messing with PIC
circuits, all I knew was the ‘F84 and my home-built programmer, so
without knowing any better I set out to code the serial capabilities
manually. It turned out great, though, and I’ve since reused the very
same code for other projects. Since the ‘F84 is cheap and easy to
program, I’ve stuck with it despite the availability of superior PICs.
That being said, let’s get started! Serial Communications – An OverviewSince
you might not be familiar with the nuts and bolts of serial
communications, let’s begin with an overview. You’ve probably had to
set parameters like baud rate, stop bits, parity, and the like when using
communications software on your computer. When your PC communicates with
another device using its serial port, it sends a bunch of bytes
(characters) one at a time. Furthermore, each byte is sent one bit at a
time (eight bits to a byte). When a bit is sent, the voltage on the serial
wire is set to one value for a “1” and another value for a “0”.
The specific values depend upon the type of logic in use (TTL, CMOS,
RS-232, etc.). We’ll
be concerned with two types of logic: TTL, where 1’s are +5V and 0’s
are 0V, and RS-232, where 1’s are –12V and 0’s are +12V. Note that
these are typical voltages, but
the specifications actually allow for a range of voltages for these
values; we’ll stick with these values for the sake of simplicity. Our
PIC chip talks using TTL voltages, but the PC uses the RS-232 voltage
levels. We’ll need some way to convert between the two—more on that
later. We’ll
be talking exclusively about asynchronous
serial communications here. That’s where the common term UART comes from
… Universal Asynchronous Receiver Transmitter.
There is also a flavor of serial communications called synchronous,
where the two communicating devices share a clock line to synchronize
their chatter. In asynchronous communications, however, the devices are
responsible for handling timing themselves and that’s what we’ll be
focusing on here. Let’s
look at a diagram of a typical stream of bits and see what sense we can
make of it.
Figure 1: Serial
communications bit stream Here,
voltage is the vertical axis of the graph, and time is the horizontal
axis. Before any data is sent, the sending device holds the data line high
(the “1” voltage). When it’s time to transmit a byte, the sending
device sends a start bit by
taking the data line low (the “0” voltage). The start bit is the
signal to the receiving device that data is coming. The
duration of each bit is determined by the baud rate set for the
communications. A typical value is 9600 baud, which means that 9600 bits
are sent per second, or each bit lasts for 1/9600 sec (0.000104167 sec).
After the start bit is sent, each of the eight bits making up the byte is
sent, with the most significant bit (the leftmost bit) sent first. The
voltage of the data line changes to high or low depending on whether each
bit is a “1” or a “0”. Note that if two or more successive “1”
bits are sent, the data line stays high for the duration of the
bits—there is no “delimiter” to signal the end of one bit and the
start of another. Thus, it’s up to the receiving device to “time”
the bits to determine when one bit ends and another begins. Usually, there
are eight data bits sent for each byte, but it’s possible to use only
seven data bits if all the bytes to be sent are alphanumeric data because
the most significant bit is always zero for alphanumeric characters
(meaning their ASCII values are less than 128). Once
the byte is sent, a parity bit can optionally
be sent. If even parity is
chosen for the protocol, if the number of “1” bits in the byte is
even, the parity bit is set to “1”, or it’s set to “0” if the
number “1” bits is odd. If odd
parity is chosen, the setting of the parity bit is reversed. It’s also
possible (and actually more typical) to select no
parity for the protocol, in which case no parity bit is sent. Once
the parity bit is sent (if parity is selected), the stop
bit is sent. The stop bit is simply a “1”, ensuring that the data
line is held in the “1” state for a time before the next start bit is
sent. Although only one stop bit is typically sent, it’s possible to
specify that one and a half or two stop bits are sent. In this case, the
line is held in the “1” state for the duration of one and a half or
two bits. Obviously,
both the sending and receiving devices must agree on the baud rate, the
number of data bits, the number of stop bits, and the parity setting, or
else there will not be successful serial communications between the two
devices. Almost universally, eight data bits, one stop bit, and no parity
are chosen. That’s what we’ll be using for this project. We’ll also
be using a baud rate of 9600. I chose this rather arbitrarily. Normally,
faster baud rates are favored, but the amount of data exchanged between
PIC and PC for this project will be small, and speed won’t be an issue. There
is one other communication parameter that we haven’t yet discussed: flow
control. In some systems, the receiving device needs to signal the
sending device to stop sending for a moment, perhaps to allow the
receiving device to empty its receive buffer and process and store the
data. Commonly, systems use either xon/xoff
or hardware flow control. Frankly, I don’t know much about flow
control so I’m not going to discuss it in any detail here. The PIC Wx
project doesn’t use flow control. We’ve
already discussed the difference between the voltage levels for TTL vs
RS-232 logic. Our PIC chip uses TTL logic levels, but it must communicate
with a PC that uses RS-232. Thus, we need to be able to convert between
the two. Luckily, Maxim (www.maxim-ic.com)
makes a nice chip called the MAX232 that does this for us. It requires a
+5V supply and four 1-uF electrolytic capacitors, and provides two
channels of TTL-to-RS-232 conversion, and two channels of RS-232-to-TTL
conversion. We only need one channel of each for this project. Simply put,
the MAX232 chip is connected between the PIC and the PC, as you can see
from the schematic below. You can get the MAX232 from a variety of
sources, including Jameco (www.jameco.com).
Get the MAX232CPE version.
Figure 2: Schematic diagram for part 2 of the PIC Wx project The
DB9F connector shown in the schematic is a female DB9 connector—you’ve
undoubtedly seen one of these before, most likely on the cable end of a
computer mouse. There are really only three pins that we care about on
this connector. Pins 2 and 3 are the transmit and receive lines for the
data, and pin 5 is the ground line. Some of the other pins are connected
to each other: pins 1, 4, and 6, and also pins 7 and 8. This was done in
case the PC expects flow control to be used. It’s a way of faking out
the PC into thinking that it’s getting the correct response when doing
flow control things. I’m not going to discuss this any further here, but
the ARRL Handbook has some additional information if you’re interested. While
we’re on the subject, we need to discuss one other aspect of serial
communications. Did you ever wonder why PCs have male connectors, and mice
have female connectors? The obvious reason for this, of course, is so that
you can plug your mouse into the PC! There’s a little more to it,
though. The circuit as shown above is wired as data communications equipment (DCE) so that it can be plugged
directly into the PC, which is wired as data
terminal equipment (DTE). Generally, DCE has a female DB9 connector,
and DTE has a male DB9 connector. In addition, DCE sends data on DB9 pin 2
and receives data on DB9 pin 3, while DTE receives on pin 2 and pin sends
on pin 3. Of course this makes sense since you want DCE to receive
whatever DTE sends, and vice versa. If you want to connect DCE to DCE, or
DTE to DTE, you need to use a null modem cable, which switches lines 2 and
3. That won’t be necessary here. The Software UARTThe
code for the PIC is much more complex this time than it was for the first
installment. I’m not going to cover it all in great detail because of
time and space constraints. Here are some highlights. First, most of the
code is for handling the serial communications. The PIC always expects to
get a command from the PC before sending anything, so it uses an external
interrupt to know when the PC has sent a start bit. Pin RB0 on the PIC
functions in this capacity when the external interrupt is enabled using
the OPTION register. Then, when RB0 changes state, the PIC stops what
it’s doing and jumps to address 0x04 and begins executing code there.
I’ve written code at that location to begin listening for bits and
saving them. It does this by sampling the RB0 state at the same interval
as the bit duration, checking in the middle of each bit. Once all eight
bits have been received, the received character is placed in a file
register for use by the program, and a flag is set in the SerialReg file
register to indicate that a character was received. Another
type of interrupt, a timer interrupt, is used to wait for each bit to arrive. Instead of
simply doing nothing between bits, the internal timer is set to generate
an interrupt when it’s time to check for the next bit. That way, the PIC
can be doing other things while waiting for the next bit to arrive. When
sending characters back to the PC, a similar procedure is used. The timer
interrupt is set to remind the PIC when it’s time to send the next bit.
At those times, the PIC simply looks at the next bit and sets the RA1 line
high or low as appropriate. When the character has been sent, a flag is
set in the SerialReg file register to indicate completion. This
all sounds complicated, but to actually use
these routines is easy. There are two subroutines, named GetAChar and
SendAChar, which you can call. GetAChar continuously checks the SerialReg
file register until the “character received” flag indicates that a
byte was received. Then it returns, and the received character will be
found in the RXBuff file register. GetAChar also does one other thing: it
calls subroutine Idle, which can be used to do other things while waiting
for the character to arrive. Subroutine
SendAChar works similarly. First, you place the character to be sent in
the TXChar file register. Then you call subroutine SendAChar. It calls the
appropriate serial routine to get the character sent, and waits for the
flag to be set indicating completion. While it waits, it also calls
subroutine Idle to allow for other processing to occur. Once the character
has been sent, SendAChar returns. Subroutine
MainLoop handles the receiving and processing of commands. It calls
GetAChar, and then checks the received command against the list of allowed
commands and calls the appropriate subroutine. If it doesn’t recognize
the command it simply ignores it. Here, there are two commands that will
be processed. Sending a ‘t’ from the PC will cause the PIC to return a
five-digit number in ASCII form, that will change each time the command is
received. A ‘v’ command from the PC will cause the PIC to return a
version string. Your Homework Assignment Your
assignment now is to build the circuit shown in the schematic, program the
PIC with the code for this installment (from the Digital QRP Homebrewing
web site http://www.njqrp.club/digitalhomebrewing/),
connect the whole works to your PC, and test it out using Hyperterminal or
some other communications software. Remember to configure the software to
talk directly to the serial port instead of a modem, and set it up for
9600 baud, 8 bits, 1 stop bit, no parity, and no flow control. Type a
‘v’ and see if you get a response, and then try typing ‘t’ several
times. Next
time we’ll be adding our first weather sensor to the system—a
temperature sensor. Should be lots of fun! 73,
Dave NK0E
Page last modified:
December 30, 2002
|