Na minha comunicação UART, preciso conhecer o byte inicial e o byte final da mensagem enviada. O byte inicial é fácil, mas o byte final, nem tanto. Eu implementei dois bytes de parada no final da minha mensagem, ou seja, \ n e \ r (10 e 13 decimais). O UART funciona apenas com valores de bytes de 0 a 255, então, qual é a segurança contra falhas? Posso imaginar, embora com baixa probabilidade, que minha mensagem possa conter os valores "10 e 13" um após o outro, quando não forem os bytes de parada.
Existe uma maneira melhor de implementar isso?
"\x0D\x0A"
.\r\n\r\n
que contém a\n\r
sequência no meio ...Respostas:
Existem diferentes maneiras de evitar isso:
Se suas mensagens tiverem no máximo 256 bytes, envie:
Então você sabe depois de receber 6 bytes de dados que é o fim; você não precisa enviar um 10 13 depois. E você pode enviar 10 13 dentro de uma mensagem. Se suas mensagens puderem ser maiores, use 2 bytes para o tamanho dos dados.
Atualização 1: Outra maneira de definir pacotes
Outra alternativa é enviar comandos que tenham um comprimento específico e possam ter muitas variações, por exemplo
Atualização 2: conexão ruim / perdas de bytes
Todas as opções acima funcionam apenas quando a linha UART está enviando bytes corretamente. Se você deseja usar formas mais confiáveis de envio, também existem muitas possibilidades. Abaixo estão alguns:
Observe que todo o mecanismo acima pode ser simples ou tão complicado quanto você deseja (ou precisa). No caso de reenvio de mensagem, também é necessário um mecanismo para identificar as mensagens (por exemplo, adicionar um número de sequência ao pacote).
fonte
Se você enviar enviar dados arbitrários -> provavelmente não será suficientemente seguro.
Uma solução comum é usar escape:
Vamos definir que os caracteres 0x02 (STX - início do quadro) e 0x03 (ETX - final do quadro) precisam ser exclusivos no fluxo de dados transmitidos. Dessa forma, o início e o final de uma mensagem podem ser detectados com segurança.
Se um desses caracteres for enviado dentro do quadro da mensagem, ele será substituído pelo prefixo de um caractere de escape (ESC = 0x1b) e pela adição de 0x20 ao caractere original.
Caráter original substituído por
O receptor reverte esse processo: sempre que ele recebe um caractere de escape, esse caractere é descartado e o próximo caractere é subtraído por 0x20.
Isso adiciona apenas alguma sobrecarga de processamento, mas é 100% confiável (supondo que não ocorram erros de transmissão, que você pode / deve verificar implementando adicionalmente um mecanismo de soma de verificação).
fonte
'\x10'
DLE (Data Link Escape). Algumas páginas da Wikipedia sugerem que o DLE era frequentemente usado de maneira oposta: dizer que o próximo byte era um caractere de controle e não um byte de dados. Na minha experiência, esse geralmente é o significado oposto para uma fuga.Você sabe, o ASCII já possui bytes para essas funções.
Ele também possui códigos para vários usos dentro da carga útil.
Seu protocolo deve especificar a granularidade mais fina de ACK (0x06) e NAK (0x15), para que dados negativos reconhecidos possam ser retransmitidos. Até essa granularidade mais fina, é aconselhável ter um campo de comprimento imediatamente após qualquer indicador de início (sem escape) e (conforme explicado em outras respostas) é aconselhável seguir qualquer indicador de parada (sem escape) com um CRC.
fonte
O UART não é à prova de falhas por sua própria natureza - estamos falando da tecnologia da década de 1960 aqui.
A raiz do problema é que o UART sincroniza apenas uma vez a cada 10 bits, permitindo que muita bobagem passe entre esses períodos de sincronização. Ao contrário, por exemplo, CAN, que mostra cada bit individual várias vezes.
Qualquer erro de dois bits que ocorra dentro dos dados corromperá um quadro UART e passará despercebido. Erros de bits nos bits de início / parada podem ou não ser detectados na forma de erros de saturação.
Portanto, não importa se você usa dados ou pacotes brutos, sempre há uma probabilidade de que inversões de bits causadas por EMI resultem em dados inesperados.
Existem inúmeras maneiras de "charlatanismo tradicional do UART" para melhorar a situação um pouco. Você pode adicionar bytes de sincronização, bits de sincronização, paridade e bits de parada dupla. Você pode adicionar somas de verificação que contam a soma de todos os bytes (e depois invertê-lo - porque por que não) ou pode contar o número de binários como soma de verificação. Tudo isso é amplamente utilizado, amplamente não científico e com uma alta probabilidade de erros ausentes. Mas foi o que as pessoas fizeram das décadas de 1960 a 1990 e muitas coisas estranhas como essas vidas hoje.
A maneira mais profissional de lidar com a transmissão segura pelo UART é ter uma soma de verificação CRC de 16 bits no final do pacote. Tudo o resto não é muito seguro e tem uma alta probabilidade de erros ausentes.
Em seguida, no nível do hardware, você pode usar o diferencial RS-422 / RS-485 para melhorar drasticamente a robustez da transmissão. Esta é uma obrigação para uma transmissão segura em distâncias maiores. O UART de nível TTL deve ser usado apenas para comunicação a bordo. O RS-232 não deve ser usado para nenhum outro propósito, mas para compatibilidade retroativa com coisas antigas.
No geral, quanto mais próximo do hardware estiver o mecanismo de detecção de erros, mais eficaz ele será. Em termos de eficácia, os sinais diferenciais são os que mais agregam, seguidos pela verificação de erros de enquadramento / saturação, etc. O CRC16 adiciona um pouco e, em seguida, o "charlatanismo tradicional do UART" adiciona um pouco.
fonte
Uma situação em que uma parte dos dados é igual à sequência de finalização deve ser considerada ao projetar o formato de um pacote de dados serial. Outra coisa a considerar é que qualquer personagem pode ser corrompido ou perdido durante a transmissão. Um caractere inicial, um caractere de parada, um byte de carga útil de dados, uma soma de verificação ou CRC, um byte de correção de erro de encaminhamento não está imune a corrupção. O mecanismo de enquadramento deve ser capaz de detectar quando um pacote possui dados corrompidos.
Existem várias maneiras de abordar tudo isso.
Estou assumindo que os pacotes são enquadrados apenas com os bytes seriais. Linhas de aperto de mão não são usadas para enquadrar. Atrasos de tempo não são usados para enquadrar.
Enviar tamanho do pacote
Envie o comprimento do pacote no início, em vez de [ou além] do caractere final no final.
profissionais: a carga útil é enviada em um formato binário eficiente.
contras: Precisa saber o tamanho do pacote no início da transmissão.
Escapar dos caracteres especiais
Escape dos caracteres especiais ao enviar os dados da carga útil. Isso já foi explicado em uma resposta anterior .
prós: o remetente não precisa saber o tamanho do pacote no início da transmissão.
contras: um pouco menos eficiente, dependendo de quantos bytes de carga útil precisam ser escapados.
Dados de carga útil codificados de forma que não possam conter caracteres de início e parada
A carga útil do pacote é codificada de forma que não possa conter os caracteres de início ou parada. Geralmente, isso é feito enviando números como sua representação ASCII ou Hex-ASCII.
prós: legível por humanos com programas terminais comuns. Não há necessidade de código para lidar com escape. Não é necessário saber o tamanho do pacote no início da transmissão
contras: menor eficiência. Para um byte de dados de carga útil, vários bytes são enviados.
fonte