如何处理缓冲串行数据 [英] How to handle buffering serial data

查看:108
本文介绍了如何处理缓冲串行数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图找出一种读取串行数据的好方法,以及完成 read()但其中包含不完整消息的处理方法。



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



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



在此示例中,假设消息是

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

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



我是C语言的新手,所以我可能会错过明显的东西,
我当前的解决方案

  int main(){
/ *省略了打开和检查串行端口* /
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(串行端口,read_buffer,sizeof(read_buffer)-1);
/ *现在,假设read_bytes返回3,读缓冲区为{0x10,0xFF,0xFF} * /
/ *使用read_buffer应该怎么办?当前附加到消息缓冲区* /
memcpy(& message_buffer [message_buffer_index],& read_buffer [0],read_bytes);
/ *现在检查消息缓冲区中的完整消息* /
for(int i = 0; i< 80; i ++){
if(message_buffer [i] = 0x10){
start_index = i;
继续;
}
if(message_buffer [i] = 0x11){
end_index = i;
}
if(start_index!= -1&& end_index!= -1){
/ *找到一条消息,用它来做某事,在这里并不重要* /
process_message();
/ *现在如何清除
缓冲区中的完整消息并将任何未处理的数据推送到
前端? * /
remove_message();
}
}
}

int process_message();
int remove_message();


解决方案

以最大程度地减少进行许多读取的开销()小字节数的系统调用(例如,一次读取一个字节的错误解决方案),请在代码中使用中间缓冲区。

read()应该处于阻塞模式,以避免返回零字节的返回代码。

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

/ *从串行终端的中间缓冲区获取一个字节* /
静态无符号字符getbyte(void)
{
if((rp-rbuf)> = bufcnt){
/ *缓冲区需要重新填充* /
bufcnt = read(fd,rbuf,BLEN);
if(bufcnt< = 0){
/ *报告错误,然后中止* /
}
rp = rbuf;
}
return * rp ++;
}

有关串行终端的正确的 termios 初始化代码,请参见此答案 。您应该将VMIN参数增加到更接近BLEN值或至少是最长预期消息的长度,并且将VTIME设置为1。


现在您可以方便地访问一个字节的接收数据

  #define MLEN 1024 / *为消息协议选择适当的值* / 
int main()
{
unsigned char mesg [MLEN];
...

while(1){
while(getbyte()!= 0x10)
/ *丢弃数据,直到找到开始* /;

长度= 0;
while(((mesg [length] = getbyte())!= 0x11){
/ *累积数据直到找到结束* /
length ++;
}

/ *处理消息* /
...


} / *为下一条消息循环* /
...
}

请注意,您对消息帧的定义不可靠。

如果数据是二进制的,因此可以使用与开始字节和结束字节相同的值,则对接收到的数据进行此分析很容易导致消息帧未对齐。


I am trying to figure out a nice solution to reading serial data, and what to do when a read() is done but it contains an incomplete message.

The expected messages between devices have a defined start and end byte so its easy to see when a message starts and ends.

I can open a serial port fine and read from the serial port. But I am encountering the computer is reading faster than data coming through and I get an incomplete message.

For this example, lets say the message expected is

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

With 0x10 the start, 0x11 the end, and 0xFF is the data bytes

I am new to C so I may be missing something obvious, My current solution

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

解决方案

To minimize the overhead of making many read() syscalls of small byte counts (e.g. the misguided solution of reading a byte at a time), use an intermediate buffer in your code.
The read() of the serial terminal should be in blocking mode to avoid a return code of zero bytes.

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

For proper termios initialization code for the serial terminal, see this answer. You should increase the VMIN parameter to something closer to the BLEN value or at least the length of longest expected message, and a VTIME of 1.

Now you can conveniently access the received data a byte at a time with minimal performance penalty.

#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 */
...
}

Note that your definition for a message frame is not robust.
If the data is binary and therefore can use the same values as the start and end bytes, then this parsing of the received data is prone to misaligned message frames.

这篇关于如何处理缓冲串行数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