串行端口c ++上的数据丢失

时间:2014-09-18 17:05:32

标签: visual-c++ serialization

在我用C ++编写的应用程序(Visual Studio 2010)中,我正在读取RS232接口上的数据(使用串口转USB转换器)。该设备发送250个采样/秒数据,每个采样32字节长(基本上是一帧),波特率为115200.帧由两个同步字节(0xFFFF)分隔。应用程序每30ms定期从串行端口读取字节(如果字节可用)。

一切都很好,我可以准确地解码接收器应用程序中的数据。然而,我不时地,随机地,在我的应用程序中收到的数据表明帧的一部分丢失了。一旦发生这种情况,大多数后续帧都会以少于32个字节的形式到达。即使我重新启动设备,错误也不会消失。但如果我重置串口,错误就消失了。

我使用1MB缓冲区,每个缓冲区用于输入和输出。我通过查询端口上可用的字节来检查缓冲区溢出情况,这些字节总是小于1MB缓冲区。我花了几天时间搞清楚这个问题,但到目前为止没有用。有人可以帮我吗?代码段低于

//页眉文件

#ifndef _SERIAL_PORT_H_
#define _SERIAL_PORT_H_

#include <windows.h>
#include <vector>

// ****************************************************************************
// ****************************************************************************

class SerialPort
{
  // **************************************************************************
  // Public data.
  // **************************************************************************

public:
  enum Parity
  {
    NO_PARITY,
    ODD_PARITY,
    EVEN_PARITY
  };

  enum StopBits
  {
    ONE_STOP_BIT,
    TWO_STOP_BITS
  };

  struct PortSettings 
  {
    char portName[256];
    int baudRate;
    Parity parity;
    StopBits stopBits;
    bool hardwareFlowControl;
    unsigned long timeout_s;
    unsigned long timeout_ms;
};

  // **************************************************************************
  // **************************************************************************

public:
  SerialPort();
  ~SerialPort();

  static void enumAvailablePorts(std::vector<int>& ports);
  bool open(const char *portName, int baudRate, Parity parity = NO_PARITY, 
    StopBits stopBits = ONE_STOP_BIT, bool hardwareFlowControl = false);
  bool open(PortSettings s);
  void close();
  bool isOpen();
  const char *getOpenedPortName();

  int bytesAvailable();
  int readData(unsigned char *data, int numBytes);
  int writeData(const char *data, int numBytes);

  // **************************************************************************
  // Private data.
  // **************************************************************************

private:
  static const int RX_BUFFER_SIZE = 1024 * 1024 ;  // = 1 MByte

  PortSettings settings;
  char openedPortName[256];

  // Windows specific variables
  HANDLE handle;
  COMMCONFIG commConfig;
  COMMTIMEOUTS commTimeouts;
};

#endif

//实施

// ****************************************************************************

#include "SerialPort.h"

#include <stdio.h>
#include <string.h>


// ****************************************************************************
/// Constructor. 
// ****************************************************************************

SerialPort::SerialPort()
{
  strcpy(settings.portName, "None");    // we have "None" here
  settings.baudRate = 115200;
  settings.parity = NO_PARITY;
  settings.stopBits = ONE_STOP_BIT;
  settings.hardwareFlowControl = false;
  settings.timeout_ms = 0;
  settings.timeout_s = 0;

  // Windows specific variables
  handle = INVALID_HANDLE_VALUE;
  strcpy(openedPortName, "");       //new
}

// ****************************************************************************
/// Destructor.
// ****************************************************************************

SerialPort::~SerialPort()
{
  if (isOpen())
  {
    close();
  }
}


// ****************************************************************************
/// Function variant to open the serial port.
// ****************************************************************************

bool SerialPort::open(const char *portName, int baudRate, Parity parity, 
  StopBits stopBits, bool hardwareFlowControl)
{
  PortSettings s;

  strcpy(s.portName, portName);
  s.baudRate = baudRate;
  s.parity = parity;
  s.stopBits = stopBits;
  s.hardwareFlowControl = hardwareFlowControl;
  s.timeout_ms = 0;
  s.timeout_s = 0;

  return open(s);
}

// ****************************************************************************
/// Opens the serial port with the given settings.
// ****************************************************************************

