Técnicas de delimitação / sincronização de protocolo serial


24

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 De um computador PCconectado à linha serial (RS-232 ou similar) e necessário para trocar informações continuamente . Ou seja, PCestá enviando um quadro de comando cada X mse Destá 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 1e 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 PChardware 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?

serial  communication  protocol  brushless-dc-motor  hall-effect  hdd  scr  flipflop  state-machines  pic  c  uart  gps  arduino  gsm  microcontroller  can  resonance  memory  microprocessor  verilog  modelsim  transistors  relay  voltage-regulator  switch-mode-power-supply  resistance  bluetooth  emc  fcc  microcontroller  atmel  flash  microcontroller  pic  c  stm32  interrupts  freertos  oscilloscope  arduino  esp8266  pcb-assembly  microcontroller  uart  level  arduino  transistors  amplifier  audio  transistors  diodes  spice  ltspice  schmitt-trigger  voltage  digital-logic  microprocessor  clock-speed  overclocking  filter  passive-networks  arduino  mosfet  control  12v  switching  temperature  light  luminous-flux  photometry  circuit-analysis  integrated-circuit  memory  pwm  simulation  behavioral-source  usb  serial  rs232  converter  diy  energia  diodes  7segmentdisplay  keypad  pcb-design  schematics  fuses  fuse-holders  radio  transmitter  power-supply  voltage  multimeter  tools  control  servo  avr  adc  uc3  identification  wire  port  not-gate  dc-motor  microcontroller  c  spi  voltage-regulator  microcontroller  sensor  c  i2c  conversion  microcontroller  low-battery  arduino  resistors  voltage-divider  lipo  pic  microchip  gpio  remappable-pins  peripheral-pin-select  soldering  flux  cleaning  sampling  filter  noise  computers  interference  power-supply  switch-mode-power-supply  efficiency  lm78xx 

4 é de apenas 1 / 8th mais desperdício do que 3.
Nick Johnson

@NickJohnson Concordo, mas está apenas sugerindo que eu adicione a coisa "Desperdício" em (3) também :)
Eugene Sh.

Eu não acho que você tenha explicado completamente suas suposições sobre erros de comunicação. Você está assumindo que a comunicação é 'perfeita', ou seja, sem erros, ou 'suficientemente perfeita' para que todos os erros sejam detectados e identificados pelo hardware de comunicação (por exemplo, as comunicações usam paridade e são apenas erros de bit único)?
precisa saber é o seguinte

O receptor pode se unir no meio de um byte e pode interpretar o bit 8 como o bit 4, por exemplo. A marcação do 9º bit não é, portanto, confiável.
Timothy Baldwin

@gbulmer A suposição original é que o canal é perfeito e o problema pode surgir apenas devido à sincronização inicial. Sob essas suposições, a "confiabilidade" à qual me referi está relacionada apenas à ressincronização. Na lista acima, todas essas técnicas garantem 100% de sucesso, exceto a primeira. Mas provavelmente o esquema de verificação de erros e o enquadramento não devem ser separados assim.
Eugene Sh.

Respostas:


15

Como você (ou você) projetou seu protocolo de sistemas?

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:

  • transparência: a comunicação de dados é transparente e "limpa em 8 bits" - (a) qualquer arquivo de dados possível pode ser transmitido, (b) sequências de bytes no arquivo sempre tratadas como dados e nunca interpretadas erroneamente como outra coisa; e (c) ) o destino recebe o arquivo de dados inteiro sem erros, sem acréscimos ou exclusões.
  • cópia simples: a formação de pacotes é mais fácil se simplesmente copiarmos cegamente os dados da fonte para o campo de dados do pacote sem alterações.
  • início exclusivo: o símbolo de início de pacote é fácil de reconhecer, porque é um byte constante conhecido que nunca ocorre em nenhum outro lugar nos cabeçalhos, no cabeçalho CRC, na carga útil dos dados ou nos dados CRC.
  • 8 bits: usa apenas bytes de 8 bits.

Eu ficaria surpreso e encantado se houvesse alguma maneira de um protocolo de comunicação ter todos esses recursos.

boas notícias

Quais são as outras possíveis técnicas / soluções para resolver o problema?

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.

Você pode apontar para os contras na lista acima, que podem ser facilmente contornados, removendo-os?

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.


Vale a pena reconsiderar esta resposta anteriormente aceita (desculpe, @DaveTweed), e o artigo vinculado é certamente uma discussão sobre o assunto. Obrigado por dedicar um tempo e escrever.
Eugene Sh.

