
Я использую встроенный контроллер ATMEL (at91sam9x25e) и его последовательный порт для отправки/приема по RS485 Modbus RTU. Обычная связь работает нормально, но иногда — при записи — некоторые пакеты разделяются с задержкой около 3,5 мс между двумя символами в сообщении и поэтому определяются как новое сообщение.
Настройки:
- Моно 3.2.8
- Modbus RTU 19,2 кбод
- RS485
- Авто-RTS
Я отправляю всю посылку сразу:
try
{
Thread.Sleep(_timePreSend);
_serialPort.Write(buffer, offset, count);
} catch(Exception err)
{
log.Error(err.Message);
} finally
{
Thread.Sleep(2);
}
Я использовал эти 2 мс ранее, когда включал/выключал RTS вручную, чтобы он передавал последние биты из буфера (UART или FIFO).
Захват с осциллографа:
120318000100020032000403200000001C0005000000000000000003D <- пробел -> 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
Я наконец выяснил, в чем проблема такого поведения. Я использовал некоторое активное ожидание в своем цикле связи, что привело к большой нагрузке на процессор. Я прочитал тогда, что использование команд SerialPort, таких какБайты для чтения и чтениясовсем не идеальны: Если вам необходимо использовать .NET System.IO.Port.Serialport
Поэтому я попробовал использовать асинхронные функции ByteStreamReadAsync и WriteAsyncкак описано в моем новом вопросеПравильный способ использования тайм-аутов с последовательным портом и ReadAsyncи у меня гораздо меньше загрузка процессора и вообще больше нет никаких пробелов.