PIC-based APRS Weather Station     


PIC APRS 
Weather Station

~ Part 2 Files ~

 

Schematic

 

Source Code

 

All Software Files
(Zip contains files for hex, list, err, cod and asm.)

 


Back to 
Digital Homebrewing Home Page


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 Overview

Since 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
(Click here for full view)

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 UART

The 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

Copyright 2002 G. Heron, N2APB