bool SerialPort::open(PortSettings s)
{
  if (isOpen())
  {
    close();
  }

  handle = INVALID_HANDLE_VALUE;
  settings = s;

  // ****************************************************************

  unsigned long confSize = sizeof(COMMCONFIG);
  commConfig.dwSize = confSize;

  handle = CreateFileA(
    s.portName, 
    GENERIC_READ | GENERIC_WRITE, 
    FILE_SHARE_READ | FILE_SHARE_WRITE, 
    NULL, 
    OPEN_EXISTING, 0, NULL
  );

  if (handle == INVALID_HANDLE_VALUE)
  {
    return false;
  }

  strcpy(openedPortName, s.portName);

  // ****************************************************************
  // Set the internal buffer size to a big value.
  // ****************************************************************

  unsigned long dwInQueue = RX_BUFFER_SIZE;
  unsigned long dwOutQueue = RX_BUFFER_SIZE;

    if (!SetupComm(handle, dwInQueue, dwOutQueue))
    {
    printf("SerialPort::open() - Unable to setup the COM-port.\n");;
    return false;
    }

  // Prepare the settings structures

  GetCommConfig(handle, &commConfig, &confSize);
  GetCommState(handle, &(commConfig.dcb));

  commConfig.dcb.fBinary = TRUE;
  commConfig.dcb.fInX    = FALSE;
  commConfig.dcb.fOutX   = FALSE;
  commConfig.dcb.fAbortOnError = FALSE;
  commConfig.dcb.fNull   = FALSE;

  // ****************************************************************
  // Set the given baud rate.
  // ****************************************************************

  switch (s.baudRate) 
  {
    case 4800: 
      commConfig.dcb.BaudRate = CBR_4800; 
      break;
    case 9600: 
      commConfig.dcb.BaudRate = CBR_9600; 
      break;
    case 19200: 
      commConfig.dcb.BaudRate = CBR_19200; 
      break;
    case 38400: 
      commConfig.dcb.BaudRate = CBR_38400; 
      break;
    case 57600: 
      commConfig.dcb.BaudRate = CBR_57600; 
      break;
    case 115200: 
      commConfig.dcb.BaudRate = CBR_115200; 
      break;
    case 921600: 
      commConfig.dcb.BaudRate = 921600;   // Must use literals for the value here.
      break;

    default:
      // Invalid baud rate
      close();
      return false;
      break;
  }

  // ****************************************************************
  // Set the number of data bits to 8.
  // ****************************************************************

  commConfig.dcb.ByteSize = 8;

  // ****************************************************************
  // Set the given number of stop bits.
  // ****************************************************************

  switch (s.stopBits) 
  {
    case ONE_STOP_BIT:
      commConfig.dcb.StopBits = ONE_STOP_BIT;
      break;

    case TWO_STOP_BITS:
      commConfig.dcb.StopBits = TWO_STOP_BITS;
      break;
  }

  // ****************************************************************
  // Set the given parity type.
  // ****************************************************************

  switch (s.parity) 
  {
    case NO_PARITY:
      commConfig.dcb.Parity = NOPARITY;
      commConfig.dcb.fParity = FALSE;
      break;

    case EVEN_PARITY:
      commConfig.dcb.Parity = EVENPARITY;
      commConfig.dcb.fParity = TRUE;
      break;

    case ODD_PARITY:
      commConfig.dcb.Parity = ODDPARITY;
      commConfig.dcb.fParity = TRUE;
      break;
  }

  // ****************************************************************
  // Disable flow control.
  // ****************************************************************

  commConfig.dcb.fOutxCtsFlow = FALSE;
  if (s.hardwareFlowControl)
  {
    commConfig.dcb.fRtsControl = DTR_CONTROL_ENABLE;
  }
  else
  {
    commConfig.dcb.fRtsControl = RTS_CONTROL_DISABLE;
  }
  commConfig.dcb.fInX = FALSE;
  commConfig.dcb.fOutX = FALSE;

  // ****************************************************************
  // Set the timeouts.
  // ****************************************************************

  commTimeouts.ReadIntervalTimeout = s.timeout_s*1000 + s.timeout_ms;
  commTimeouts.ReadTotalTimeoutMultiplier = s.timeout_s*1000 + s.timeout_ms;
  commTimeouts.ReadTotalTimeoutConstant = 0;
  commTimeouts.WriteTotalTimeoutMultiplier = s.timeout_s*1000 + s.timeout_ms;
  commTimeouts.WriteTotalTimeoutConstant = 0;
  SetCommTimeouts(handle, &commTimeouts);

  // ****************************************************************
  // Apply the changed configuration.
  // ****************************************************************

  return (bool)SetCommConfig(handle, &commConfig, sizeof(COMMCONFIG));

//  return true;
}

