
저는 ATMEL 임베디드 컨트롤러(at91sam9x25e)와 직렬 포트를 사용하여 RS485 Modbus RTU를 통해 전송/수신하고 있습니다. 정상적인 통신은 잘 작동하지만 때로는 작성 시 일부 패키지가 메시지 내의 두 문자 사이에 약 3.5ms의 지연으로 분할되어 새 메시지로 감지됩니다.
설정:
- 모노 3.2.8
- Modbus RTU 19.2kBaud
- RS485
- 자동 RTS
전체 패키지를 한 번에 보냅니다.
try
{
Thread.Sleep(_timePreSend);
_serialPort.Write(buffer, offset, count);
} catch(Exception err)
{
log.Error(err.Message);
} finally
{
Thread.Sleep(2);
}
이전에는 RTS를 수동으로 활성화/비활성화할 때 2ms를 사용하여 버퍼(UART 또는 FIFO)에서 마지막 비트를 전송했습니다.
오실로스코프에서 캡처:
120318000100020032000403200000001C000500000000000000003D <- 간격 -> B6
stty의 출력:
stty -F /dev/ttyS4 -a
speed 19200 baud; rows 0; columns 0; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread clocal -crtscts
ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff
-iuclc -ixany -imaxbel -iutf8
-opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke
이 격차의 이유는 무엇입니까? 그것을 방지하려면 어떻게 해야 합니까?
답변1
이 문제의 원인이 무엇인지 정확히 말하기는 어렵습니다. 불행하게도 Linux 직렬 포트는 타이밍을 보장하지 않으며 일반 UART 프로토콜은 이와 같이 엄격한 타이밍 요구 사항을 갖지 않습니다. 프로토콜에 이러한 종류의 타이밍 보장이 필요한 경우 커널 드라이버를 자세히 살펴보고 간격 없이 모든 바이트의 원자적 전달을 보장하는 자체 드라이버를 작성해야 하며 아마도 인터럽트를 사용하여 UART 버퍼가 항상 적시에 리필됩니다. 커널의 도움 없이는 사용자 공간에서 이런 종류의 보장을 할 수 없습니다.
즉, 분할의 원인을 파악하고 해결 방법을 모색하고 실제로 목적에 맞게 작동할 만큼 충분히 신뢰할 수 있는지 경험적으로 판단할 수 있습니다. 지연은 항상 마지막 바이트 이전입니까? 그렇다면 스택의 무언가가 어떤 이유로든 문자를 버퍼링하고 있는 것처럼 들립니다. 아마도 여러 바이트를 하드웨어에 동시에 전달하고 작은 시간 초과 후에 단일 바이트만 전달하여 처리량을 최적화하기 위한 것일 수 있습니다. 먼저 사용자 공간 앱이 제대로 작동하는지 확인해야 합니다. 해당 앱을 실행하여 시스템 호출을 확인 strace
하고 단일 시스템 호출로 패킷이 커널에 기록되는지 확인하세요 write
. 그렇다면 이제 UART 드라이버의 커널 소스를 읽고 이상한 버퍼링이 관련되어 있는지 확인해야 합니다.
또 다른 가능성은 커널 드라이버가 단순히 커널 스레드(또는 프로세스 컨텍스트에서 직접)의 데이터를 처리하며, 여러분이 보고 있는 공백은 예약되는 또 다른 프로세스라는 것입니다. 그렇다면 이 문제를 해결하는 유일한 방법은 커널 드라이버를 수정하거나 직접 작성하여 다른 프로세스가 우선 순위를 갖도록 허용하지 않고 엄격한 실시간 방식으로 전송을 처리하도록 하는 것입니다(예: 인터럽트 컨텍스트에서 모든 작업 수행).
답변2
마침내이 행동의 문제가 무엇인지 알아 냈습니다. 통신 루프에서 바쁜 대기를 사용하여 CPU 부하가 많이 발생했습니다. 그때 나는 다음과 같은 SerialPort 명령을 사용한다는 것을 읽었습니다.BytesToRead 및 읽기전혀 이상적이지 않습니다. .NET System.IO.Port.Serialport를 사용해야 하는 경우
그래서 ByteStream의 비동기 함수를 사용해 보았습니다.ReadAsync 및 WriteAsync내 새 질문에 설명된 대로직렬 포트 및 ReadAsync에서 시간 초과를 사용하는 올바른 방법CPU 사용량이 훨씬 적고 더 이상 공백이 전혀 없습니다.