Pergunta

Estou enviando/recebendo dados sobre uma linha de série no Linux e gostaria de encontrar o atraso entre os caracteres.

O Modbus usa um atraso de 3,5 caracteres para detectar limites do quadro de mensagens. Se houver mais de um atraso de 1,5 caractere, o quadro da mensagem será declarado incompleto.

Estou escrevendo um programa rápido em C, que é basicamente

fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
// setup newtio
....
tcsetattr(fd, TCSANOW, &newtio);
for(;;) {
    res = read(fs, buf, 1);
    if (res > 0) {
        // store time in milliseconds?
        //do stuff
    }
}

Existe alguma maneira de medir o tempo aqui? Ou preciso procurar recuperar dados da linha serial de uma maneira diferente?

Também tentei conectar a Sigio para obter um sinal sempre que houver dados, mas pareço obter dados de 8 bytes por vez.

(Sim, eu sei que existem algumas bibliotecas Modbus, mas quero usar isso em outros aplicativos)

Foi útil?

Solução

O Modbus é como muitos protocolos antigos e realmente odeia o hardware moderno.

A razão pela qual você está recebendo 8 bytes de cada vez é: seu PC tem (pelo menos) 16 bytes fIFO serial em receber e transmitir, no hardware. A maioria é 64byte ou maior.

Isto é É possível informar ao dispositivo UART para interromper e emitir uma interrupção recebida após vários tempos de char.

O nível de gatilho é ajustável, mas o driver de baixo nível o define "inteligente". tente o modo de baixa latência usando o setSerial) Você pode mexer com o código no driver serial, se necessário. Google It (aviso de conteúdo maduro), não é bonito.

Portanto, a rotina é como pseudocódigo

int actual=read (packet, timeout of 1.5 chars)

look at actual # of received bytes

if less than a packet, has issues, discard.

nada bom.

Outras dicas

A resposta simples é ... você não pode (não sem escrever seu próprio motorista em série)!

Se você está escrevendo um modbus mestre Há alguma esperança: você pode detectar o fim de uma resposta de escravo esperando algum quantidade de tempo (desde que mais de 3,5 chars) sem receber nada (selecione (2) pode ajudá -lo aqui) ou analisando a resposta em tempo real, ao ler (o segundo método desperdiça muito menos tempo). Você também deve ter cuidado para esperar pelo menos 3,5 caracteres-tempo antes de olhar para transmitir uma nova solicitação, depois de receber a resposta à solicitação anterior. "Pelo menos" está em operação aqui! Esperar mais não importa. Esperar menos faz.

Se você escreve um modbus escravo Então você é sem sorte. Você simplesmente não podes faça isso confiável do userspace linux. Você tem que escrever seu próprio motorista em série.

BTW, isso não é culpa do Linux. Isso se deve à estupidez inacreditável do método de enquadramento de Modbus.

Eu acho que você está fazendo isso da maneira errada. Há um mecanismo embutido para garantir que os personagens entrem em todos juntos.

Basicamente, você vai querer usar ioctl() e defina o VMIN e VTIME parâmetros adequadamente. Nesse caso, parece que você deseja VMIN (número mínimo de caracteres em um pacote) para ser 0 e VTIME (quantidade mínima de tempo permitida entre os personagens ser 15 (Eles são décimos de segundos).

Algum verdade Código de exemplo básico:

struct termio t;
t.c_cc[ VMIN ] = 0;
t.c_cc[ VTIME ] = 15;
if (ioctl( fd, TCSETAW, &t ) == -1)
{
    printf( msg, "ioctl(set) failed on port %s. Exiting...", yourPort);
    exit( 1 );
}

Faça isso antes do seu open() mas antes do seu read(). Aqui estão alguns links que achei muito úteis:

Guia de programação em série

Compreendendo VMIN e Vmax

Espero que pelo menos ajude/aponte você na direção certa, mesmo que não seja uma resposta perfeita para sua pergunta.

Você não pode usar tempo limite. Em taxas de transmissão mais altas, o tempo limite de 3,5 caracteres significa alguns milissegundos, ou mesmo centenas de microssegundos. Esses tempos limite não podem ser tratados no espaço do usuário do Linux.

No lado do cliente, não é grande coisa, pois o Modbus não envia mensagens assíncronas. Portanto, cabe a você não enviar 2 mensagens consecutivas dentro de 3,5 caracteres.

No lado do servidor, o problema é que, se seus clientes tiverem um tempo limite de resposta extremamente curto e o Linux estiver muito ocupado, você não poderá escrever uma solução de enquadramento à prova de balas. Há uma chance de que a função read () retorne mais de um pacote. Aqui está um exemplo (um pouco artificial).

  1. O cliente grava um pacote no servidor. O tempo limite é digamos 20 ms.

  2. Digamos que o Linux está no momento muito ocupado, para que o kernel não acorde seu tópico nos próximos 50 ms.

  3. Depois de 20 MS, o cliente detecta que não recebeu nenhuma resposta, por isso envia outro pacote para o servidor (talvez se ressente do anterior).

  4. Se o Linux acorda seu tópico de leitura após 50 ms, a função Read () pode obter 2 pacotes ou até 1 e meio, dependendo de quantos bytes foram recebidos pelo driver de porta serial.

Na minha implementação, uso um método simples que tenta analisar bytes on-the-fly-primeiro detectando o código da função e depois tento ler todos os bytes restantes para uma função específica. Se eu conseguir um pacote e meio, analisarei apenas o primeiro e os bytes restantes são deixados no buffer. Se mais bytes chegarem a um curto tempo limite, eu os adiciono e tento analisar, caso contrário, descarto -os. Não é uma solução perfeita (por exemplo, alguns sub-código para a Função 8 não possuem um tamanho fixo), mas como o Modbus RTU não possui caracteres STX ETX, é o melhor que pude descobrir.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top