缓冲区管理和从串行数据中提取帧

Buffer management and extracting frames from serial data

我正在编写代码,使用 windows api 通过 C 解析串行数据。我能够读取数据,并且还编写了代码来解析帧。我面临的困难是从传入的串行数据中提取有效帧。

我的解析函数接受一个有效的框架,没有。该帧中的字节数。

void parse_serial(unsigned char* receivedData, int noOfBytes)

这个帧结构如下:header(0x10), subsystemID, status, datalength, data, checksum(header till data).

我想从传入数据中提取此帧并将其发送到我的解析函数。此外,如果未提取一半帧,则剩余数据应与下一个传入数据一起使用。到目前为止,这是我的代码:

hSerial = CreateFileA(pcCommPort,
    GENERIC_READ | GENERIC_WRITE,
    0,
    0,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    0);

void read_serial()
{
    int BUFFERLENGTH =256;
    BOOL  Read_Status;
    unsigned char  SerialBuffer[BUFFERLENGTH+1];
    DWORD NoBytesRead;

    Read_Status = SetCommMask(hSerial, EV_RXCHAR); //Configure Windows to Monitor the serial device for Character Reception

        if (Read_Status == FALSE)
            printf("Error! in Setting CommMask");
        else
            printf("Setting CommMask successful");

        Read_Status = WaitCommEvent(hComm, &dwEventMask, NULL); //Wait for the character to be received

         /*-------------------------- Program will Wait here till a Character is received ------------------------*/

        if (Read_Status == FALSE)
        {
            printf("\n    Error! in Setting WaitCommEvent()");
        }
        else If  WaitCommEvent()==True Read the RXed data using ReadFile();
        {
            printf("\n\n    Characters Received \t");

            do
            {
                Read_Status = ReadFile(hComm, &TempChar, sizeof(TempChar), &NoBytesRead, NULL);
                if (!ReadFile(hComm, SerialBuffer, BUFFERLENGTH, &NoBytesRead, NULL))
                {
                    printf("wrong character");
                }
                SerialBuffer[i] = TempChar;
                printf("%c", SerialBuffer[i]);
                i++;
            } while (NoBytesRead > 0);
        }
  }

只需使用 memmove 将不完整的帧(消息)移动到缓冲区的开头,并且在完成应用程序的功能之前不要关心优化(只需在代码中添加 TODO 注释以了解可能的改进)。

稍后当您无事可做时,您可以结合 ReadFileScatter 实现循环缓冲区,这允许您将传入数据写入多个块,这些块可以位于不同的缓冲区中(即在缓冲区的开头)和缓冲区的结尾,一半的帧占据了缓冲区的中间)

更新: 我检查了 ReadFileScatter 的文档,我怀疑它不适合您的目的,因为它需要缓冲区对齐,并且会使循环缓冲区的实现更加复杂,并且在小型静态缓冲区的情况下可能是不可能的。

您仍然可以将 FileRead 与循环缓冲区一起使用,但复制少量数据的开销很小,因此我建议您将实现限制为使用 memmove 以保持解决方案简单(除非您开始使用大框架喜欢> 64kb)。

你可以 return 回到 ReadFileScatter 解决方案只有当你使用大缓冲区并且 memmove 会开始减慢你的应用程序时(但是使用 "slow" 串行通信速度你很可能不会遇到任何瓶颈)。

我相信您一定能够自己找到循环缓冲区的现有解决方案。

我明白了。 我正在读取 SerialBuffer 中有关 compot 的所有可用数据,并将其复制到 localBuff 上一个周期的任何剩余数据之后。

if(ReadFile(hSerial, SerialBuffer, BUFFERLENGTH, &NoBytesRead, NULL))
       {
            if(NoBytesRead<=0)
           {
               qDebug()<<"No data on serial port";
           }
           else
           {
               tempDataAvail = NoBytesRead;
               memcpy(&localBuff[lastCycleBytes], &SerialBuffer,(sizeof(localBuff) - lastCycleBytes));
           }
       }

我正在发送 localBuffextractValidFrame 没有。前一个周期的字节数附加了新数据。

lastCycleBytes += tempDataAvail;
       if(lastCycleBytes >= sizeof(localBuff))
       {
           qDebug()<<"buffer filled with garbage - flushed!";
           lastCycleBytes = 0;
       }
extractValidFrame(localBuff,lastCycleBytes);

提取帧后,我将其发送以解析并按帧大小向左移动剩余数据。

memcpy(&validFrame[0], &receivedData[startLoc],endLoc-startLoc+1);
parse_serial(validFrame);
memset(&validFrame, 0,endLoc-startLoc+1);
memcpy(&receivedData[0], &receivedData[endLoc+1],noOfBytes-endLoc-1);
lastCycleBytes = noOfBytes-endLoc-1;