
Estoy usando un controlador integrado ATMEL (at91sam9x25e) y su puerto serie para enviar/recibir a través de RS485 Modbus RTU. La comunicación normal funciona bien, pero a veces, al escribir, algunos paquetes se dividen con un retraso de aproximadamente 3,5 ms entre dos caracteres dentro de un mensaje y, por lo tanto, se detectan como mensajes nuevos.
Ajustes:
- Mono 3.2.8
- Modbus RTU 19,2 kbaudios
- RS485
- RTS automático
Envío el paquete completo de una vez:
try
{
Thread.Sleep(_timePreSend);
_serialPort.Write(buffer, offset, count);
} catch(Exception err)
{
log.Error(err.Message);
} finally
{
Thread.Sleep(2);
}
Utilicé los 2 ms antes, cuando estaba habilitando/deshabilitando RTS manualmente, para que transmitiera los últimos bits del búfer (UART o FIFO).
Captura desde osciloscopio:
120318000100020032000403200000001C000500000000000000003D <- espacio -> B6
Salida de styt:
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
¿Cuál podría ser la razón de esta brecha? ¿Qué puedo hacer para evitarlo?
Respuesta1
Es difícil decir exactamente qué causa esto. Desafortunadamente, los puertos seriales de Linux no ofrecen garantías de sincronización, y no se supone que los protocolos UART normales tengan requisitos de sincronización tan estrictos como este. Si su protocolo requiere este tipo de garantía de sincronización, tendrá que profundizar en los controladores de su núcleo y tal vez escribir su propio controlador que garantice la entrega atómica de todos los bytes sin espacios, probablemente usando interrupciones para garantizar que el búfer UART esté siempre recargado de manera oportuna. No se puede ofrecer este tipo de garantía desde el espacio de usuario sin la ayuda del kernel.
Dicho esto, puede intentar descubrir qué causa su división y, tal vez, solucionarlo y determinar empíricamente que es lo suficientemente confiable en la práctica para funcionar para sus propósitos. ¿El retraso es siempre anterior al último byte? Si es así, parece que algo en la pila está almacenando en búfer un carácter por alguna razón, tal vez para optimizar el rendimiento al entregar varios bytes al hardware a la vez y solo entregar el único byte después de un pequeño tiempo de espera. Primero debe asegurarse de que su aplicación de espacio de usuario se esté comportando correctamente: verifique las llamadas al sistema ejecutándola strace
y asegúrese de que el paquete se escriba en el kernel en una sola write
llamada al sistema. Si es así, es hora de leer el código fuente del kernel de su controlador UART y ver si hay algún almacenamiento en búfer extraño involucrado.
Otra posibilidad es que el controlador del kernel simplemente trate con los datos en un subproceso del kernel (o directamente en el contexto de su proceso), y las brechas que está viendo sean otro proceso que se está programando. Si es así, la única forma de evitar esto es modificar el controlador del kernel o escribir el suyo propio, de modo que maneje la transmisión de manera estricta en tiempo real sin permitir que otros procesos tengan prioridad (por ejemplo, hacer todo en un contexto de interrupción).
Respuesta2
Finalmente descubrí cuál era el problema de este comportamiento. Usé algo de espera ocupada en mi bucle de comunicación, lo que provocó una gran carga de la CPU. Leí entonces que usar comandos SerialPort comoBytes para leer y leerno son ideales en absoluto: Si debe utilizar .NET System.IO.Port.Serialport
Por lo tanto, intenté usar funciones asincrónicas de ByteStream.LeerAsync y WriteAsynccomo se describe en mi nueva preguntaManera correcta de usar tiempos de espera con puerto serie y ReadAsyncy tengo mucho menos uso de la CPU y ya no tengo ningún hueco.