Quanto è sicuro \ n \ r come stop byte?


8

Nella mia comunicazione UART ho bisogno di conoscere il byte iniziale e il byte finale del messaggio inviato. Il byte iniziale è semplice ma il byte finale, non tanto. Ho implementato due byte di stop alla fine del mio messaggio, ovvero \ n e \ r (10 e 13 decimali). UART funziona solo su valori byte 0-255, quindi quanto è sicuro? Posso immaginare, sebbene con bassa probabilità, che il mio messaggio possa contenere i valori "10 e 13" uno dopo l'altro quando non sono i byte di stop.

C'è un modo migliore per implementarlo?


7
Per inviare dati arbitrari devi usare i pacchetti o il byte stuffing. Nel tuo caso la probabilità che il motivo appaia in una determinata posizione è 1/65536. Che arriva a 1 se hai un flusso di dati casuale abbastanza lungo.
Oldfart

4
Potete fornire un contesto per favore. I bit di stop fanno parte della comunicazione UART ma i byte di stop? Questo suona come un problema di software puro e dipende da ciò che è stato concordato dal mittente e dal destinatario.
Warren Hill,

2
@MariusGulbrandsen se i tuoi dati sono veramente arbitrari e non strettamente testuali (pensa ASCII), la terminazione nulla non funzionerà; dovrai implementare un pacchetto.
RamblinRose

4
BTW: Tale pratica comune è quella di mettere il ritorno del carrello prima del salto di riga: "\x0D\x0A".
Adrian McCarthy,

3
@AdrianMcCarthy Penso che il punto di invertire sia minimizzare le probabilità che sia una sequenza valida. Detto questo, ti darebbero due finali di fila di Windows \r\n\r\nche contengono la \n\rsequenza nel mezzo ...
Mike Caron,

Risposte:


14

Esistono diversi modi per impedirlo:

  • Assicurati di non inviare mai una combinazione 10/13 nei tuoi messaggi regolari (quindi solo come byte di stop). Ad esempio per inviare 20 21 22 23 24 25:

