串口读取功能的通信超时和线程不超时
COMMTIMEOUT and Thread not timming out on serial port Read function
我正在调用一个通过串行端口操作 I/O 板的函数,以检查它是否在我的主类的实例中进行通信。我知道这是有风险的,但不幸的是,这是一个已经使用了一段时间的旧代码部分,因此在要求我改进它时无法更改功能。
如果没有通信问题,应用程序将启动,使用该功能并继续没有问题。
当I/O板出现通信故障时出现问题时,就会出现问题,我发现读取功能挂起并阻止应用程序在大部分时间启动。有时,应用程序将加载并报告存在通信故障。
我试图实现的是每次出现通信故障时都能成功加载应用程序。
comport最初是使用COMMTIMEOUT设置的,我希望当没有要读取的内容时,它会使端口超时。我试图改变时间,但无济于事。
我还尝试为读取功能使用线程,以便它不会阻塞启动但仍然挂起。
当前端口是同步设置的。
有人有什么建议吗?如果需要,我可以放一些代码示例。
主.cpp
extern COMPort comPort;
BOOL Main::InitInstance()
{
int i = comPort.DoorLatchOff();
If(i<0) printf("Error checksum. COM port?n");
else printf("checksum ok.n");
}
COMPort.h
class CCOMPort
{
public:
CCOMPort (COM_PORT port = NULL_COM, DCB * state = NULL);
BOOL SetPortNumber (COM_PORT port = NULL_COM, DCB * state = NULL);
void Read(BYTE* buff, int count);
int DoorLatchOff(void);
protected:
HANDLE m_hPort;
};
static HANDLE m_hPortThreaded;
typedef struct readParam{BYTE* readBuff;int readCount;}RP, *PRP;
DWORD WINAPI ThreadedRead( LPVOID lpParam );
比较.cpp
CCOMPort::CCOMPort (COM_PORT port, DCB * state) : m_portNum (port), m_hPort(INVALID_HANDLE_VALUE)
{
SetPortNumber (port, state);
}
BOOL CCOMPort::SetPortNumber (COM_PORT port, DCB * state)
{
if (m_hPort != INVALID_HANDLE_VALUE){
::CloseHandle (m_hPort);
m_hPort = INVALID_HANDLE_VALUE;
}
m_portNum = port;
m_currState = m_defState;
m_originState = m_defState;
if (m_portNum != NULL_COM){
stringstream ssPortName;
ssPortName << "COM" << (m_portNum + 1) << ":" << flush;
m_hPort = ::CreateFile (ssPortName.str().c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
NULL);
if (m_hPort == INVALID_HANDLE_VALUE)
return FALSE;
else
{
GetState (& m_originState);
if (state)
m_currState = * state;
SetState (& m_currState);
GetCommTimeouts(m_hPort, &timeouts);
timeouts.ReadIntervalTimeout = 75; //15
timeouts.ReadTotalTimeoutMultiplier = 5; //1
timeouts.ReadTotalTimeoutConstant = 1250; //250
timeouts.WriteTotalTimeoutMultiplier = 5; //1
timeouts.WriteTotalTimeoutConstant = 1250; //250
SetCommTimeouts(m_hPort, &timeouts);
FlushOutput ();
FlushInput ();
PurgeOutput ();
PurgeInput ();
}
}
return TRUE;
}
void CCOMPort::Read(BYTE* buff, int count)
{
PRP pReadArray[1];
DWORD dwThreadArray[1];
HANDLE hThreadArray[1];
m_hPortThreaded = m_hPort;
pReadArray[0] = (PRP) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RP));
if(pReadArray[0] == NULL){
ExitProcess(2);
}
pReadArray[0]->readBuff = buff;
pReadArray[0]->readCount = count;
hThreadArray[0] = CreateThread(NULL,
0,
ThreadedRead,
pReadArray[0],
0,
&dwThreadArray[0]);
if(hThreadArray[0] == NULL){
ExitProcess(3);
}
WaitForSingleObject(hThreadArray[0],500/*INFINITE*/);
CloseHandle(hThreadArray[0]);
if(pReadArray[0] != NULL){
HeapFree(GetProcessHeap(), 0, pReadArray[0]);
pReadArray[0] = NULL;
}
}
DWORD WINAPI ThreadedRead(LPVOID lpParam)
{
BOOL bDone = FALSE, bResult;
//int buff_idx = 0;
DWORD dwCommModemStatus;
DWORD dwBytesTransfered;
PRP pReadArray;
pReadArray = (PRP)lpParam;
SetCommMask(m_hPortThreaded, EV_RXCHAR);
while(!bDone){
WaitCommEvent(m_hPortThreaded, &dwCommModemStatus, 0);
if(dwCommModemStatus == 0){
bDone = TRUE;
break;
}
if(dwCommModemStatus & EV_RXCHAR){
bResult = ReadFile(m_hPortThreaded, pReadArray[0].readBuff, pReadArray[0].readCount, &dwBytesTransfered, 0);
bDone = TRUE;
}
}
return(bResult);
}
int COMPort::DoorLatchOff(void)
{
unsigned char comm_str[10];
int chksum, chksum1;
DWORD count = 6;
WriteComm(21, 7, 0);
comm.Read(comm_str, count);
chksum = comm_str[0] + comm_str[2] + comm_str[3];
chksum1 = comm_str[4];
chksum1 = (chksum1 << 8) | comm_str[5];
if(chksum == chksum1)
return(0);
else
return(-1);
}
最近我遇到了同样的问题,但我已经解决了。 有两种方法:
-
在论坛上,有些人建议将 ReadIntervalTimeout 和 ReadTotalTimeoutMultiplier 都设置为 MAXDWORD,如 MSDN 文档的 NOTES 部分所示。但在这种情况下,每次输入缓冲区中至少有一个字符时,函数都会返回。
-
我发现的最可靠的决定是将 ReadIntervalTimeout 和 ReadTotalTimeoutMultiplier 设置为 0,将 ReadTotalTimeoutConstant 设置为您的超时值,如下所示。它对我来说效果很好。
COMMTIMEOUTS commtimeouts; GetCommTimeouts (hCommFile, &commtimeouts); commtimeouts.ReadIntervalTimeout = 0; commtimeouts.ReadTotalTimeoutMultiplier = 0; commtimeouts.ReadTotalTimeoutConstant = timeout; commtimeouts.WriteTotalTimeoutMultiplier = 0; commtimeouts.WriteTotalTimeoutConstant = 0; SetCommTimeouts (hCommFile, &commtimeouts);
拜托,你能尝试从ThreadedRead中删除WaitCommEvent函数,看看它是否仍然挂起吗?
DWORD WINAPI ThreadedRead(LPVOID lpParam)
{
BOOL bResult;
DWORD dwBytesTransfered = 0;
PRP pReadArray;
pReadArray = (PRP)lpParam;
while (dwBytesTransfered == 0) {
bResult = ReadFile(m_hPortThreaded, pReadArray[0].readBuff, pReadArray[0].readCount, &dwBytesTransfered, 0);
Sleep(250);
}
return(bResult);
}
处理硬件 I/O 时,最佳做法是将应用程序 (GUI) 线程与命令执行线程分离。
如果您正在开发C++Win32应用程序,则可以使用SerialLib。它是一个古老但稳定的 Win32 事件驱动串行库。
- 从不同线程使用int64的不同字节安全吗
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 在C++中使用cURL和多线程
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 超时后,线程睡眠不会继续执行
- 串口读取功能的通信超时和线程不超时
- 是否可以使用标准的C 线程而不是FLTK超时来更新窗口
- async_connect()超时,多个线程执行io_service.run()
- 超时时暂停线程
- 使用Posix时间的Boost线程超时
- 如何唤醒从另一个线程选择没有超时时间的呼叫
- 多线程套接字超时
- 选择线程:超时和用户输入
- 等待线程超时:冻结
- 多线程:阻塞等待超时
- 带线程超时的Boost ASIO线程池
- 立即从另一个线程中止zeromq-recv()或poll(),而无需等待超时
- 带超时的多线程面向对象套接字代码
- 如何将超时设置为 MFC 线程
- 具有特定超时的STD线程