所以我试图定义的通信协议串行通信时,我想要能够发送4个字节的数字设备,但我不能确定如何确保设备开始把它捡起来在右边的字节。

例如,如果我想送

0x1234abcd 0xabcd3f56 ...

我怎么确保该设备不开始阅读在错误的位置,并得到的第一个词为:

0xabcdabcd

是否有一个聪明的方式这样做?我以为使用的标记于开始一个消息,但是如果我想送些我选择为数据?

有帮助吗?

解决方案

为什么不发送世代odicetagcode字节后跟一个生成的icetagcode字节如果您知道数据的大大是多大? 或者,尽可能用作其他二进制协议,并且仅用固定标题发送固定大小的包装。说你只会发送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;
    }
  }
} 
.

此设置的内容是确定字节是否为标题字节而不是字节的实际内容,而是在流中的位置。如果要具有可变数量的数据字节,则将另一个字节添加到标题以指示其后面的数据字节数。这样,如果您的客户端永远不会将其解释为数据流中的标题,则无论您将其发送到数据流中的标头,都不重要。

其他提示

netstring

对于这一应用,也许比较简单的"netstring"格式是适当的。

例如,该文本"hello world!"编码为:

12:hello world!,

空串编码作为三个大字:

0:,

它可以表示作为一系列字节

'0' ':' ','

这个词0x1234abcd在一个netstring(使用 网络字节了),随后通过一词0xabcd3f56在另一个netstring、编码作为系列的字节

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

(该newline character' '之前和之后的每一netstring是任选的,而是使它更加容易测试和调试).

帧同步

我怎么确保该设备不开始阅读在错误的地方

一般的解决方法的 帧同步 问题是读到一个暂时的缓冲区,希望我们已经开始读在正确的位置。后来,我们运行的一些一致性检查的消息,在缓冲区。如果消失败的检查,事情已经走了错误的, 所以我们扔掉的数据,在该缓冲区,并开始结束。(如果它是一个重要的信息,我们希望,发射机将重新发送)。

例如,如果串电缆插入过半的第一netstring, 收看到byte string:

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

因为接收足够的智慧等待':'之前期待下一个字节是有效的数据,该接收器能够忽略的第一部分消息,然后收到的第二个消息正确。

在某些情况下,可提前知道什么是有效的信息的长度(s);这使它更容易接收器来检测到它已开始阅读在错误的地点。

发送开始的消息记作为数据

我以为使用的标记于开始一个消息,但是如果我想送些我选择为数据?

在发送之后的netstring头、发射机发送原始数据作-是的-甚至如果情况看起来像开始的消息的标记。

在正常情况下,接收器已经有框架的同步。该netstring分析器已经阅读的"长度"和":"header, 所以netstring分析器 把原始数据的数字直接进入正确的位置在缓冲区--即使这些数据字节发生看起来像":"header字节或","脚字节。

// 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码文本文字,而非任何任意的二进制的价值观。

帧同步(再)

我以为使用的标记于开始一个消息,但是如果我想送些我选择为数据?

我们已经涵盖的情况下接收器已经有框架的同步。的情况下接收器还没有框架的同步是相当混乱。

最简单的解决方案就是为发射机发送的一系列无害字节 (也许是新行或空间符), 长度的最大可能有效的消息, 作为一个序言之前每个netstring.不管是什么国家接收方是在当串行总线就是插入, 这些无害字节的最终驱动的收入 "WAITING_FOR_LENGTH"的状态。然后当tranmitter发送的报文头(长随后通过":"), 接收正确地认识到它作为一个组头,并且已恢复帧同步。

(它是不是真的有必要为发射机发送的前导之前 分组。也许发射器可以发送1个20分组;然后接收器是保证恢复帧同步在20分组(通常少)之后串电缆插入)。

其他协议

其他系统的使用简单的Fletcher-32校或更多的东西复杂的探测许多种错误的netstring格式不能检测( 一个, b ), 并且可以同步,即使没有一个序言。

许多协议使用一个特殊的"启动包"的标记,并且使用各种"逃脱"的技术,以避免实际发送一个文字"启动包"字节在传送数据,即使实际数据中,我们想要发送的发生有价值。( 相一致的开销字节的馅, 位填充, quoted-printable 和其他种类的 二进制文本编码, 等等)。

这些协议具有的优点是,接收器可以肯定的是,当我们看到的"启动包"的标记,这是实际的启动分组(而不是一些数字,恰巧发生在拥有同样的价值)。这使得处理同步损失更容易--简单地抛弃字节,直到下一个"启动包"的标记。

许多其他格式,包括netstring格式,请允许任何可能的字节的数值要传送的数据。所以接收者必须更聪明有关处理开始的头字节, 可能会 是一个实际开始的头,或 可能会 是字节的数据-但至少他们不必处理与"逃脱"或令人惊讶的大缓冲所需要的,在最坏的情况下,举行"固定64字节的数据电文"之后逃脱。

选择一种方法实际上没有任何简单的其他--这只是推动的复杂性,另一个地方,作为预测的 水床的理论.

你会介意掠过讨论各种方式处理开始的头字节,其中包括这两个方面, 串维基教科书的编程, 和编辑的书,使它更好吗?

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top