
Estou usando um controlador incorporado ATMEL (at91sam9x25e) e sua porta serial para enviar/receber via RS485 Modbus RTU. A comunicação normal funciona bem, mas às vezes - durante a escrita - alguns pacotes são divididos com um atraso de cerca de 3,5ms entre dois caracteres dentro de uma mensagem e são, portanto, detectados como uma nova mensagem.
Configurações:
- Mono 3.2.8
- Modbus RTU 19,2 kBaud
- RS485
- RTS automático
Estou enviando o pacote inteiro de uma vez:
try
{
Thread.Sleep(_timePreSend);
_serialPort.Write(buffer, offset, count);
} catch(Exception err)
{
log.Error(err.Message);
} finally
{
Thread.Sleep(2);
}
Usei os 2 ms antes, quando estava habilitando/desabilitando o RTS manualmente, para que ele transmitisse os últimos bits do buffer (UART ou FIFO).
Captura do osciloscópio:
120318000100020032000403200000001C00050000000000000003D <- lacuna -> B6
Saída do 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
Qual poderia ser a razão dessa lacuna? O que posso fazer para evitá-lo?
Responder1
É difícil dizer exatamente o que causa isso. Infelizmente, as portas seriais do Linux não oferecem garantias de tempo, e os protocolos UART normais não devem ter requisitos de tempo rígidos como este. Se o seu protocolo exigir esse tipo de garantia de tempo, você terá que se aprofundar nos drivers do kernel e talvez escrever seu próprio driver que garanta a entrega atômica de todos os bytes sem lacunas, provavelmente usando interrupções para garantir que o buffer UART seja sempre reabastecido em tempo hábil. Você não pode fazer esse tipo de garantia no espaço do usuário sem a ajuda do kernel.
Dito isso, você pode tentar descobrir o que causa sua divisão e, talvez, contorná-la e determinar empiricamente se é confiável o suficiente na prática para funcionar para seus propósitos. O atraso é sempre antes do último byte? Nesse caso, parece que algo na pilha está armazenando um caractere em buffer por algum motivo, talvez para otimizar o rendimento, entregando vários bytes ao hardware de uma só vez e entregando apenas o byte solitário após um pequeno tempo limite. Você deve primeiro garantir que seu aplicativo de espaço de usuário esteja se comportando corretamente: verifique as chamadas do sistema executando-o strace
e certifique-se de que o pacote seja gravado no kernel em uma única write
chamada do sistema. Se for, é hora de ler a fonte do kernel do seu driver UART e ver se há algum buffer estranho envolvido.
Outra possibilidade é que o driver do kernel simplesmente lide com os dados em um thread do kernel (ou diretamente no contexto do seu processo), e as lacunas que você está vendo sejam outro processo sendo agendado. Nesse caso, a única maneira de contornar isso é modificar o driver do kernel ou escrever o seu próprio, de modo que ele lide com a transmissão estritamente em tempo real, sem permitir que outros processos tenham prioridade (por exemplo, fazendo tudo no contexto de interrupção).
Responder2
Finalmente descobri qual era o problema desse comportamento. Usei uma espera ocupada em meu loop de comunicação, o que levou a uma carga pesada da CPU. Eu li então que usar comandos SerialPort comoBytesToRead e Lernão são nada ideais: Se você deve usar .NET System.IO.Port.Serialport
Portanto tentei usar funções assíncronas de ByteStreamReadAsync e WriteAsyncconforme descrito na minha nova perguntaManeira correta de usar tempos limite com porta serial e ReadAsynce tenho muito menos uso de CPU e nenhuma lacuna.