From OpenCircuits
Jump to navigation Jump to search

Some notes on Microcontroller RS232 Communications

This is based on one person's experience with Microcontroller RS232 Serial communications on a PIC16F877A, I hope others will add to this and correct any mistakes I may have made. These notes cover some issues that are not commonly discussed, at least in the sources I have seen, or all in one place.

This is one of a series of articles on Microcontroller Serial Communications, rooted at this site here: Microcontroller Serial Communications Articles

What hardware: UART or not?[edit]

A UART is a peripheral that can send and receive serial data and signal the processor when a full character has been sent or received. ( there are similar peripherals with similar names, they support asynchronous communications, many PIC's have them or variations, so do many other microcontrollers ) Having a UART means that microcontroller can ignore the data for a period of one character ( or more if the UART has a character buffer). If the microcontroller does not have a UART then the processor must time in each bit, normally checking several times a bit to see if the bit is high or low. This is known a “bit banging” Because timing is so critical it is normally done in assembler with careful counting of each instruction to make sure the timing is correct. High level languages sometimes have built in facilities that can manage bit-banging. Changing the timing is non-trivial. Doing much else during the communications process is also difficult. A UART can send and receive at the same time, not so simple with bit banging. In my, russ_hensel 's, opinion, the UART is so inexpensive that it is not worth the trouble for a hobbyist to do with out the UART.

What language?[edit]

Again, in my russ_hensel 's opinion, assembler is just too tedious for the non-professional or non-hard-core. I like C, but other high level languages are good too. Some have libraries or built in functions that handle all the low level details for you. Some will do it in what is essentially bit-banging, still hiding the details.

What handshaking?[edit]

One of the traditional problems with RS232 is dropping of bytes. This happens when the sender is too fast for the receiver. How you manage this is called handshaking. There are various methods to make sure all the information gets through:

  • Just take it slow and hope it is slow enough – this may work, do some calculations. Works best if a small number of bytes are exchanged only occasionally. This is often the case.
  • Use extra wires to signal when ready ( hardware handshaking ). Too complicated for me.
  • Use “X on X off” protocol ( software handshaking ). Too complicated for me.
  • Send data to the microcontroller as a short enough “command” so that the PIC can buffer the whole thing, then wait for the microcontroller to respond with a signal ( I use a single character cr = ascii 13 ) before sending the next command. Usually I have see a carriage return, or a line feed, or both for that response. If you are using a terminal emulation program the cr lf pair can be nice. Remember shorter is faster. In general the microcontroller should responds with responses that are short enough so that the PC can manage them ( but it can manage a lot normally, I believe it has fairly fancy hardware/software to buffer a lot of characters ) or at a data rate that is fairly low. More important is that for fast microcontroller activity the less communications time there is the more time there is for other stuff.
    • The PIC waits until it is completely finished executing the command and is ready to accept the next command before it responds, indicating "ready for the next command". This is the method that I russ_hensel normally use, it is pretty simple and has been pretty reliable for me.
    • The PIC immediately responds as soon as it receives a complete, valid command, indicating "I got the message". The microcontroller then executes the command -- which may involve sending further information back to the PC. If the PC doesn't get a "I got the message" response back in a reasonable amount of time it assumes the PIC happened to be busy, wasn't ready for a command at that time, and tries re-sending the command later.

What Data To Send?[edit]

Typically the PC sends a command and the microcontroller executes it.

It makes things vastly easier to debug if a human can type those commands and read the response (with a terminal emulator). This requires (a) using printable ASCII characters for everything in both directions (avoiding unprintable "control characters"), and (b) the microcontroller waits until the command has been finished typing (even if the human types extremely slowly) before sending back the response.

I like 1 character commands because they are so easy to interpret on the microcontroller. Usually I make them case insensitive.

There are a variety of ways to send numbers:

  • decimal numbers (easiest for humans to read and write)
  • base 16 "hex numbers" (easiest for microcontrollers to read and write, and still be human-readable)
  • binary numbers 01101110 can be useful for the human operator if, for example, a port set up command is being issued.

If more than one number is sent or recieved, we typically use some other "separator character" between each value. In sending or recieving multiple parameters you should adopt a special character different from the end of response character. Common choices are the space character, the tab character, or the comma. If recording data to a file and importing to a spreadsheet, think about what the spreadsheet parses best.

Out of a misguided quest for "efficiency"[1], lots of people have spent a lot of time figuring out ways to sent the same amount of information in fewer transmitted bytes. Because many people do this (even though it the system harder to debug), I will mention some of those techniques here:

  • Data compression techniques can be used to further reduce the number of bytes required to transmit numbers. But data compression makes latency much worse (longer) and requires more code and RAM space, so it is not suitable for small embedded systems.
  • Remove the separator character between values. (after this step, the following ideas *at best* reduce the number of bytes transmitted to 1/2).
  • higher bases than base 16, but still using printable ASCII characters: base 32 (numbers plus most upper case alpha), base64, base91, and other binary-to-text encoding. Takes more code on the PIC, makes latency worse.
  • base 256, also called "a binary protocol": all possible characters, without regard to whether they are printable or not. (If you ever get off by 1 character, how do you re-sync to the beginning of the next message?)(makes debugging far more difficult).

In deciding on the encoding think about the PC side of the system. If you are planning to mainly use a terminal enulator then speed in sending to the microcontroller probably matters not at all, and friendliness to the human operator matters a lot. If you have an application specially written for the microcontroller then a more complicated interface optimized for speed and robustness is an option.

All too many times I (DavidCary) have 2 systems, and I can't remember which one (if either) has been updated to the latest version of the software. It is convenient if there is some command (typically "?" or "ver", perhaps followed by a newline) that tells the PIC to send back a year-month-day-hour:minute timestamp. Some programmers make this "version" command send back the name of the software, the name of the people involved in the project, and perhaps a couple of lines summarizing the most useful commands. If you are talking to the microcontroller with a dedicated application ( as I tend to do ) then I issue the version command right after the connection is made and display it. If the microcontroller does not respond at all or with the wrong version then I and the software knows that there is a connection problem. I am planning to implement this in a connection routine that scans all ports to look for the one with the correct version response.

Interrupt driven?[edit]

You can make your communications interrupt driven either by letting the UART generate interrupts, or by using a timer to generate interrupts where you poll the UART. Normally this is most useful on the receive end where if you are too slow you loose data. On the transmit end the worst that normally happens is that you send to slowly. How do you decide if the receive should be interrupt driven? This really depends upon your program. What is the most time critical part of the program? Normally I would give that part of the program the use of the interrupt. An example: I was writing a RC servo program and wanted to time a 1 to 2 ms pulse to an accuracy of a few micro-seconds. So I timed the pulses using one of the timers, this gave me an accuracy of about 1 micro-second. In the main loop I would check the UART reception each time around the loop. I know I am ok if the time around the loop plus the time for interrupts that may have happened is less than the time to receive a character. All this is fairly easy to calculate, and it is not too hard to make it around the loop in one character time. At 9600 baud you have about 1 ms per character. You can also check more than once in the main loop. If you need more time, you may want to lower your baud rate. Normally I have found that the character receiving is not the critical task so I have not used the interrupts for communications. Your mileage may vary.

What speed?[edit]

Faster is better, but only if it works. Check the manual for your chip to see how close you can get to the exact baud rate with the crystal you have chosen. Try to stay within a few percent. You may want to change your crystal to get a better match. I have used 19.2 K with 4 and 20 meg Hz crystals with good results, I have not tried much with other speeds, could we get reports from other people. If you run too fast for your receive software you may want to drop the baud rate to make reception easier on the microcontroller, also low rates are generally more reliable, how fast do you need to go?: Another issue is the length of the wire you use. You can probably Google to find advice on the longest wire for each baud rate, but in general use lower speeds for longer wires. If you want to go a real long way consider using RS485 voltage levels ( you will need a converter on the PC as well as the PIC ) to go the distance.

How many wires?[edit]

RS232 can run on as few 3 wires, transmit, receive and ground ( maybe even 2 if receive and transmit are shared on one wire ) Other wires may be used for additional functions. I have always used just three and think that this is most common. Three wires is what I recommend.

What voltages?[edit]

If you take the signals directly from the PIC the voltages will be the same as the PIC 0 to 5 volts typically, even lower if the supply to the PIC is less than 5 volts. These voltages are too close to 0 to be “standard” for RS232 although a few PCs will work with them. Normally people will use a conversion circuit. You can put this in each project or build it into the cable ( cables for with this built in can be purchased or you can build them ). The cable approach makes the circuit simpler and you can reuse the cable between projects.

The voltage level at the PIC UART TX and RX pins (0 to 5 V) is often called "TTL level". The voltage level at the FT232 UART TX and RX pins is also "TTL level". The voltage level at a PC 9-pin "serial port" TX and RX pins (-12 to +12 V) is often called "standard RS232 level".

Level Shifters[edit]

We use "level shifters" to convert between these different ways of expressing UART "1" and "0" bits.

Any one of the following ways will work:

  • Actually this way may work, just ignore the levels an hook it up to your PC, I have found several references that claim that many PC port will accept the wildly off signals. We are not responsible if anything goes wrong.
  • Some people strongly believe in building these from basic components, google will help you find some circuits for this.
  • hook a TTL to RS232 level converter chip, such as the MAX232, between the PIC and a standard RS232 level. Then either connect the RS232 level side directly to a PC 9-pin "serial port", or use a off-the-shelf "USB to serial adapter cable" (which typically includes *another* RS232 to TTL level converter chip, and a FT232 chip to convert between USB and TTL level).
  • build a TTL to RS232 level converter out of 2 cheap MOSFETs and 2 resistors using the Dale N0XAS configuration, between the PIC and a PC 9-pin "serial port".
  • directly connect a FT232 between the PIC and a PC's USB port.
  • hook a TTL to RS485 level converter chip ([2]) to the PIC. Then use an off-the-shelf "USB to RS485 adapter cable" (which typically includes *another* RS485 to TTL level converter chip, and a FT232 chip[3] to convert between USB and TTL level).
  • hook a TTL to RS485 level converter chip to the PIC. Then use an off-the-shelf "RS232 to RS485 adapter box" (which typically includes *another* TTL to RS485 level convert chip, and a TTL to RS232 level converter chip).

More on Level Shifters[edit]

Another consideration on level shifters is where to "build" them. They can be in your project on its circuit board, but putting the level shifter on a seperate board lets you shift it around between projects. You can consider the level shifter to be part of the cable, or in some cases the are actually built into the cable. There are quite a few around, and often at prices near $10.

Some links:

Serial Adapter looks like it may be the same as

P4 - Serial TTL-to-RS232 Adapter/Programmer (rev 1.0) with DTR Support

Assembling a RS232 to TTL Serial Adapter an instructable using this:

P4 RS232 to TTL Serial Adapter Kits also a good price.

RLC-1 RS232 Level Converter in a DB9 backshell A classic.

USB-TTL Serial Cable Usb at the pc end looks like ttl rs232 at the other, great if your pc does not have a serial port.

RS232 to TTL Serial Adapters A Great Range RS232 to TTL Adapters from a UK Source.

USB to TTL Adapters Using a Silabs IC or a FTDI FT232R IC from a UK Source. (FTDI USB to TTL +5V & FTDI USB to TTL +3.3 (I/O) Cables)


Your Comments:[edit]

  • Edit the text
  • Use the discussion page
  • Contact russ_hensel