Electronic – PIC16: Problems with UART receive

picuart

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?

Best Answer

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.