Domanda

Quindi sto cercando di definire un protocollo di comunicazione per la comunicazione seriale, voglio essere in grado di inviare 4 numeri di byte al dispositivo, ma non sono sicuro come assicurarsi che il dispositivo inizia a prenderlo a destrabyte.

ad esempio se voglio inviare

0x1234abcd 0xabcd3f56 ...
.

Come faccio a garantire che il dispositivo non inizi a leggere nel punto sbagliato e ottieni la prima parola come:

0xabcdabcd
.

C'è un modo intelligente di fare questo?Ho pensato di usare un marker per l'inizio di un messaggio, ma cosa succede se voglio inviare il numero scelgo come dati?

È stato utile?

Soluzione

Perché non inviare un byte start-of-message seguito da un byte length-of-data se sai quanto saranno grandi i dati?

In alternativa, fare come altri protocolli binari e invia solo dimensioni fisse di pacchetti con un'intestazione fissa. Dì che invierai solo 4 byte, allora sarai che avrai uno o più byte di intestazione prima del contenuto effettivo dei dati.

Modifica: Penso che tu ti fraintendomi. Ciò che intendo è che il cliente dovrebbe considerare sempre i byte come intestazione o dati, non basato sul valore ma piuttosto basato sulla posizione nel flusso. Diciamo che stai inviando quattro byte di dati, un byte sarebbe il byte di intestazione.

+-+-+-+-+-+
|H|D|D|D|D|
+-+-+-+-+-+
.

Il client sarebbe quindi una bella macchina di stato di base, lungo le linee di:

int state = READ_HEADER;
int nDataBytesRead = 0;
while (true) {
  byte read = readInput();
  if (state == READ_HEADER) {
    // process the byte as a header byte
    state = READ_DATA;
    nDataBytesRead = 0;
  } else {
    // Process the byte as incoming data
    ++nDataBytesRead;
    if (nDataBytesRead == 4) 
    {
      state = READ_HEADER;
    }
  }
} 
.

La cosa di questa configurazione è che ciò che determina se il byte è un byte di intestazione non è il contenuto effettivo di un byte, ma piuttosto la posizione nel flusso. Se si desidera disporre di un numero variabile di byte di dati, aggiungere un altro byte all'intestazione per indicare il numero di byte di dati che lo seguono. In questo modo, non importa se si invia lo stesso valore dell'intestazione nel flusso di dati poiché il tuo cliente non lo interpreterà mai come altro tranne i dati.

Altri suggerimenti

netstring

Per questa applicazione, forse il relativamente semplice " Netstring " il formato è adeguato. Ad esempio, il testo "Hello World!" codifica come:

12:hello world!,
.

La stringa vuota codifica come tre caratteri:

0:,
.

che può essere rappresentato come serie di byte

'0' ':' ','
.

La parola 0x1234abcd in una netstring (usando ordine byte di rete ), seguito dalla parola 0xabcd3f56 In un'altra netstring, codifica come serie di byte

'\n' '4' ':' 0x12 0x34 0xab 0xcd ',' '\n'
'\n' '4' ':' 0xab 0xcd 0x3f 0x56 ',' '\n'
.

(il carattere newline '\ n' prima e dopo ogni netstring è facoltativo, ma facilita la prova e il debug).

Sincronizzazione del telaio

Come faccio a garantire che il dispositivo non inizi a leggere nel punto sbagliato

La soluzione generale al Sincronizzazione del telaio Il problema è di leggere in un buffer temporaneo, sperandolo Abbiamo iniziato a leggere nel punto giusto. Successivamente, corriamo alcuni controlli di coerenza sul messaggio nel buffer. Se il messaggio non riesce il controllo, qualcosa è andato storto, Quindi buttiamo via i dati nel buffer e ricominciamo. (Se fosse un messaggio importante, speriamo che il trasmettitore lo richiamerà).

