USART 数据传入错误

pas*_*sty 5 c avr atmega serial-port usart

Atmega162我正在写和之间的通信PC

在我的PCBI 上有接口RS485(从 转换RS422而来MAX485),它通过ADAM-4520收发器进入COM port

我一直在终端中测试我的程序,这对我来说似乎很奇怪,发送字符MCU工作正常,但接收到的字符PC被更改了(我无法弄清楚这种转换的任何方案)。

例如,这些 ASCII 字符的解释方式如下:

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
Run Code Online (Sandbox Code Playgroud)

我已经测试了它的几个传输参数,但似乎没有帮助。源代码在这里:

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;
}
Run Code Online (Sandbox Code Playgroud)

PD4在发送和接收之间切换。

Ate*_*Lux 0

看来您的设置中对空闲线路的处理错误。

让我们看看 RS-485/RS-422 上的传输是什么样的。默认情况下,当没有设备传输时,线路处于空闲状态。通常,它们由终端电阻拉在一起,并且线路上没有差分电压。或者有偏置电阻拉动线路以1在 A 和 B 之间产生 200mV(逻辑)差异。

当发送器驱动器启用并且正在发送 0 或 1 时,发送器将线路拉开,使线路上的差分电压超过 200mV,并带有一个符号或另一个符号。

一个字节的传输看起来像这样

............   0 1000 1100 1 1    ............

(idle line)  | |  (data)   | |  |  (idle again)
driver enabled |           | |  driver disabled
       start bit      parity |  
                      stop bit   
Run Code Online (Sandbox Code Playgroud)

在此示例中,1假设启用偶校验,则传输数字(0x31 十六进制或 00110001 二进制)。

在 UART 上,每个传输的字节都由起始位(逻辑上的0)开始,然后数据以小端形式传输(即从最低有效位开始),并通过传输一个或多个停止位来结束,这是合乎逻辑的1

通常,接收器将空闲线视为1,并且从1到 的第一次转换0被视为起始位。

但是让我们看看当空闲线被视为逻辑时会发生什么0

000000000000   0 1000 1100 11 000000000000
(nothing,        ||^^ ^^^^ ^^ |
framing error)   ||   data    treated as stop 
 1 treated as idle|           
 0 treated as start
Run Code Online (Sandbox Code Playgroud)

在这种情况下,起始位(级别 0)被忽略,第一位1被视为先前传输的停止位。之后的第一位0被视为字节的起始位,接下来的 8 位被视为数据。

在我们的示例中,数据为 11001100,即 204。让我们看看其他示例

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                  

Run Code Online (Sandbox Code Playgroud)

尽管我的假设并不符合你的前半部分例子。例如,对于 1,我希望您获得数字 192,对于 5 - 193,除非您在禁用奇偶校验时获得这些数字:

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
Run Code Online (Sandbox Code Playgroud)

我的结论是:你必须检查从PC到MCU的RS-422线。它存在一些问题,使接收方认为0线路处于空闲状态时是合乎逻辑的。可能是偏置电阻连接错误。

而且,正如评论中所说,您在初始化方面遇到了问题:

UCSR0C |= (1<<UCSZ10)|(1<<UCSZ00)|(1<<USBS0)|(1<<UPM10);      
              ^^^^^^ has to be UCSZ01
Run Code Online (Sandbox Code Playgroud)

但是,由于您使用的|=是而不是简单的分配,并且UCSZ01bit 默认情况下是1,所以这也可能按预期工作。

还有循环

while ( !(UCSR0A & (1<<RXC0)) );
Run Code Online (Sandbox Code Playgroud)

不应该在中断处理程序中。RXC由于只有当设置位时才会触发中断,因此您可以假设 中有数据UDR,否则,此循环可能会永远挂起您的中断例程。