在我用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);
}
答案 0 :(得分:0)
当我看到像
这样的代码时,我感到畏缩int numBytesAvailable = port->bytesAvailable();
port->readData(rawEmgBuffer, numBytesAvailable);
我主要在very broken C# implementations看到这种方法。
从串口读取可用数据的正确方式简单得多:
SetCommTimeouts
将读取超时设置为零(只需要执行一次)。ReadFile
,传入缓冲区和缓冲区大小。它将立即返回,只有已经收到的字节数,最多为您给出的缓冲区大小。通过第四个参数可以知道复制到缓冲区的字节数。如果您发现缓冲区已完全填满,您可能需要更快地再次阅读(例如,在处理完第一个卡盘后,无需等待下一个计时器回调)。如果您发现缓冲区为空,您可以告诉用户检查它们的连接。
重叠I / O是您工具箱的一个很好的补充,因为它允许您在数据可用时立即获取事件。但是如果你通过轮询获得足够好的性能(并且你的轮询循环不忙等待),那么阻塞读取时没有超时是理想的。