1
bom que você apontar ESPIGAS, então eu não tenho que escrever uma resposta :-)
Nils Pipenbrinck

11

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


Essa é uma excelente maneira de evitar a explosão de bytes em 2x nos dados, na pior das hipóteses. Eu usei esquemas semelhantes, mas mais específicos de aplicativos, mas é ótimo ver isso descrito de uma maneira padrão. Vou uso ESPIGAS de agora em diante ...
WJL

1
Agradeço também por apontar o COBS - um pequeno algoritmo muito interessante.
Nick Johnson.

6

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.)


Eu acho que a confiabilidade é um pouco probabilística? Dependendo da força do código de verificação de erros / correção que pode encontrar quadros que parecem correto em um fluxo de bytes aleatório / arbitrária.
Eugene Sh.

Claro, isso é possível. Mas, na prática, é relativamente fácil escolher um algoritmo de verificação suficientemente forte.
Dave Tweed

Se você tem uma taxa diferente de zero de erros de dados, sempre há uma chance diferente de zero de aceitar uma mensagem inválida.
Nick Johnson

@NickJohnson Assumindo um canal perfeitamente limpo, ainda haverá (teoricamente) incompatibilidades com essa abordagem. Claro que a probabilidade deles pode ser insignificante.
Eugene Sh.

1
Eu sei que você já sabe disso, e já o mencionou de passagem, mas a versão em que você não armazena em buffer uma mensagem inteira ou simplesmente é preguiçosa sobre como decodificar é menos confiável. Se você ressincronizar no próximo byte SOH após a soma de verificação incompatível, em vez do próximo byte SOH após o SOH "falso", terá uma chance muito boa de descartar o início da mensagem real e permanecer fora de sincronia para muitas mensagens ou, no pior caso, para sempre.
quer

5

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 0x00para indicar o início do quadro e o 0xfffim do quadro.

Vi duas maneiras principais de codificar os dados como texto:

  1. 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.

  2. 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ó:

  • Fácil de depurar
  • Fácil de implementar em uma linguagem de script
  • O hardware pode ser simplesmente testado usando Hyperterminal / minicom
  • Fácil de implementar no hardware (a menos que seja um micro realmente pequeno como um PIC)
  • Pode ser um quadro de tamanho fixo ou tamanho variável.
  • Enquadramento previsível e tempo de recuperação rápido de sincronização (recupera no final do quadro atual)

Vigarista:

  • Sobrecarga muito grande em comparação à transmissão binária pura (então, a E / S de texto também pode "compactar" números como o envio de um byte "0"(0x30) em vez de quatro bytes 0x00000000)
  • Não é tão fácil de implementar em micros muito pequenos como o PIC (a menos que sua biblioteca inclua uma 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.


Para codificações de texto, há também o base85 , que possui apenas 25% de sobrecarga em vez de 33%.
Dave Tweed

Eu consideraria um subconjunto / variação do quarto método.
Eugene Sh.

@EugeneSh .: Tecnicamente, é um subconjunto de bytestuffing. Por outro lado, como você considera um subconjunto de marcação de bits, é possível entender por que essa ambiguidade faz dela uma categoria por si só. Além disso, você não pode considerar a maioria das implementações de codificação de texto como um subconjunto de marcação de bits, porque os bits de marcação nunca são usados ​​(por exemplo, eu geralmente uso <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).
slebetman

4

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.


Os códigos de correção de erros estão um pouco à parte da questão. Eles devem ser adicionados a qualquer um desses esquemas de qualquer maneira. As "informações fora da banda" a que você está se referindo são iguais a "controle de fluxo de hardware", eu acho?
Eugene Sh.

@EugeneSh. - Na verdade, o uso de bits de verificação de erro para o enquadramento é perfeitamente válido, embora computacionalmente caro no lado de recebimento. Você simplesmente faz o cálculo do erro para cada alinhamento de dados possível, e o que obtém êxito é um alinhamento válido em um quadro não corrompido. Obviamente, se o quadro estiver corrompido, você não o encontrará.
Dave Tweed

@DaveTweed Bem, é praticamente o que eu quis dizer com a primeira técnica. Ou estou te entendendo mal?
Eugene Sh.

Não, você não está entendendo mal; era disso que eu estava falando. No entanto, seu "golpe" está errado - é confiável e também pode ser robusto com relação aos erros de transmissão reais.
Dave Tweed

@DaveTweed E o tempo de recuperação? Você tem algum exemplo de como ele pode ser robusto?
Eugene Sh.

3

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.


É o mesmo que a resposta de Nick Johnson.
Dave Tweed
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.