2012-02-27 2 views
2

Так что я пытаюсь определить протокол связи для последовательной связи, я хочу, чтобы иметь возможность отправлять 4 байта номера на устройство, но я не уверен, как убедиться, что устройство начинает выбирать это на правом байте.байт выравнивание в последовательном общении

Например, если я хочу, чтобы отправить

0x1234abcd 0xabcd3f56 ... 

как я убеждается, что устройство не начинают читать в неправильном месте и получить первое слово, как:

0xabcdabcd 

ли там умный способ сделать это? Я думал использовать маркер для начала сообщения, но что, если я хочу отправить номер, который я выбираю в качестве данных?

ответ

7

Почему бы не отправить байт start-of-message, за которым следует байт length-of-data, если вы знаете, насколько велики данные?

В качестве альтернативы, выполните как другие бинарные протоколы и отправляйте только фиксированные размеры пакетов с фиксированным заголовком. Скажем, что вы отправите только 4 байта, тогда вы знаете, что перед фактическим содержимым данных у вас будет один или несколько байтов заголовка.

Редактировать: Я думаю, вы меня недооцениваете. Я имею в виду, что клиент должен всегда рассматривать байты как заголовок или данные, а не на основе значения, а скорее на основе позиции в потоке. Скажем, вы отправляете четыре байта данных, тогда один байт будет байтом заголовка.

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

клиент затем будет довольно основное состояние машиной, вдоль линий:

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; 
    } 
    } 
} 

Дела об этой установке является то, что определяет, будет ли байты заголовка байты не фактические содержимое байта, а скорее положение в потоке. Если вы хотите иметь переменное количество байтов данных, добавьте еще один байт в заголовок, чтобы указать количество байтов данных, следующих за ним. Таким образом, не имеет значения, отправляете ли вы такое же значение, как заголовок в потоке данных, поскольку ваш клиент никогда не будет интерпретировать его как что-либо, кроме данных.

+0

Вот как я в конечном итоге решил использовать, но он по-прежнему не полностью исправить мою проблему, что, если мои данные выглядят точно так же, как мой заголовок? – vanjoe

+0

Я попытался объяснить далее, что я имею в виду, надеюсь, что это поможет вам. – Dervall

+0

Значит, это означает, что вы не потеряете данные прямо? потому что иначе вы не знаете, на какой позиции вы находитесь, и не можете понять, где находится заголовок. Или я до сих пор недопонимаю? – vanjoe

3

netstring

Для этого приложения, может быть относительно простой формат "netstring" является адекватной.

Например, текст "hello world!"Кодирует как:

12:hello world!, 

пустая строка кодирует как три символа:

0:, 

, которая может быть представлена ​​в виде последовательности байтов

'0' ':' ',' 

Слово 0x1234abcd в одном netstring (используя network byte order), за которым следует слово 0xabcd3f56 в другой сетке, кодируется как серия байтов

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

(Символ новой строки '\ n' до и после каждой сетевой строки является необязательным, но облегчает тестирование и отладку).

цикловой синхронизации

как я убеждается, что устройство не начинают читать в неправильном месте

Общее решение frame synchronization проблемы читать во временный буфер, надеясь, что мы начали читать в нужном месте. Позже мы запускаем некоторые проверки согласованности сообщения в буфере. Если сообщение не проходит проверку, что-то пошло не так, , поэтому мы выбрасываем данные в буфере и начинаем все заново. (Если это было важное сообщение, мы надеемся, что передатчик отправит его).

Например, если последовательный кабель подключен на полпути через первый netstring, приемника видит байты строки:

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

Поскольку приемник достаточно умен, чтобы ждать «:» прежде, чем ожидали следующий байт должен быть достоверным, приемник может игнорировать первое частичное сообщение, а затем принимать второе сообщение правильно.

В некоторых случаях вы заранее знаете, какова действительная длина (и) сообщений; , что делает его еще проще, чтобы приемник обнаружил, что он начал читать в неправильном месте.

