Pregunta

Entonces, estoy tratando de definir un protocolo de comunicación para la comunicación en serie, quiero poder enviar 4 números de bytes al dispositivo, pero no estoy seguro de cómo asegurarse de que el dispositivo comienza a recogerlo a la derecha.byte.

Por ejemplo, si quiero enviar

0x1234abcd 0xabcd3f56 ...

¿Cómo me aseguro de que el dispositivo no comience a leer en el lugar incorrecto y obtener la primera palabra como:

0xabcdabcd

¿Hay una forma inteligente de hacer esto?Pensé en usar un marcador para el inicio de un mensaje, pero ¿qué pasa si quiero enviar el número que elijo como datos?

¿Fue útil?

Solución

¿Por qué no enviar un byte start-of-message seguido de un byte length-of-data Si sabe lo grande que va a ser los datos?

Alternativamente, realice como otros protocolos binarios y solo envíe tamaños fijos de paquetes con un encabezado fijo. Diga que solo enviará 4 bytes, entonces sabrá que tendrá uno o más bytes de encabezado antes del contenido de datos real.

Editar: Creo que me están entendiendo mal. Lo que quiero decir es que se supone que el cliente siempre debe considerar los bytes, ya que los encabezados o datos, no se basan en el valor, sino en función de la posición en el flujo. Diga que está enviando cuatro bytes de datos, entonces un byte sería el byte del encabezado.

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

El cliente será entonces una máquina de estado bastante básica, a lo largo de las líneas de:

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 de esta configuración es que lo que determina si el byte es un byte de encabezado no es el contenido real de un byte, sino más bien la posición en el flujo. Si desea tener un número variable de bytes de datos, agregue otro byte al encabezado para indicar el número de bytes de datos que siguen. De esta manera, no importará si está enviando el mismo valor que el encabezado en el flujo de datos, ya que su cliente nunca lo interpretará como nada más que datos.

Otros consejos

netstring

Para esta aplicación, quizás el relativamente simple " NetString " El formato es adecuado.

Por ejemplo, el texto "Hola mundo!" codifica como:

12:hello world!,

La cadena vacía codifica como los tres caracteres:

0:,

que se puede representar como la serie de bytes

'0' ':' ','

La palabra 0x1234Abcd en una red (usando orden de bytes de red ), seguido de la palabra 0xabcd3f56 en otra red, codifica como la serie de bytes

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

(El carácter de nueva línea '\ N' antes y después de cada red es opcional, pero facilita la prueba y la depuración).

sincronización de cuadros

¿Cómo me aseguro de que el dispositivo no comience a leer en el lugar incorrecto

La solución general a la Sincronización de marco El problema es leer en un búfer temporal, esperando que Hemos comenzado a leer en el lugar correcto. Más tarde, ejecutamos algunos controles de consistencia en el mensaje en el búfer. Si el mensaje falla el cheque, algo se ha equivocado, Así que desechamos los datos en el búfer y comencemos. (Si fue un mensaje importante, esperamos que el transmisor lo envíe).

Por ejemplo, si el cable serial está enchufado a la mitad de la primera red, El receptor ve la cadena de byte:

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

Debido a que el receptor es lo suficientemente inteligente como para esperar el ':' antes de esperar que el siguiente byte sea datos válidos, el receptor puede ignorar el primer mensaje parcial y luego recibir el segundo mensaje correctamente.

En algunos casos, usted sabe con anticipación lo que será la (s) longitud (s) de mensaje válida (s); Eso hace que sea aún más fácil que el receptor detecte, ha comenzado a leer en el lugar equivocado.

enviando marcador de inicio de mensaje como datos

Pensé en usar un marcador para el inicio de un mensaje, pero ¿qué pasa si quiero enviar el número que elijo como datos?

Después de enviar el encabezado NetString, el transmisor envía los datos en bruto como es, incluso si sucede como el marcador de inicio de mensaje.

En el caso normal, el receptor ya tiene sincronización de cuadros. El analizador NetString ya ha leído el encabezado "Longitud" y ":", Así que el analizador de red Pone los datos en bruto los bytes directamente en la ubicación correcta en el búfer, incluso si esos bytes de datos se parecen al byte ":" del encabezado o el byte "", ".

pseudocode

// 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();

más consejos

Al definir un protocolo de comunicación en serie, Me parece que hace que las pruebas y la depuración mucho sean más fáciles si el protocolo siempre utiliza caracteres de texto ASCII legibles humanos, en lugar de cualquier valores binarios arbitrarios.

sincronización de cuadros (de nuevo)

Pensé en usar un marcador para el inicio de un mensaje, pero ¿qué pasa si quiero enviar el número que elijo como datos?

Ya cubrimos el caso donde el receptor ya tiene sincronización de cuadros. El caso en el que el receptor aún no tiene sincronización de cuadros es bastante desordenado.

La solución más simple es que el transmisor envíe una serie de bytes inofensivos (Tal vez los nuevos líneas o caracteres espaciales), la longitud del mensaje válido máximo posible, como un preámbulo justo antes de cada red. No importa en qué estado se encuentre el receptor cuando el cable en serie esté enchufado, Esos bytes inofensivos eventualmente conducen el receptor en el Estado "WAYS_FOR_LENGTY". Y luego, cuando el tranmitter envía el encabezado del paquete (longitud seguido de ":"), El receptor lo reconoce correctamente como un encabezado de paquetes y se ha recuperado sincronización de cuadros.

(no es realmente necesario para th

E Transmisor para enviar ese preámbulo antes de cada paquete . Tal vez el transmisor pueda enviarlo por 1 de los 20 paquetes; Luego, se garantiza que el receptor recupere la sincronización de cuadros en 20 paquetes (generalmente menos) después de que el cable en serie esté enchufado).

otros protocolos

Otros sistemas Use una suma de comprobación simple de Fletcher-32 o algo más complicado para detectar muchos tipos de errores que el formato NetString no puede detectar ( a , b ), y puede sincronizar incluso sin preámbulo.

Muchos protocolos utilizan un marcador especial de "inicio de paquete", y use una variedad de técnicas de "escape" para evitar que realmente envíe un byte literal "inicio de paquete" en los datos transmitidos, incluso si los datos reales queremos enviar sucede que tiene ese valor. ( relleno constante de byte de arriba , relleno de bits , citado-imprimible y otros tipos de codificación de binario a texto , etc.).

Los protocolos tienen la ventaja de que el receptor puede estar seguro de que cuando vemos el marcador "Inicio de paquete", es el inicio del paquete real del paquete (y no un byte de datos que coinciden de manera que tenga el mismo valor). Esto hace que la pérdida de sincronización de manejo sea mucho más fácil, simplemente deseche los bytes hasta el siguiente marcador "Inicio de paquete".

Muchos otros formatos, incluido el formato NetString, permiten que cualquier valor de byte posible se transmita como datos. Por lo tanto, los receptores tienen que ser más inteligentes acerca de manejar el byte de inicio de encabezado que podrían ser un inicio de encabezado real, o podría ser un byte de datos, pero en Al menos no tienen que lidiar con "escapar" o el búfer sorprendentemente grande requerido, en el peor de los casos, para mantener un "mensaje de datos fijo de 64 bytes" después de escapar.

Elegir un enfoque realmente no es más simple que el otro, simplemente empuja la complejidad a otro lugar, según lo predicho Teoría de la lecho de agua .

¿Le importaría romper la discusión de varias formas de manejar el byte de inicio de encabezado, incluidas estas dos formas, en el programación en serie wikiBook , ¿Y editar ese libro para hacerlo mejor?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top