5

I am writing a communication between Atmega162 and PC.

On my PCB I have interface RS485 (converted from RS422 by MAX485) and it goes through ADAM-4520 transceiver into COM port.

I've been testing my program in terminal and it seems strange to me, that sending characters from MCU works fine, but these received from PC are changed (I cannot figure out any scheme of this conversion).

For example these ASCII characters are interpreted this way:

0   => 0
1   => 64
2   => 32
3   => 32
4   => 16
5   => 65
6   => 16
7   => 16
8   => 8
'1' => 204
'2' => 102
'3' => 70
'4' => 51
'5' => 141
'6' => 35
'7' => 51
'8' => 6
'9' => 142

I've been testing it for several transmission parameters, but it doesn't seem to help. The source code is here:

void USART_init()
{   
    UCSR0B |= (1<<RXEN0) | (1<<TXEN0) | (1<<RXCIE0);    
    UCSR0C |= (1<<UCSZ10)|(1<<UCSZ00)|(1<<USBS0)|(1<<UPM10);        
    UBRR0H = 0;
    UBRR0L = 12;

    DDRD |= 1<<PD1;

    _delay_ms(1);
}

void USART_Transmit( unsigned char data )
{   
    PORTD |= 1<<PD4;
    while ( !( UCSR0A & (1<<UDRE0)) );  
    UDR0 = data;    
    while (!(UCSR0A & (1 << TXC0)))
    PORTD &= ~(1<<PD4);
}

ISR(USART0_RXC_vect)
{   
    unsigned char a;
    while ( !(UCSR0A & (1<<RXC0)) );

    a = UDR0; 

    speed_1 = a;
}

PD4 switches between transmitting and receiving.

pasiasty
  • 71
  • 8
  • 1
    You probably have the parity or number of data bits set wrongly. – Paul R Jan 31 '15 at 10:28
  • well I asumed so, but I really have checked every possibility. I think, that ADAM 4520 can cause troubles. In datasheet [link](http://e-oktat.pmmf.hu/webgui/www/uploads/1017/ADAM-4510_4510S_4520_Startup_ed.4.pdf) stays, that I can choose BAUD rate and data format (between 9 and 12 bits). But i don't know, if this amount includes start, stop, parity bits and so on. Besides all 3 flags (FE, DOR and UPE) in UCSRA0 are set :/ – pasiasty Jan 31 '15 at 11:58
  • I would go for a simple basic configuration: 8 data bits, 1 stop bit, no parity, 9600 baud. – Paul R Jan 31 '15 at 12:05
  • that's what i did on the beginning. – pasiasty Jan 31 '15 at 12:12
  • 1
    The test values you used are not very helpful to diagnose the problem, they have too many bits turned on and it looks like you sent more than a single byte. But it *looks* like you got the + and - wires reversed, RS485 uses differential signals. That causes bit values to be inverted and a data-bit to be misinterpreted as a start bit. Also very, very important that you improve your error checking, you are surely getting framing errors here. – Hans Passant Jan 31 '15 at 14:31
  • I updated example values and you're right - I am getting framing errors. Actually all possible errors (frame error, parity error and data overrun). Wires cannot be reversed, because transmitting from MCU to PC works fine and after I switched them I couldn't send anything :/ – pasiasty Jan 31 '15 at 16:17
  • 1
    No, I'm not talking about swapping the TxD and RxD signals, I'm talking about the polarity of the signals. Each signal has two wires, the two for RxD are reversed. Use electronics.stackexchange.com to ask questions about proper wiring for RS-485. – Hans Passant Jan 31 '15 at 16:44
  • Actually for RS485 there are only two wires (Data+ and Data-) two wires for each signal as you mentioned are in RS 422 protocol. – pasiasty Jan 31 '15 at 17:29
  • 3
    `while ( !(UCSR0A & (1< – UncleO Feb 01 '15 at 03:37
  • yes you're right. But still it doesn't solve the problem, because incoming data is wrong even if i do not transmit anything before. – pasiasty Feb 01 '15 at 14:18
  • If possible, try connecting the device to PC using a 3-wire RS232 interface first, ths is to verify whether the serial communication (both hardware and settings) is working fine. I think the problem is caused by the RS485 hardware connection because the USART setting is common for both tx and rx and one direction of data flow is ok. – mfc Feb 01 '15 at 21:32
  • I did and everything seems to work fine. Then it must be caused by this ADAM-4520. I'll use another converter then. Thanks for help – pasiasty Feb 02 '15 at 13:23
  • The 2-wire RS485 iis physically multiplexing 2 sets of signal (tx/rx) onto 1 set of signal line. This is further complicated by the fact that both ends of the communication line is doing it at its own timing and own way (RTS/Auto control). So when one side is transmitting while the other is still in tx mode, no signal will be received, but when it switch to rx mode in the middle of a tx packet, it will result in corrupted rx data or framing error. So the best way of using RS485 is to reserve some slack timing between tx/rx cycle and alway keep both ends in rx mode when idle. – mfc Feb 02 '15 at 16:18
  • well I suppose, that maybe this converter did not manage idle mode in right way and it assumed, that MCU was whole time active, but unfortunately I won't be able to check it out anymore, because I had to give it back (it wasn't mine) :/ – pasiasty Feb 02 '15 at 21:25
  • UCSR0C |= (1< – AterLux Jan 18 '17 at 14:11

1 Answers1

0

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.

AterLux
  • 4,566
  • 2
  • 10
  • 13