отправка стартового сообщения из-маркера в виде данных

Я думал использовать маркер для начала сообщения, но что, если я хочу, чтобы отправить номер я выбираю как данные?

После отправки заголовка netstring передатчик передает необработанные данные как есть - даже если это похоже на маркер начала сообщения.

В нормальном случае приемник уже имеет синхронизацию кадров. Парсер netstring уже прочитал заголовок «длина» и «:», , поэтому парсер netstring помещает необработанные байты данных в нужное место в буфере - даже если эти байты данных выглядят как « : "байт заголовка или", "нижний байт.

псевдокод

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

больше советов

При определении последовательного протокола связи, Я считаю, что делает тестирование и отладку много легче, если протокол всегда использует человеческий читаемый ASCII текстовые символы, а не любые произвольные двоичные значения.

синхронизации кадров (снова)

Я думал использовать маркер для начала сообщения, но что, если я хочу, чтобы отправить номер я выбираю как данные?

Мы уже рассмотрели случай, когда приемник уже имеет синхронизацию кадров. Случай, когда приемник еще не имеет синхронизации кадров, довольно грязный.

Самое простое решение для передатчика, чтобы отправить ряд безвредные байт (возможно Newlines или разделителей), длина максимально возможного допустимого сообщения, в качестве преамбулы непосредственно перед каждым netstring. Независимо от того, в каком состоянии находится приемник, когда подключен последовательный кабель, эти безвредные байты в конечном итоге приводят приемник в состояние «WAITING_FOR_LENGTH». И затем, когда транмиттер отправляет заголовок пакета (длина, за которой следует «:»), , приемник правильно распознает его как заголовок пакета и восстановил синхронизацию кадра.

(Это не действительно необходимо для передатчика, чтобы отправить эту преамбулу, прежде чем каждый пакет Возможно, передатчик может отправить его в течение 1 из 20 пакетов;. Затем приемник гарантированно восстановить синхронизацию кадра в 20 пакетов (как правило, меньше) после подключения последовательного кабеля).

другие протоколы

Другие системы используют простой Fletcher-32 контрольную сумму или что-то более сложное, чтобы обнаружить многие виды ошибок в том, что формат netstring не может обнаружить (a, b), и синхронизировать даже без преамбулы.

Многие протоколы используют специальный маркер «начала пакета» и используют различные методы «экранирования», чтобы избежать фактической отправки в переданные данные литерального «начального пакета», даже если реальные данные, которые мы хотим send имеет такое значение. (Consistent Overhead Byte Stuffing, bit stuffing, quoted-printable и другие виды binary-to-text encoding и т. Д.).

Эти протоколы имеют то преимущество, что приемник может быть уверен, что, когда мы видим маркер «начала пакета», это фактический старт пакета (а не некоторый байт данных, который, по совпадению, имеет одно и то же значение). Это значительно облегчает обработку синхронизации синхронизации - просто отбросьте байты до следующего маркера «начало пакета».

Многие другие форматы, включая формат netstring, позволяют передавать любое возможное значение байта в виде данных. Так что приемники должны быть умнее обращаться с байт стартового заголовка, чтобы мог быть фактическим начальным заголовком, или мог бы быть байтом данных - но по крайней мере им не нужно иметь дело с «экранирование» или удивительно большой буфер, требуемый, в худшем случае, для сохранения «фиксированного 64-байтового информационного сообщения» после экранирования.

Выбор одного подхода действительно не проще, чем другой - он просто подталкивает сложность к другому месту, как и прогнозировалось waterbed theory.

Не могли бы вы отказаться от обсуждения различных способов обработки байт стартового заголовка, включая эти два способа, на Serial Programming Wikibook, и редактирования этой книги, чтобы сделать ее лучше?

+0

отличный ответ. Достаточно ответы на вопрос «как я могу убедиться, что устройство не начинает чтение в неправильном месте». –

 Смежные вопросы

  • Нет связанных вопросов^_^