Como a comunicação serial assíncrona está amplamente difundida entre os dispositivos eletrônicos até hoje em dia, acredito que muitos de nós já encontramos essa pergunta periodicamente. Considere um dispositivo eletrônico D
e um computador PC
conectado à linha serial (RS-232 ou similar) e necessário para trocar informações continuamente . Ou seja, PC
está enviando um quadro de comando cada X ms
e D
está respondendo com quadro de relatório / telemetria de status cada Y ms
(o relatório pode ser enviado como resposta a solicitações ou de forma independente - não importa realmente aqui). Os quadros de comunicação podem conter quaisquer dados binários arbitrários . Supondo que os quadros de comunicação sejam pacotes de comprimento fixo.
O problema:
Como o protocolo é contínuo, o lado receptor pode perder a sincronização ou simplesmente "ingressar" no meio de um quadro enviado em andamento; portanto, não saberá onde está o início do quadro (SOF). Se os dados tiverem um significado diferente, com base em sua posição em relação ao SOF, os dados recebidos serão corrompidos, potencialmente para sempre.
A solução necessária
Esquema confiável de delimitação / sincronização para detectar o SOF com pouco tempo de recuperação (isto é, não deve demorar mais do que, digamos, 1 quadro para ressincronizar).
As técnicas existentes que conheço (e utilizo algumas) de:
1) Cabeçalho / soma de verificação - SOF como valor predefinido de bytes. Soma de verificação no final do quadro.
- Prós: Simples.
- Contras: Não confiável. Tempo de recuperação desconhecido.
2) Byte stuffing:
- Prós: recuperação rápida e confiável, pode ser usada com qualquer hardware
- Contras: Não é adequado para comunicação baseada em quadro de tamanho fixo
3) marcação do 9º bit - adicione cada byte com um bit adicional, enquanto SOF marcado com 1
e bytes de dados são marcados com 0
:
- Prós: Recuperação confiável e rápida
- Contras: Requer suporte de hardware. Não é suportado diretamente pela maioria do
PC
hardware e software.
4) Marcação do 8º bit - tipo de emulação dos itens acima, enquanto utiliza o 8º bit em vez do 9º, deixando apenas 7 bits para cada palavra de dados.
- Prós: recuperação rápida e confiável, pode ser usada com qualquer hardware.
- Contras: Requer um esquema de codificação / decodificação de / para a representação convencional de 8 bits para / da representação de 7 bits. Um pouco desperdício.
5) Baseado em tempo limite - assuma o SOF como o primeiro byte vindo após algum tempo ocioso definido.
- Prós: Sem sobrecarga de dados, simples.
- Contras: Não é tão confiável. Não funcionará bem com sistemas de tempo ruim como, por exemplo, Windows PC. Sobrecarga potencial de sobrecarga.
Pergunta: Quais são as outras possíveis técnicas / soluções para resolver o problema? Você pode apontar para os contras na lista acima, que podem ser facilmente contornados, removendo-os? Como você (ou você) projetou seu protocolo de sistemas?
fonte
Respostas:
Na minha experiência, todo mundo gasta muito mais tempo depurando sistemas de comunicação do que jamais esperava. Por isso, sugiro enfaticamente que sempre que você precisar fazer uma escolha para um protocolo de comunicação, escolha qualquer opção que facilite a depuração do sistema, se possível.
Convido você a criar alguns protocolos personalizados - é divertido e muito educativo. No entanto, também encorajo você a olhar para os protocolos pré-existentes. Se eu precisasse comunicar dados de um lugar para outro, tentaria muito usar algum protocolo preexistente que outra pessoa já gastou muito tempo depurando.
É muito provável que escrever seu próprio protocolo de comunicação do zero contra muitos dos mesmos problemas comuns que todos têm quando escrevem um novo protocolo.
Há uma dúzia de protocolos de sistema incorporado listados em Bons protocolos baseados em RS232 para comunicação incorporada ao computador - qual é o mais próximo dos seus requisitos?
Mesmo que alguma circunstância impossibilitasse o uso exato de qualquer protocolo pré-existente, é mais provável que algo funcione mais rapidamente, iniciando com um protocolo que quase atenda aos requisitos e depois ajustando-o.
más notícias
Como eu disse antes :
Infelizmente, é impossível para qualquer protocolo de comunicação ter todos esses recursos interessantes:
Eu ficaria surpreso e encantado se houvesse alguma maneira de um protocolo de comunicação ter todos esses recursos.
boas notícias
Muitas vezes, torna a depuração muito, muito mais fácil se um humano em um terminal de texto puder substituir qualquer um dos dispositivos de comunicação. Isso requer que o protocolo seja projetado para ser relativamente independente do tempo (não atinge o tempo limite durante as pausas relativamente longas entre as teclas digitadas por um ser humano). Além disso, esses protocolos são limitados aos tipos de bytes fáceis para um ser humano digitar e depois ler na tela.
Alguns protocolos permitem que as mensagens sejam enviadas no modo "texto" ou "binário" (e exigem que todas as mensagens binárias possíveis tenham alguma mensagem de texto "equivalente" que significa a mesma coisa). Isso pode ajudar a tornar a depuração muito mais fácil.
Algumas pessoas parecem pensar que limitar um protocolo para usar apenas os caracteres imprimíveis é "um desperdício", mas a economia no tempo de depuração costuma valer a pena.
Como você já mencionou, se você permitir que o campo de dados contenha qualquer byte arbitrário, incluindo os bytes de início do cabeçalho e final do cabeçalho, quando um receptor for ligado pela primeira vez, é provável que o receptor sincronize incorretamente. o que parece um byte de início de cabeçalho (SOH) no campo de dados no meio de um pacote. Normalmente, o receptor obtém uma soma de verificação incompatível no final desse pseudo-pacote (que normalmente está na metade de um segundo pacote real). É muito tentador simplesmente descartar toda a pseudo-mensagem (incluindo a primeira metade desse segundo pacote) antes de procurar o próximo SOH - com a conseqüência de que o receptor pode ficar fora de sincronia para muitas mensagens.
Como alex.forencich apontou, uma abordagem muito melhor é que o receptor descarte bytes no início do buffer até o próximo SOH. Isso permite que o receptor (depois de trabalhar com vários bytes de SOH nesse pacote de dados) sincronize imediatamente no segundo pacote.
Como Nicholas Clark apontou, o COBS ( Overhead Overhead Byte ) tem uma sobrecarga fixa que funciona bem com quadros de tamanho fixo.
Uma técnica que geralmente é ignorada é um byte de marcador de fim de quadro dedicado. Quando o receptor é ligado no meio de uma transmissão, um byte marcador de fim de quadro dedicado ajuda o receptor a sincronizar mais rapidamente.
Quando um receptor é ativado no meio de um pacote, e o campo de dados de um pacote contém bytes que parecem ser um começo de pacote (o início de um pseudo-pacote), o transmissor pode inserir uma série de bytes de marcador de fim de quadro após esse pacote, para que esses bytes de pseudo-início de pacote no campo de dados não interfiram na sincronização imediata e na decodificação correta do próximo pacote - mesmo quando você é extremamente azarado e a soma de verificação desse pseudo-pacote parece correto.
Boa sorte.
fonte
Os esquemas de preenchimento de bytes funcionaram muito bem para mim ao longo dos anos. Eles são legais porque são fáceis de implementar em software ou hardware, você pode usar um cabo USB para UART padrão para enviar pacotes de dados e garantir um enquadramento de boa qualidade sem se preocupar com tempos limite, troca a quente ou qualquer outra coisa assim.
Eu defenderia um método de preenchimento de bytes combinado com um byte de comprimento (módulo 256 de tamanho de pacote) e um CRC no nível de pacote e, em seguida, usaria o UART com um bit de paridade. O byte de comprimento garante a detecção de bytes descartados, o que funciona bem com o bit de paridade (porque a maioria dos UARTs eliminará quaisquer bytes que falhem na paridade). Em seguida, o CRC no nível do pacote oferece segurança extra.
Quanto à sobrecarga do byte-stuffing, você analisou o protocolo COBS? É uma maneira genial de fazer byte-stuffing com uma sobrecarga fixa de 1 byte a cada 254 transmitidos (mais seu enquadramento, CRC, LEN, etc.).
https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
fonte
Sua opção nº 1, SOH mais soma de verificação, é confiável e se recupera no próximo quadro não corrompido.
Suponho que você já saiba o tamanho de uma mensagem ou o comprimento seja codificado no (s) byte (s) imediatamente após o SOH. O byte de verificação aparece no final da mensagem. Você também precisa de um buffer do lado do recebimento para os dados que contenham pelo menos a duração da sua mensagem mais longa.
Toda vez que você vê um byte SOH no início do buffer, é potencialmente o início de uma mensagem. Você varre o buffer para calcular o valor de verificação para essa mensagem e verifica se ele corresponde aos bytes de verificação no buffer. Se sim, você terminou; caso contrário, você descarta os dados do buffer até chegar ao próximo byte SOH.
Observe que, se uma mensagem realmente tiver erros de dados, esse algoritmo a descartará - mas você provavelmente faria isso de qualquer maneira. Se o seu algoritmo de verificação incluir a correção direta de erros, você poderá verificar se há erros corrigíveis em cada alinhamento potencial de mensagens.
Se as mensagens tiverem um comprimento fixo, você poderá dispensar completamente o byte SOH - basta testar TODAS as possíveis posições iniciais para obter um valor de verificação válido.
Você também pode dispensar o algoritmo de verificação e manter apenas o byte SOH, mas isso torna o algoritmo menos determinístico. A ideia é que, para alinhamentos de mensagens válidos, o SOH sempre apareça no início de uma mensagem. Se você tiver um alinhamento incorreto, é improvável que o próximo byte no fluxo de dados seja outro SOH (depende da frequência com que o SOH aparece nos dados da mensagem). Você pode selecionar os bytes SOH válidos somente com base nisso. (É basicamente assim que funciona o enquadramento em serviços de telecomunicações síncronos como T1 e E1.)
fonte
Uma opção não mencionada, mas amplamente utilizada (especialmente na Internet) é a codificação ASCII / texto (na verdade, a maioria das implementações modernas assume UTF-8). Na minha experiência, os profissionais de hardware detestam fazer isso, mas as pessoas de software tendem a preferir isso a quase qualquer outra coisa (principalmente para a tradição do Unix de fazer tudo baseado em texto).
A vantagem da codificação de texto é que você pode usar caracteres não imprimíveis para enquadrar. Por exemplo, o mais simples seria usar algo como
0x00
para indicar o início do quadro e o0xff
fim do quadro.Vi duas maneiras principais de codificar os dados como texto:
Quando um funcionário de hardware / montagem é solicitado a fazer isso, provavelmente será implementado como codificação hexadecimal. Isso é simplesmente converter os bytes em seus valores hexadecimais em ASCII. A sobrecarga é grande. Basicamente, você transmitirá dois bytes para cada byte de dados real.
Quando um software é solicitado a fazer isso, provavelmente será implementado como codificação base64. Essa é a codificação de fato da internet. Usado para tudo, desde anexos MIME de email a codificação de dados de URL. A sobrecarga é exatamente 33%. Muito melhor do que a simples codificação hexadecimal.
Como alternativa, você pode abandonar completamente os dados binários e transmitir texto. Nesse caso, a técnica mais comum é delimitar dados com nova linha (apenas
"\n"
ou"\r\n"
). Os comandos NMEA (GPS), Modem AT e sensores Adventech ADAM são alguns dos exemplos mais comuns disso.Todos esses protocolos / enquadramentos baseados em texto têm os seguintes prós e contras:
Pró:
Vigarista:
"0"
(0x30) em vez de quatro bytes 0x00000000)sprintf()
função)Pessoalmente, para mim, os profissionais superam os contras. A facilidade de depuração sozinha conta como 5 pontos (portanto, esse ponto único já supera os dois contras).
Depois, existem soluções não tão cuidadosamente pensadas, muitas vezes provenientes de profissionais de software: envie dados codificados sem pensar em enquadramento.
Eu tive que interagir com o hardware que enviou XML bruto no passado. O XML era todo o enquadramento existente. Felizmente, é bastante fácil descobrir os limites dos quadros pelas
<xml></xml>
tags. O grande problema para mim é que ele usa mais de um byte para o enquadramento. Além disso, o próprio enquadramento pode não ser corrigido, pois a tag pode conter atributos:<tag foo="bar"></tag>
portanto, você precisará armazenar em buffer no pior caso para encontrar o início do quadro.Recentemente, vi pessoas começarem a enviar JSON a partir de portas seriais. Com o JSON, o enquadramento é, na melhor das hipóteses, um palpite. Você só tem o caractere
"{"
(ou"["
) para detectar o quadro, mas eles também estão contidos nos dados. Então você acaba precisando de um analisador de descida recursiva (ou pelo menos um contador de chaves) para descobrir o quadro. Pelo menos, é trivial saber se o quadro atual termina prematuramente:"}{"
ou"]["
é ilegal no JSON e, portanto, indica que o quadro antigo foi encerrado e um novo quadro foi iniciado.fonte
<
e>
como delimitadores e acredito que o email usa novas linhas. Nota: sim, o email é um formato com estrutura adequada que pode ser transmitido via RS232 um amigo meu costumava correr um servidor de distribuição de correio para sua casa usando RS232).O que você descreve como "Xth bit marking" pode ser generalizado em outros códigos que têm a propriedade de expandir os dados por uma fração constante, deixando algumas palavras de código livres para serem usadas como delimitadores. Muitas vezes, esses códigos também oferecem outros benefícios; Os CDs usam de oito a catorze modulações , o que garante um comprimento máximo de execução de 0 bits entre cada um. Outros exemplos incluem códigos de bloco de Correção de Erro de Encaminhamento , que usam bits adicionais para codificar também informações de detecção e correção de erros.
Outro sistema que você não mencionou é o uso de informações fora da banda, como uma linha de seleção de chips, para delimitar transações ou pacotes.
fonte
Outra opção é o que é conhecido como codificação de linha . A codificação de linha fornece ao sinal certas características elétricas que facilitam a transmissão (garantias de comprimento máximo de operação balanceadas e CC) e suportam caracteres de controle para enquadramento e sincronização do relógio. Os códigos de linha são usados em todos os protocolos seriais de alta velocidade modernos - Ethernet 10M, 100M, 1G e 10G, ATA serial, FireWire, USB 3, PCIe etc. Os códigos de linha comuns são 8b / 10b , 64b / 66b e 128b / 130b. Também existem códigos de linha mais simples que não fornecem informações de enquadramento, apenas o balanço DC e a sincronização do relógio. Exemplos destes são Machester e NRZ. Você provavelmente deseja usar 8b / 10b se quiser sincronizar rapidamente; os outros códigos de linha não foram projetados para sincronizar com pressa. O uso de um código de linha como o que foi oferecido acima exigirá o uso de hardware personalizado para transmitir e receber.
Quanto à sua opção 5, o serial RS232 padrão deve suportar o envio e o recebimento de intervalos onde a linha fica ociosa por alguns bytes. No entanto, isso pode não ser suportado em todos os sistemas.
Geralmente, o método de enquadramento mais simples e confiável é a sua opção 1, em combinação com um campo de comprimento e rotina simples de CRC ou soma de verificação. A rotina de decodificação é simples: descarte os bytes até obter um byte inicial, leia o campo length, aguarde o quadro inteiro, verifique a soma de verificação e mantenha-a em boas condições. Se a soma de verificação estiver incorreta, comece a descartar bytes do buffer até obter um byte de início e repita. O principal problema dessa técnica é encontrar um início de byte de quadro que, na verdade, não é o início de um byte de quadro. Para aliviar esse problema, uma técnica é escapar bytes com o mesmo valor que o início do byte de quadro com outro caractere de controle e alterar o byte de escape para que ele tenha um valor diferente. Nesse caso, você também precisará fazer a mesma coisa com o novo byte de controle.
fonte