# PIC16: Problems with UART receive

I have a PIC16 for which I have the (asynchronous) UART transmit working just fine, but the UART receive producing invalid results.

For example, a gets interpreted as O, b gets interpreted as ', and c gets as N.

Here is my receive function:

char UART_read(void) {
while(!PIR1bits.RCIF) {}

return RCREG;
}


My hypothesis is that the UART polarity is wrong for the receiver, so that in particular the start/stop bits get messed up. I have set the SCKP bit (see page 302) to invert the data on the TX/CK pin but I cannot find an equivalent for the RX/DT pin.

What could be the cause of the UART receive not working? How can I invert the data on the RX/DT pin?

-
What is the source of data? Are you sure source and sink have the same baud rate, stop bits, parity, voltage levels, etc? –  apalopohapa Nov 14 '12 at 7:27
Also, what happens if you perform a loopback test? –  apalopohapa Nov 14 '12 at 7:28

EDIT: This answer has been modified to reflect the comment by the question asker and points out that he is quite right in his diagnosis of the problem, which isn't terribly useful information. The only useful piece of information in the section is the fact that there is no receive invert bit... But perhaps it will help future people diagnose their own UART problems

So let's take a look

a maps to O which means 01100001 maps to 01001111

b maps to ' which means 01100010 maps to 00100111

Let's assume that there's an implicit 1 before each of those and an implicit 0 after (start and stop bits). The RX module receives a continuous low signal, then the stop bit is sent as a high, which will be eaten as an idle signal, then the initial 0 will be consumed as a stop bit. Then the remaining bits will be inverted,

so 'a' == 1 10000110 0 initially (we send least significant bit first)
1 (implicit start bit) and starting 1 are both consumed as idle bits
then the first 0 is treated as a start bit
so received == 00001100 inverted (the extra zeros are the stop bit and idle bits after transmission has ended)
11110011 and reverse it (it was sent LSB first)
11001111 is what the inverted input would look like
01001111 is what's actually received which is super close!
if what you actually sent was "abc" all in a row, then the start bit of the b would make what was received 01001111 which matches exactly

'b' == 1 01000110 0 ==> 10001100 inverted and reversed gives
11001110 is what should have "logically" happened
00100111 actual
So that doesn't quite match, but if we assume that the "abc" is what happened again, we get
01001110 which is close enough (not sure where the shift came from)


Looks like you nailed the diagnosis, unfortunately there is no similar "invert signal" configuration bit in the receiver on the PIC. Though it's not hard to put an inverter on the receive path, which is incidentally what I would recommend doing!

As Olin noted, standard rs-232 signals are from +3V to +15V for a logical '1' and -3V to -15V for a logical '0'. The PIC is designed to operated at "TTL" levels, which means "transistor-transistor logic". The idea is that the PIC is designed to talk to other things which are physically close (i.e. on the same board) and thus the extra transmission distance and noise rejection provided by the full rs-232 levels are not needed for "standard" operation. It's not practical to be able to completely internally generate the positive/negative voltages needed for rs-232 operation completely internally on the PIC, so "normal" rs-232 communication was never really an option.

Since TTL levels are not the standard but instead a derivative of the standard (same protocol for timing, start and stop bits, parity, but different voltages), you don't buy a consumer "rs-232 adapter" for your computer unless it adheres to the standard. They do make TTL-level rs-232 adapters and they're very popular on hobbyist websites! Check out Adafruit's cable or the Sparkfun FTDI breakout. I find that in general, if it goes to a DB9 connector, it's probably a rs-232 level adapter.