Ad esempio, se il cavo seriale è collegato a metà della prima netstring, Il ricevitore vede la stringa Byte:

0xab 0xcd ',' '\n' '\n'  '4' ':' 0xab 0xcd 0x3f 0x56 ',' '\n'
.

Poiché il ricevitore è abbastanza intelligente da attendere il ':' prima di aspettarsi che il prossimo byte sia valido dei dati, il ricevitore è in grado di ignorare il primo messaggio parziale e quindi ricevere correttamente il secondo messaggio.

In alcuni casi, sai in anticipo quali saranno le lunghezze dei messaggi validi; Ciò rende ancora più facile per il ricevitore rilevare che ha iniziato a leggere nel punto sbagliato.

Invio del marker di avvio del messaggio come dati

Ho pensato di usare un marker per l'inizio di un messaggio, ma cosa succede se voglio inviare il numero scelgo come dati?

Dopo aver inviato l'intestazione della netstring, il trasmettitore invia il DATA RAW AS-è - anche se accade sembrare il marker di avvio del messaggio.

Nel caso normale, il record ha già la sincronizzazione del fotogramma. Il parser Netstring ha già letto la "lunghezza" e l'intestazione ":", Quindi il parser della netstring Mette i byte dei dati grezzi direttamente nella posizione corretta nel buffer, anche se tali byte di dati si verificano come il byte di intestazione ":" o il byte ",", byte del piè di pagina.

pseudocodice

// netstring parser for receiver
// WARNING: untested pseudocode
// 2012-06-23: David Cary releases this pseudocode as public domain.

const int max_message_length = 9;
char buffer[1 + max_message_length]; // do we need room for a trailing NULL ?
long int latest_commanded_speed = 0;
int data_bytes_read = 0;

int bytes_read = 0;
int state = WAITING_FOR_LENGTH;

reset_buffer()
    bytes_read = 0; // reset buffer index to start-of-buffer
    state = WAITING_FOR_LENGTH;

void check_for_incoming_byte()
    if( inWaiting() ) // Has a new byte has come into the UART?
        // If so, then deal with this new byte.
        if( NEW_VALID_MESSAGE == state )
            // oh dear. We had an unhandled valid message,
            // and now another byte has come in.
            reset_buffer();
        char newbyte = read_serial(1); // pull out 1 new byte.
        buffer[ bytes_read++ ] = newbyte; // and store it in the buffer.
        if( max_message_length < bytes_read )
            reset_buffer(); // reset: avoid buffer overflow

        switch state:
            WAITING_FOR_LENGTH:
                // FIXME: currently only handles messages of 4 data bytes
                if( '4' != newbyte )
                    reset_buffer(); // doesn't look like a valid header.
                else
                    // otherwise, it looks good -- move to next state
                    state = WAITING_FOR_COLON;
            WAITING_FOR_COLON:
                if( ':' != newbyte )
                    reset_buffer(); // doesn't look like a valid header.
                else
                    // otherwise, it looks good -- move to next state
                    state = WAITING_FOR_DATA;
                    data_bytes_read = 0;
            WAITING_FOR_DATA:
                // FIXME: currently only handles messages of 4 data bytes
                data_bytes_read++;
                if( 4 >= data_bytes_read )
                    state = WAITING_FOR_COMMA;
            WAITING_FOR_COMMA:
                if( ',' != newbyte )
                    reset_buffer(); // doesn't look like a valid message.
                else
                    // otherwise, it looks good -- move to next state
                    state = NEW_VALID_MESSAGE;

void handle_message()
    // FIXME: currently only handles messages of 4 data bytes
    long int temp = 0;
    temp = (temp << 8) | buffer[2];
    temp = (temp << 8) | buffer[3];
    temp = (temp << 8) | buffer[4];
    temp = (temp << 8) | buffer[5];
    reset_buffer();
    latest_commanded_speed = temp;
    print( "commanded speed has been set to: " & latest_commanded_speed );
}

