如何处理缓冲串行数据

时间:2019-01-27 20:17:32

标签: c serial-port buffer

我正在努力寻找一种读取串行数据的好方法,以及完成read()但却包含不完整消息的处理方法。

设备之间的预期消息具有定义的开始和结束字节,因此很容易看到消息开始和结束的时间。

我可以打开一个串行端口,然后从串行端口读取。但是我遇到的是计算机读取速度快于数据读取速度,并且收到的消息不完整。

在此示例中,假设消息是

0x10 0xFF 0xFF 0xFF 0xFF 0x11

以0x10开头,以0x11结尾,数据字节为0xFF

我是C的新手,所以我可能缺少明显的东西, 我当前的解决方案

int main() {
     /* Ommited serial port opening and checking*/
     char read_buffer[80];
     char message_buffer[80];
     int message_buffer_index = 0;
     int start_index = -1;
     int end_index = -1;
     int read_bytes;
     read_bytes = read(serial_port, read_buffer, sizeof(read_buffer) - 1);
     /* Now lets say read_bytes returns 3 and read buffer is {0x10, 0xFF, 0xFF} */
     /* What should I do with the read_buffer? Currently appending to message buffer*/
     memcpy(&message_buffer[message_buffer_index], &read_buffer[0], read_bytes);
     /* Now check the message buffer for a full message */
     for (int i = 0; i < 80; i++) {
          if (message_buffer[i] = 0x10) {
               start_index = i;
               continue;
          }
          if (message_buffer[i] = 0x11) {
               end_index = i;
          }
          if (start_index != -1 && end_index != -1) {
               /* Found a message, do something with it, not super important here */
               process_message();
               /* Now how to erase the full message from the 
               buffer and push any non processed data to the 
               front? */
               remove_message();
          }
    }
}

int process_message();  
int remove_message();

3 个答案:

答案 0 :(得分:1)

要最大程度地减少进行许多小字节计数的 read()系统调用的开销(例如,一次读取一个字节的错误解决方案),请在代码中使用中间缓冲区。
串行终端的 read()应该处于阻塞模式,以避免返回零字节的返回码。

#define BLEN    1024
unsigned char rbuf[BLEN];
unsigned char *rp = &rbuf[BLEN];
int bufcnt = 0;

/* get a byte from intermediate buffer of serial terminal */
static unsigned char getbyte(void)
{
    if ((rp - rbuf) >= bufcnt) {
        /* buffer needs refill */
        bufcnt = read(fd, rbuf, BLEN);
        if (bufcnt <= 0) {
            /* report error, then abort */
        }
        rp = rbuf;
    }
    return *rp++;
}

有关串行终端的正确的 termios 初始化代码,请参见this answer。您应该将VMIN参数增加到更接近BLEN值。

现在,您可以方便地一次访问一个字节的接收数据,而对性能的影响最小。

#define MLEN    1024  /* choose appropriate value for message protocol */
int main() 
{
    unsigned char mesg[MLEN];
    ...

    while (1) {
        while (getbyte() != 0x10)
            /* discard data until start found */ ;

        length = 0;
        while ((mesg[length] = getbyte()) != 0x11) {
            /* accumulate data until end found */ 
            length++;
        }

        /* process the message */
        ...


    }  /* loop for next message */
...
}

请注意,您对消息框架的定义不可靠。
如果数据是二进制的,因此可以使用与开始和结束字节相同的值,则对接收到的数据的这种分析很容易导致消息帧未对齐。

答案 1 :(得分:0)

您需要循环缓冲区。将数据放入缓冲区,然后在例如有足够数据或在任何方便的时候处理该数据。

维基百科上有一篇很棒的文章https://en.wikipedia.org/wiki/Circular_buffer

enter image description here

答案 2 :(得分:0)

最好使用stdio进行阅读,如下所示:

FILE *fp = fdopen(serial_port, "r");

while (blabla) {
  while (fgetc(fp) != 0x10)
    ;  // wait until start
  while ((c = fgetc(fp)) != 0x11)
    message_buffer[message_buffer_index++] = c;
  // here you have a complete message
}

如果需要,插入以检查EOF和错误