USART错误数据传入

5

我正在编写 Atmega162PC 之间的通信。

在我的 PCB 上,我有一个 RS485 接口(通过 MAX485RS422 转换),并通过 ADAM-4520 转换器连接到 COM端口

我一直在终端中测试我的程序,但收到的来自 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

我已经尝试了几种传输参数,但似乎并没有帮助。这里是源代码:

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 可以在发送和接收之间切换。


1
您可能将奇偶校验位或数据位数设置错误。 - Paul R
那是我一开始所做的。 - pasiasty
1
你使用的测试值对于诊断问题并不是很有帮助,它们打开了太多的位,看起来你发送了超过一个字节的数据。但是看起来你把正负极线接反了,RS485使用差分信号,这会导致位值被反转,数据位被误解为起始位。此外,非常重要的是你需要改进错误检查,因为在这里你肯定会遇到帧错误。 - Hans Passant
1
不,我不是在说交换TxD和RxD信号,我在说信号的极性。每个信号都有两根线,RxD的两根线是反向的。请使用electronics.stackexchange.com来询问有关RS-485正确布线的问题。 - Hans Passant
3
在ISR中,while (!(UCSR0A & (1 << RXC0)))是不必要的。必须设置RCX0标志才能运行ISR。在while (!(UCSR0A & (1 << TXC0)))之后似乎漏了一个;。你改变PORTD的时间太早了。 - user66363
显示剩余10条评论
1个回答

0

看起来你的设置中空闲线被错误地处理了。

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

当启用发射器驱动程序并且正在传输0或1时,发射器会将线路分开,使它们之间的差分电压超过200mV,带有一个符号或另一个符号。

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

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

(idle line)  | |  (data)   | |  |  (idle again)
driver enabled |           | |  driver disabled
       start bit      parity |  
                      stop bit   

在这个例子中,数字1(0x31十六进制或00110001二进制)正在传输,假设启用了偶校验。
在UART上,每个传输的字节都以起始位(逻辑0)开始,然后以小端形式传输数据(即从最低有效位开始),并通过传输一个或多个停止位来完成,这是逻辑1
通常,空闲线被接收器视为1,并且从10的第一次转换被视为起始位。
但是,让我们看看当空闲线被视为逻辑0时会发生什么?
000000000000   0 1000 1100 11 000000000000
(nothing,        ||^^ ^^^^ ^^ |
framing error)   ||   data    treated as stop 
 1 treated as idle|           
 0 treated as start

在这种情况下,起始位(电平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                  

虽然我的假设在您的示例的前半部分不成立。例如,对于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

我的结论:您需要检查从PC到MCU的RS-422线路。它存在一些问题,使接收器在线路处于空闲状态时认为存在逻辑0。可能是由于偏置电阻连接方式不正确。

另外,正如评论中所说,您还存在初始化问题:

UCSR0C |= (1<<UCSZ10)|(1<<UCSZ00)|(1<<USBS0)|(1<<UPM10);      
              ^^^^^^ has to be UCSZ01

但是,由于您使用的是|=而不是简单赋值,并且UCSZ01位默认为1,因此这也可能按预期工作。

同时循环

while ( !(UCSR0A & (1<<RXC0)) );

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接