Looks like idle line is treated wrongly in your setup.
Lets see what a transmission over RS-485/RS-422 is looks like.
By default, when no device is transmitted, lines are in idle state. Normally they are pulled together by the terminal resistors and there is no differential voltage across the lines. Or there are bias resistors which pull the line to make 200mV (logical 1
) difference between A and B.
When transmitter driver is enabled and 0 or 1 is being transmitted, the transmitter pulls the lines apart, making differential voltage across them more than 200mV with one sign or another.
And transmission of a byte would be look like this
............ 0 1000 1100 1 1 ............
(idle line) | | (data) | | | (idle again)
driver enabled | | | driver disabled
start bit parity |
stop bit
In this example digit 1
(0x31 hex, or 00110001 bin) is being transmitted, assuming even parity is enabled.
On UART, each transmitted byte is started by a start-bit (which is logical 0
) then the data is being transmitted in little-endian form (i.e. starting from the least significant bit), and it finished by transmitting one or more stop bits, which is logical 1
.
Normally, idle line is treated by the receiver as 1
, and first transition from 1
to 0
is treated as a start bit.
But lets see whats happens, when idle line is treated as logic 0
?
000000000000 0 1000 1100 11 000000000000
(nothing, ||^^ ^^^^ ^^ |
framing error) || data treated as stop
1 treated as idle|
0 treated as start
in this case, start bit (level 0) is ignored, and first bit 1
is treated as the stop bit of previous transmission. First bit 0
after that is treated as the start bit of a byte, and following 8 bits are considered as the data.
In our example data would be 11001100, which is 204. Lets see other examples
transmitted '4' (0x34): ..... 0 0010 1100 11 ...........
received: S 1100 11 00 - result 51
transmitted '5' (0x35): ..... 0 1010 1100 01 ...........
received: S10 1100 01 0 - result 141
transmitted '6' (0x36): ..... 0 0110 1100 01 ...........
received: S 1100 01 00 - result 35
transmitted '7' (0x37): ..... 0 1110 1100 11 ...........
received: S 1100 11 00 - result 51
transmitted '8' (0x38): ..... 0 0001 1100 11 ...........
received: S0 11 00000 - result 6
transmitted 0: ..... 0 0000 0000 01 ...........
received: S00000000 - result 0
Although my hypothesis does not add up for the first half of your examples. E.g., for 1 i expect you to get number 192, for 5 - 193, except if you got those numbers while parity was disabled:
transmitted 1: ..... 0 1000 0000 1 ...........
received: S00 0000 1 0 - result 64
transmitted 2: ..... 0 0100 0000 1 ...........
received: S0 0000 1 00 - result 32
transmitted 3: ..... 0 1100 0000 1 ...........
received: S0 0000 1 00 - result 32
transmitted 5: ..... 0 1010 0000 1 ...........
received: S10 0000 1 0 - result 65
transmitted 8: ..... 0 0001 0000 1 ...........
received: S000 1 0000 - result 8
My conclusion: you have to check RS-422 line which goes from PC to the MCU. It has some issues which make the receiver think there is logical 0
when line is in the idle state. Probably there are bias resistors which are connected the wrong way.
And also, as it was said in the comments, you have an issue with initialization:
UCSR0C |= (1<<UCSZ10)|(1<<UCSZ00)|(1<<USBS0)|(1<<UPM10);
^^^^^^ has to be UCSZ01
But, since you are using |=
instead of simple assign, and UCSZ01
bit is by default is 1
, this also may work as expected.
Also the loop
while ( !(UCSR0A & (1<<RXC0)) );
should not be in the interrupt handler. Since the interrupt fires only when RXC
bit is set, you may assume there is a data in UDR
, otherwise, this loop may hang your interrupt routine forever.