// ****************************************************************************
/// Closes the serial port.
// ****************************************************************************

void SerialPort::close()
{
  if (handle != INVALID_HANDLE_VALUE)
  {
    CloseHandle(handle);
  }
  handle =INVALID_HANDLE_VALUE;
}

// ****************************************************************************
// ****************************************************************************

bool SerialPort::isOpen()
{
  return (handle != INVALID_HANDLE_VALUE);
}

// ****************************************************************************
// ****************************************************************************

const char *SerialPort::getOpenedPortName()
{
  return openedPortName;
}


// ****************************************************************************
/// Returns the number of bytes available for reading.
// ****************************************************************************

int SerialPort::bytesAvailable()
{
  // Determine the number of new bytes.
  COMSTAT comStat;
  DWORD errorMask = 0;

  ClearCommError(handle, &errorMask, &comStat);
  int newBytes = comStat.cbInQue;

  if (newBytes > RX_BUFFER_SIZE/2)
  {
    printf("\nSerial Port buffer is more than half full"); 
  }

  return newBytes;
}


// ****************************************************************************
/// Tries to read the given number of bytes from the RX buffer. The actual 
/// number of read bytes is returned.
// ****************************************************************************

int SerialPort::readData(unsigned char *data, int numBytes)
{
  DWORD bytesRead;
  ReadFile(handle, (void*)data, numBytes, &bytesRead, NULL);

  return (int)bytesRead;
}


// ****************************************************************************
/// Writes data out on the serial port.
/// \return The actual number of bytes written.
// ****************************************************************************

int SerialPort::writeData(const char *data, int numBytes)
{
  if (isOpen() == false)
  {
    return 0;
  }

  DWORD bytesWritten;

  if (WriteFile(handle, (void*)data, (DWORD)numBytes, &bytesWritten, NULL)) 
  {
    // Flush the bytes
    FlushFileBuffers(handle);
    return (int)bytesWritten;
  }
  else 
  {
    return 0;
  }
}


// ****************************************************************************

//应用

main()
{

SerialPort port; 

port.open(emgPortName, 115200, SerialPort::NO_PARITY, 
      SerialPort::ONE_STOP_BIT, true); 

// repeat following code every 30ms

int numBytesAvailable = port->bytesAvailable();


  if (numBytesAvailable >= RAW_BUFFER_SIZE)
  {  
    printf("\nWarning!!!!!!!!!!!\nSerial port buffer overflow!!!"); 
    numBytesAvailable = RAW_BUFFER_SIZE; 
  }  

  port->readData(rawEmgBuffer, numBytesAvailable); 


}

1 个答案:

答案 0 :(得分:0)

当我看到像

这样的代码时,我感到畏缩
int numBytesAvailable = port->bytesAvailable();
port->readData(rawEmgBuffer, numBytesAvailable);

我主要在very broken C# implementations看到这种方法。

从串口读取可用数据的正确方式简单得多:

  • 使用SetCommTimeouts将读取超时设置为零(只需要执行一次)。
  • 调用ReadFile,传入缓冲区和缓冲区大小。它将立即返回,只有已经收到的字节数,最多为您给出的缓冲区大小。通过第四个参数可以知道复制到缓冲区的字节数。

如果您发现缓冲区已完全填满,您可能需要更快地再次阅读(例如,在处理完第一个卡盘后,无需等待下一个计时器回调)。如果您发现缓冲区为空,您可以告诉用户检查它们的连接。

重叠I / O是您工具箱的一个很好的补充,因为它允许您在数据可用时立即获取事件。但是如果你通过轮询获得足够好的性能(并且你的轮询循环不忙等待),那么阻塞读取时没有超时是理想的。