看起来你的设置中空闲线被错误地处理了。
让我们看看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
,并且从
1
到
0
的第一次转换被视为起始位。
但是,让我们看看当空闲线被视为逻辑
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
中有数据,否则,此循环可能会永远挂起您的中断例程。
while (!(UCSR0A & (1 << RXC0)))
是不必要的。必须设置RCX0标志才能运行ISR。在while (!(UCSR0A & (1 << TXC0)))
之后似乎漏了一个;
。你改变PORTD的时间太早了。 - user66363