我在跨平台应用程序( Linux嵌入式和实际的嵌入式目标)中的串口有一些问题,它也可以在Windows上工作,以使开发更容易。这是关于Windows实现的。
因此,串行协议的实现是针对操作系统和非OS系统的混合体,我不会触及实现本身。我想使它与现有的实现兼容。如果这在合理的时间内失败了,我将为串行读取做一个单独的线程。
好的,基本上,实现打开串口,在我们的IO系统中注册文件描述符(在Linux上使用epoll,在Windows上使用WaitForMultipleObjects ),然后,基本上,只需要等待所有句柄并执行所需的任何操作。因此,当手柄发出读取信号时,我们希望从串口读取。不幸的是,在Windows上,您无法指定是在等待读还是写,所以我想我应该使用以下解决方案:
CreateFile与FILE_FLAG_OVERLAPPEDSetCommMask与EV_RXCHAROVERLAPPED结构WaitCommEvent结构调用OVERLAPPED,该结构通常返回ERROR_IO_PENDING这是基本的设置。我注册事件句柄,而不是等待文件句柄。当手柄发出信号时,我做以下操作:
ReadFileResetEvent并再次调用WaitCommEvent但是,如果指定FILE_FLAG_OVERLAPPED,则必须使用重叠IO进行读写。因此,我认为,每当ReadFile或WriteFile返回ERROR_IO_PENDING时,我只需等待带有WaitForSingleObject和GetOverlappedResult的IO。不过,我似乎没有进入这一阶段。它似乎基本上可以工作,但有时它会在一个ResetEvent调用上崩溃,就好像重叠仍然是活动的(虽然我认为它仍然不应该崩溃)。
所以,真正的问题。能按我的意愿去做吗?这种方法总体上有问题吗,还是应该起作用?还是使用另一个线程是唯一好的解决方案?通信已经在一个单独的线程中,因此它至少是三个线程。
我将尽可能多地发布代码,尽管它与实际代码相比减少了,而实际代码包含了许多与串行读取无关的内容。
SerialPort::SerialPort(const std::string &filename)
{
fd = INVALID_HANDLE_VALUE;
m_ov = new OVERLAPPED(); // Pointer because header shouldn't include Windows.h.
memset(m_ov, 0, sizeof(OVERLAPPED));
m_waitHandle = m_ov->hEvent = CreateEvent(0, true, 0, 0);
}
SerialPort::~SerialPort(void)
{
Close();
CloseHandle(m_ov->hEvent);
delete m_ov;
}构造函数在一个单独的线程中调用,该线程稍后调用Open:
bool SerialPort::Open(void)
{
if (fd != INVALID_HANDLE_VALUE)
return true;
fd = CreateFile(filename.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (fd != INVALID_HANDLE_VALUE) {
DCB dcb;
ZeroMemory(&dcb, sizeof(DCB));
COMMTIMEOUTS timeouts = {0};
timeouts.ReadIntervalTimeout = TimeOut();
timeouts.ReadTotalTimeoutConstant = TimeOut();
timeouts.ReadTotalTimeoutMultiplier = TimeOut() / 5;
if (timeouts.ReadTotalTimeoutMultiplier == 0) {
timeouts.ReadTotalTimeoutMultiplier = 1;
}
if (!SetCommTimeouts(fd, &timeouts)) {
DebugBreak();
}
SetCommMask(fd, EV_RXCHAR);
InitWait();
return true;
}
return false;
}
void SerialPort::InitWait()
{
if (WaitForSingleObject(m_ov->hEvent, 0) == WAIT_OBJECT_0) {
return; // Still signaled
}
DWORD dwEventMask;
if (!WaitCommEvent(fd, &dwEventMask, m_ov)) {
// For testing, I have some prints here for the different cases.
}
}然后,线程通过相当长的链调用WaitForMultipleObjects on m_waitHandle,这与OVERLAPPED结构的hEvent成员相同。这是在一个循环中完成的,列表中还有其他几个句柄,这就是为什么这与典型的解决方案不同,在这种解决方案中,有一个线程独占地从串口读取。基本上,我对循环没有控制,这就是为什么我尝试在正确的时间执行WaitCommEvent (在InitWait内)。
当发出句柄信号时,线程将调用ReadData方法:
int SerialPort::ReadData(void *buffer, int size)
{
if (fd != INVALID_HANDLE_VALUE) {
// Timeouts are reset here to MAXDWORD/0/0, not sure if necessary.
DWORD dwBytesRead;
OVERLAPPED ovRead = {0};
ovRead.hEvent = CreateEvent(0, true, 0, 0);
if (ReadFile(fd, buffer, size, &dwBytesRead, &ovRead)) {
if (WaitForSingleObject(m_ov->hEvent, 0) == WAIT_OBJECT_0) {
// Only reset if signaled, because we might get here because of a timer.
ResetEvent(m_waitHandle);
InitWait();
}
CloseHandle(ovRead.hEvent);
return dwBytesRead;
} else {
if (GetLastError() == ERROR_IO_PENDING) {
WaitForSingleObject(ovRead.hEvent, INFINITE);
GetOverlappedResult(fd, &ovRead, &dwBytesRead, true);
InitWait();
CloseHandle(ovRead.hEvent);
return dwBytesRead;
}
}
InitWait();
CloseHandle(ovRead.hEvent);
return -1;
} else {
return 0;
}
}编写过程如下,无需同步:
int SerialPort::WriteData(const void *buffer, int size)
{
if (fd != INVALID_HANDLE_VALUE) {
DWORD dwBytesWritten;
OVERLAPPED ovWrite = {0};
ovWrite.hEvent = CreateEvent(0, true, 0, 0);
if (!WriteFile(fd, buffer, size, &dwBytesWritten, &ovWrite)) {
if (GetLastError() == ERROR_IO_PENDING) {
WaitForSingleObject(ovWrite.hEvent, INFINITE);
GetOverlappedResult(fd, &ovWrite, &dwBytesWritten, true);
CloseHandle(ovWrite.hEvent);
return dwBytesWritten;
} else {
CloseHandle(ovWrite.hEvent);
return -1;
}
}
CloseHandle(ovWrite.hEvent);
}
return 0;
}现在看来确实起作用了。再也没有死机了,至少我不能复制它们。所以,就像现在一样,我只是在问我所做的事情是否是理智的,还是我应该以不同的方式去做。
发布于 2013-03-01 21:33:44
顺便说一下,我在您展示的代码中没有看到任何错误,但是我想建议一些替代代码来清理ReadData()和WriteData()中的错误处理:
int SerialPort::ReadData(void *buffer, int size)
{
if (fd == INVALID_HANDLE_VALUE)
return 0;
OVERLAPPED ovRead = {0};
ovRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!ovRead.hEvent)
return -1;
DWORD dwBytesRead;
if (!ReadFile(fd, buffer, size, &dwBytesRead, &ovRead))
{
if (GetLastError() != ERROR_IO_PENDING)
{
CloseHandle(ovRead.hEvent);
return -1;
}
if (!GetOverlappedResult(fd, &ovRead, &dwBytesRead, TRUE))
{
CloseHandle(ovRead.hEvent);
return -1;
}
}
if (WaitForSingleObject(m_waitHandle, 0) == WAIT_OBJECT_0)
{
ResetEvent(m_waitHandle);
InitWait();
}
CloseHandle(ovRead.hEvent);
return dwBytesRead;
}
int SerialPort::WriteData(const void *buffer, int size)
{
if (fd == INVALID_HANDLE_VALUE)
return 0;
OVERLAPPED ovWrite = {0};
ovWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!ovWrite.hEvent)
return -1;
DWORD dwBytesWritten;
if (!WriteFile(fd, buffer, size, &dwBytesWritten, &ovWrite))
{
if (GetLastError() != ERROR_IO_PENDING)
{
CloseHandle(ovWrite.hEvent);
return -1;
}
if (!GetOverlappedResult(fd, &ovWrite, &dwBytesWritten, TRUE))
{
CloseHandle(ovWrite.hEvent);
return -1;
}
}
CloseHandle(ovWrite.hEvent);
return dwBytesWritten;
}https://stackoverflow.com/questions/15136645
复制相似问题