20 21 22 23 24 25 10 13

  • Escape 10 e 13 (o tutti i caratteri non ASCII con un carattere escape, ad es. Quindi, per inviare 20 21 10 13 25 26 inviare: (vedi commento di / crediti per: DanW)

20 21 1b 10 1b 13 25 26

  • Definire un pacchetto quando si inviano messaggi. Ad esempio, se si desidera inviare il messaggio 20 21 22 23 24 25 anziché aggiungere il numero di byte da inviare, quindi il pacchetto è:

<nr_of_data_bytes> <data>

Se i tuoi messaggi hanno un massimo di 256 byte, invia:

06 20 21 22 23 24 25

Quindi sai dopo aver ricevuto 6 byte di dati che è la fine; non è necessario inviare successivamente un 10 13. E puoi inviare 10 13 all'interno di un messaggio. Se i tuoi messaggi possono essere più lunghi, puoi utilizzare 2 byte per la dimensione dei dati.

Aggiornamento 1: un altro modo di definire i pacchetti

Un'altra alternativa è quella di inviare comandi che hanno una lunghezza specifica e possono avere molte varianze, ad es

10 20 30 (comando 10 che ha sempre 2 byte di dati)

11 30 40 50 (comando 11 che ha sempre 3 byte di dati)

12 06 10 11 12 13 14 15 (Comando 12 + 1 byte per il numero di byte di dati che seguono)

13 01 02 01 02 03 ... (Comando 13 + 2 byte (01 02 per 256 + 2 = 258 byte di dati che seguono)

14 80 90 10 13 (Comando 14 seguito da una stringa ASCII che termina con 10 13)

Aggiornamento 2: perdita di connessione / byte errata

Tutto quanto sopra funziona solo quando la linea UART invia correttamente i byte. Se desideri utilizzare metodi di invio più affidabili, ci sono anche molte possibilità. Di seguito sono riportati alcuni:

  1. Invio di un checksum all'interno del pacchetto (controllare google per CRC: controllo di ridondanza ciclica). Se il CRC è ok, il destinatario sa che il messaggio è stato inviato ok (con alta probabilità).
  2. Se è necessario inviare nuovamente un messaggio, è necessario utilizzare un meccanismo di conferma (ACK / risposta) (ad esempio, il mittente invia qualcosa, il destinatario riceve dati corrotti, invia un NACK (non riconosciuto), il mittente può inviare di nuovo.
  3. Timeout: nel caso in cui il ricevitore non ottenga un ACK o NACK in tempo, è necessario inviare nuovamente un messaggio.

Si noti che tutto il meccanismo sopra può essere semplice o complicato come si desidera (o è necessario). In caso di reinvio del messaggio, è necessario anche un meccanismo per identificare i messaggi (ad es. Aggiungere un numero progressivo nel pacchetto).


1
"Assicurati di non inviare mai una combinazione 10/13 nei tuoi messaggi regolari (quindi solo come byte di stop)." - Non hai detto come inviare dati che non includono una combinazione 10/13 - è necessario sfuggire. Quindi "20 10 13 23 10 13" potrebbe essere inviato come "20 1b 10 1b 13 23" con 1b come personaggio di fuga.
Dan W,

1
Nota che usando un campo di lunghezza come proposto, avrai problemi quando il tuo collegamento seriale è danneggiato e perde un singolo byte. Tutto andrà fuori sincrono.
Jonas Schäfer,

@DanW Se usi il primo o 2 byte come numero di byte di dati, non importa se 10 o 13 fanno parte di quei dati ... Quindi 20 10 13 23 10 13 possono essere inviati come 06 20 10 13 23 10 13 dove 06 è il numero di byte di dati che seguono.
Michel Keijzers,

@MichelKeijzers - sì, ma questa è la seconda soluzione che menzioni. Nella prima soluzione manca una spiegazione delle sequenze di escape per impedire la trasmissione dei byte di arresto.
Dan W,

Entrambi gli approcci funzionano e sono comunemente usati, ma hanno diversi vantaggi e svantaggi, che è possibile aggiungere se lo si desidera, anche se è al di là di quanto richiesto dall'OP.
Dan W,

13

Quanto è sicuro \ n \ r come stop byte?

Se si invia inviare dati arbitrari -> probabilmente non abbastanza sicuro.

Una soluzione comune è usare l'escaping:

Definiamo che i caratteri 0x02 (STX - inizio frame) e 0x03 (ETX - fine frame) devono essere univoci all'interno del flusso di dati trasmesso. In questo modo è possibile rilevare in modo sicuro l'inizio e la fine di un messaggio.

Se uno di questi caratteri deve essere inviato all'interno della cornice del messaggio, viene sostituito dal prefisso di un carattere di escape (ESC = 0x1b) e aggiungendo 0x20 al carattere originale.

Personaggio originale sostituito da

0x02 -> 0x1b 0x22  
0x03 -> 0x1b 0x23  
0x1b -> 0x1b 0x3b  

Il ricevitore inverte questo processo: ogni volta che riceve un carattere di fuga, questo personaggio viene abbandonato e il personaggio successivo viene sottratto da 0x20.

Ciò aggiunge solo un certo sovraccarico di elaborazione, ma è affidabile al 100% (presupponendo che non si verifichino errori di trasmissione, che è possibile / verificare mediante l'implementazione di un meccanismo di checksum).


1
Bella risposta. Il carattere di escape comune utilizzato per i protocolli ASCII era '\x10'DLE (Data Link Escape). Alcune delle pagine di Wikipedia suggeriscono che il DLE veniva spesso usato in modo opposto: dire che il byte successivo era un carattere di controllo piuttosto che un byte di dati. Nella mia esperienza, questo è generalmente il significato opposto di una fuga.
Adrian McCarthy,

2
Una cosa da tenere d'occhio è che la dimensione del buffer nel caso peggiore raddoppia. Se la memoria è molto stretta, potrebbe non essere la soluzione migliore.
TechnoSam,

1
@Rev Qual è la logica per l'aggiunta di 0x20 al personaggio originale? Lo schema di fuga non funzionerebbe anche senza quello?
Nick Alexeev

1
@NickAlexeev: è più facile / veloce identificare i limiti effettivi del frame se si rimuove qualsiasi altra occorrenza dei caratteri riservati dallo stream. In questo modo, è possibile separare la ricezione e l'analisi dei frame (incluso il non-escape). Ciò può essere particolarmente rilevante se si dispone di un controller molto lento senza FIFO e / o velocità di trasmissione dati elevate. Quindi puoi semplicemente copiare i byte in arrivo (tra STX / ETX) nel frame buffer quando arrivano, contrassegnare il frame come completo ed eseguire l'elaborazione con priorità inferiore.
Rev1.0

@TechnoSam: buon punto.
Rev1.0

5

Sai, ASCII ha già byte per queste funzioni.

  • 0x01: inizio dell'intestazione - inizio byte
  • 0x02: inizio del testo - fine delle intestazioni, inizio del payload
  • 0x03: fine del testo - fine del payload
  • 0x04: fine trasmissione - stop byte
  • 0x17: fine del blocco di trasmissione - il messaggio continua nel blocco successivo

Ha anche codici per vari usi all'interno del payload.

  • 0x1b: escape (sfuggire al carattere successivo - utilizzare nel payload per indicare che il carattere successivo non è una delle strutture che descrivono i codici utilizzati nel protocollo)
  • 0x1c, 0x1d, 0x1e, 0x1f: file, gruppo, record e separatore unità, rispettivamente - usati come byte di stop e start simultanei per parti di dati gerarchici

Il protocollo deve specificare la massima granularità di ACK (0x06) e NAK (0x15), in modo che i dati riconosciuti negativi possano essere ritrasmessi. Fino a questa granularità più fine, è saggio avere un campo di lunghezza immediatamente dopo qualsiasi indicatore di avvio (senza escape) e (come spiegato in altre risposte) è saggio seguire qualsiasi indicatore di stop (senza escape) con un CRC.


Invierò dati arbitrari, suppongo che potrebbe essere stato fonte di confusione usare "\ n \ r" nella mia domanda quando non invio dati ASCII. Anche se, mi piace questa risposta, è molto istruttiva sull'invio di ASCII su UART
CK

@MariusGulbrandsen: Fintanto che il protocollo stabilisce dove si trova il payload e quali codici devono essere evasi in ogni sezione del payload, è possibile inviare qualsiasi cosa, non solo dati testuali.
Eric Towers,

4

UART non è sicuro per sua natura: qui stiamo parlando della tecnologia degli anni '60.

La radice del problema è che UART si sincronizza solo una volta per 10 bit, consentendo a un sacco di cose incomprensibili di passare tra quei periodi di sincronizzazione. A differenza, ad esempio, di CAN che campiona ogni singolo bit più volte.

Qualsiasi errore a doppio bit che si verifica all'interno dei dati danneggerà un frame UART e passerà inosservato. Gli errori di bit nei bit di avvio / arresto possono o meno essere rilevati sotto forma di errori di sovraccarico.

Pertanto, indipendentemente dal fatto che si utilizzino dati o pacchetti non elaborati, è sempre probabile che i bit invertiti causati dall'IME generino dati imprevisti.

Esistono numerosi modi di "quackery UART tradizionale" per migliorare la situazione in modo così lieve. È possibile aggiungere byte di sincronizzazione, bit di sincronizzazione, parità, bit di stop doppio. È possibile aggiungere checksum che contano la somma di tutti i byte (e quindi invertirlo - perché no) oppure si può contare il numero di binari come checksum. Tutto questo è ampiamente usato, selvaggiamente non scientifico e con un'alta probabilità di errori mancanti. Ma questo è ciò che la gente ha fatto dagli anni '60 agli anni '90 e molte cose strane come queste vite oggi.

Il modo più professionale per gestire la trasmissione sicura su UART è disporre di un checksum CRC a 16 bit alla fine del pacchetto. Tutto il resto non è molto sicuro e ha un'alta probabilità di errori mancanti.

Quindi a livello hardware è possibile utilizzare il differenziale RS-422 / RS-485 per migliorare drasticamente la robustezza della trasmissione. Questo è un must per una trasmissione sicura su lunghe distanze. UART di livello TTL deve essere utilizzato solo per le comunicazioni di bordo. RS-232 non dovrebbe essere usato per nessun altro scopo ma compatibilità con le versioni precedenti.

Nel complesso, più è vicino all'hardware il meccanismo di rilevamento degli errori, più è efficace. In termini di efficacia, i segnali differenziali aggiungono di più, seguiti dal controllo di errori di frame / overrun ecc. CRC16 ne aggiunge un po ', quindi "Quackery UART tradizionale" aggiunge un po'.


7
Questo consiglio è abbastanza tangenziale: in realtà non hai affrontato la domanda posta. In particolare, le soluzioni proposte possono risolvere altri problemi, ma non risolvono il problema di base della domanda in questa pagina , che è confusione tra i bye di frame e quelli di payload. Al massimo, la tua proposta rifiuta i dati validi che incorporano un byte di frame a causa di CRC o errori simili, senza alcun modo per comunicarli.
Chris Stratton,

3
In effetti, questa risposta peggiora le cose. L'originale aveva solo byte di dati e stop byte. Ciò aggiunge una terza categoria, byte CRC. E come presentato qui, quelli possono assumere qualsiasi valore, incluso {10,13}.
Salterio

1
@MSalters: il CRC può essere esadecimale con codifica ASCII per evitare questo problema. Un altro trucco che ho visto su RS485 è impostare il bit 7 sul byte di avvio / indirizzo.
Transistor

Ri "CAN che campiona ogni singolo bit più volte." : Il campionamento effettivo del valore bit è solo una volta per bit. A cosa ti riferisci qui? Qualche tipo di controllo degli errori, come da parte del mittente? Sincronizzazione dell'orologio?
Peter Mortensen,

L'inversione del checksum è stata eseguita in modo tale che la somma dell'intero blocco di dati si tradurrebbe in uno zero, che è un po 'più facile da codificare e un po' più veloce da eseguire. Inoltre, CRC è molto meglio di quanto si pensi, cercalo su Wikipedia.
toolforger,

0

... Posso immaginare, anche se con bassa probabilità, che il mio messaggio possa contenere i valori "10 e 13" uno dopo l'altro quando non sono i byte di stop.

Quando si progetta il formato di un pacchetto di dati seriali, si deve considerare una situazione in cui una porzione di dati è uguale alla sequenza finale. Un'altra cosa da considerare è che qualsiasi personaggio può essere danneggiato o perso durante la trasmissione. Un carattere di inizio, un carattere di arresto, un byte di payload dei dati, un checksum o un byte CRC, un byte di correzione dell'errore forward non sono immuni alla corruzione. Il meccanismo di framing deve essere in grado di rilevare quando un pacchetto contiene dati corrotti.

Esistono diversi modi per affrontare tutto questo.

Sto assumendo che i pacchetti siano inquadrati solo con i byte seriali. Le linee di handshake non vengono utilizzate per l'inquadramento. I ritardi non vengono utilizzati per l'inquadramento.

Invia lunghezza pacchetto

Invia la lunghezza del pacchetto all'inizio, anziché [o in aggiunta a] il carattere finale alla fine.

pro: il payload viene inviato in un efficiente formato binario.

contro: è necessario conoscere la lunghezza del pacchetto all'inizio della trasmissione.

Sfuggire ai personaggi speciali

Sfuggire ai caratteri speciali quando si inviano i dati del payload. Questo è già spiegato in una risposta precedente .

pro: il mittente non deve conoscere la lunghezza del pacchetto all'inizio della trasmissione.

contro: Leggermente meno efficiente, a seconda di quanti byte di payload devono essere evitati.

Dati del payload codificati in modo tale che non possano contenere caratteri di avvio e arresto

Il payload del pacchetto è codificato in modo tale da non poter contenere i caratteri di inizio o fine. Di solito, questo viene fatto inviando numeri come loro rappresentazione ASCII o Hex-ASCII.

pro: leggibile dall'uomo con i comuni programmi terminali. Non è necessario che il codice gestisca l'escaping. Non è necessario conoscere la lunghezza del pacchetto all'inizio della trasmissione

contro: efficienza inferiore. Per un byte di dati del payload, vengono inviati diversi byte.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.