-
That's an O (the letter), not a 0 (the number). So 01100001, 01100010, 01100011 map to 01001111, 00100111, 01001110 respectively. Also, remember bits are sent LSB first. So if the receive line is inverted, the right-most 1 is the start bit. So 01100001 becomes ~10110000, 01100010 becomes ~11011000 and 01100010 becomes ~10100111, all matching what I receive. –  Randomblue Nov 14 '12 at 17:43
@Randomblue : I corrected my response to actually do the analysis properly, and added some extra notes about the rs-232 and TTL discrepancy for PIC to computer interaction. –  Kit Scuzz Nov 15 '12 at 9:08
Neither of the two products you linked to convert logic level signals to RS-232. They both appear to be USB to serial converters, which is not applicable here. –  Olin Lathrop Nov 15 '12 at 13:43
@OlinLathrop : I was never quite certain what the actual protocol was called. "Serial" implies any non-parallel communication protocol, and "Asynchronous Serial Protocol" would seem to allow all sorts of different character framing methods, endianness, etc. You're right in that the links I gave are not to, explicitly, rs-232 adapters. Is USB to UART the correct term? The Adafruit cable actually calls itself a "USB FTDI TTL-232 cable", hence why I assumed "TTL-level rs-232 adapter" was an appropriate title. –  Kit Scuzz Nov 15 '12 at 17:23
@OlinLathrop : As for their applicability to this conversation, I'll admit that I was making an assumption about Randomblue's usage here. He said that he was trying to interface his Windows 7 computer with his project (in the comment below your answer), and I gave links to products which will make that easier for him. He could use max232 circuits to interface the chip he has now with the serial adapter he has now, but I assume that, like most hobbyists, he'll find that gets tiresome quickly and want a simpler method. –  Kit Scuzz Nov 15 '12 at 17:26

To see if you need inversion, look at the line idle level. Normal logic level UART signals idle high. Since you inverted the output, it should be idling low. If the other device is using inverted signals to receive (your transmit), then almost certainly it is using the same levels for its transmissions (your reception). Check with a scope, but you probably need inversion, which can be done with a inverter logic gate or just a transistor and a resistor or two considering the low data rate.

How did you ever think reception would work without inversion when transmission required it!?

You have now revealed that you simply connected the PIC UART lines to RS-232 receive and transmit lines and somehow exected everything to work. The digital logic levels used by the PIC and the RS-232 levels are not compatible, and high/low are also inverted. RS-232 is supposed to be below -5 V for line idle (space) and above +5 V for active (mark). Clearly a PIC can't guarantee these levels when transmitting, and can be damaged by these levels when receiving. The normal digital logic levels are high for space and low for mark, which is also what the PIC does except for a few limited ones such as yours where one or both of the lines can be inverted.

Some RS-232 receiver chips play loose with the RS-232 specs and work with 0-5 V signals driving their receive line. This is apparently the case with your PC, which is why you were able to receive characters on the PC after just inverting the PIC transmit line. To get PC --> PIC transmission to work, you have to invert the signal and map the result to the PIC's Vss to Vdd range. Failure to do that may damage the PIC, which may have already happened.

All this is why the MAX232 chip type was created. This is powered from the PIC Vdd and Vss, contains its own charge pumps to make the RS-232 voltages, has the proper driver and receiver circuitry on each side, and also does the inversion. Many companies make a number of variants of this. These are very common and available chips.

You can also get little modules that have the converter chip, charge pump and power supply caps, and standard DB-9 connector all in a single unit. I make one of these that is even sold on microchipdirect. See http://www.microchipdirect.com/ProductSearch.aspx?Keywords=TEMRS002.

-
To answer your question, I'm using what I'm assuming is an extremely common setup: Win7 with TeraTerm on one side, and PIC16 on the other. Shouldn't UART "just work"? –  Randomblue Nov 14 '12 at 17:27
@Random: Absolutely not, as even a cursory look at the relevant specs would have revealed. Connecting a PIC directly to a RS-232 can damage the PIC and not provide the correct levels on the RS-232 side, even if you did simply invert the logic signals. This is why there are chips like the common MAX232 and its many variants and knockoffs. You really need to read some specs occasionally. –  Olin Lathrop Nov 14 '12 at 17:39
You have no idea how many times I've read the EUSART chapter (starting page 291 of the PIC16 datasheet). There is no mention or warning regarding direct connections to RS232, or the MAX232. Where did I fail in the process? My assumption was that the PIC16 datasheet chapter on EUSART would cover the common use case: RS232. –  Randomblue Nov 14 '12 at 17:52
The datasheet mentions maximum input levels on any pin. RS232 is far outside those levels. It's not really in the scope of the PIC16 datasheet to explain how to convert them. In general, if you're trying to connect two things, you can't expect to get all of your information from reading the specifications for just one of them. –  Jim Paris Nov 14 '12 at 17:55
@Random: It's not Microchip's job, and it would clutter up the datasheet, to explain RS-232 or what conversions are needed to connect the PIC to something else. They document what the PIC does. It's your job to know what you are connecting the PIC to and add any interface circuitry that may be required based on the specs of the other side. –  Olin Lathrop Nov 14 '12 at 17:59