void loop () # main loop, repeated forever
    # then check to see if a byte has arrived yet
    check_for_incoming_byte();
    if( NEW_VALID_MESSAGE == state ) handle_message();
    # While we're waiting for bytes to come in, do other main loop stuff.
    do_other_main_loop_stuff();
.

Altri suggerimenti

Durante la definizione di un protocollo di comunicazione seriale, Trovo che fa il test e il debug molto più facile se il protocollo utilizza sempre caratteri di testo ASCII leggibili dall'uomo, piuttosto che qualsiasi valori binari arbitrari.

Sincronizzazione del telaio (di nuovo)

Ho pensato di usare un marker per l'inizio di un messaggio, ma cosa succede se voglio inviare il numero scelgo come dati?

Abbiamo già coperto il caso in cui il ricevitore ha già la sincronizzazione del fotogramma. Il caso in cui il ricevitore non ha ancora la sincronizzazione del fotogramma è piuttosto disordinato.

La soluzione più semplice è per il trasmettitore per inviare una serie di byte innocui (forse neoklines o caratteri spaziali), la lunghezza del messaggio valido massimo possibile, come preambolo poco prima di ogni netstring. Non importa quale stato il ricevitore sia quando il cavo seriale è collegato, Questi byte innocui alla fine guidano il ricevitore nel "Waiting_for_length" stato. E poi quando il tranmiritter invia l'intestazione del pacchetto (lunghezza seguita da ":"), Il ricevitore lo riconosce correttamente come intestazione di pacchetti e ha recuperato la sincronizzazione del fotogramma.

(non è realmente necessario per th

e Trasmettitore per inviare quel preambolo prima di ogni pacchetto . Forse il trasmettitore potrebbe inviarlo per 1 su 20 pacchetti; Quindi il ricevitore è garantito per recuperare la sincronizzazione del fotogramma in 20 pacchetti (di solito inferiore) dopo che il cavo seriale è inserito).

Altri protocolli

Altri sistemi utilizzano un semplice checksum Fletcher-32 o qualcosa di più complicato per rilevare molti tipi di errori che il formato di netstring non può rilevare ( a , B ), e può sincronizzare anche senza un preambolo.

Molti protocolli utilizzano un indicatore "Start of Packet" speciale e utilizzare una varietà di tecniche "fuga" per evitare effettivamente l'invio di un byte di "inizio del pacchetto" letterale nei dati trasmessi, anche se i dati reali che vogliamo inviare capita di avere quel valore. ( coerente byte in testa ripieno , bit ripieno , citato-stampabile e altri tipi di codifica binaria-text , ecc.).

.

Questi protocolli hanno il vantaggio che il record può essere sicuro che quando vediamo il marcatore "Inizio del pacchetto", è l'inizio effettivo del pacchetto (e non alcuni dati di dati che accadono coincidentalmente avere lo stesso valore). Ciò rende molto più semplice la gestione della perdita di sincronizzazione, è sufficiente scartare i byte fino al successivo marcatore "Inizio del pacchetto".

Molti altri formati, incluso il formato di netstring, consentono di trasmettere qualsiasi valore di byte possibile come dati. Quindi i ricevitori devono essere più intelligenti sulla gestione del byte di avvio-of-header che potrebbe essere un avvio effettivo, o potrebbe essere un byte di dati - ma a almeno non devono affrontare "Escaping" o il tampone sorprendentemente grande richiesto, nel peggiore dei casi, per contenere un "Messaggio dati a 64 byte fisso" dopo Escaping.

Scelta di un approccio non è davvero più semplice dell'altro: spinge solo la complessità in un altro posto, come previsto da Teoria dei containali .

Ti dispiacerebbe scremare sulla discussione di vari modi di gestire il byte di avviamento, compresi questi due modi, a programmazione seriale wikibook , e modificando quel libro per renderlo migliore?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top