串口读取功能的通信超时和线程不超时

COMMTIMEOUT and Thread not timming out on serial port Read function

本文关键字:超时 线程 通信 读取 功能 串口      更新时间:2023-10-16

我正在调用一个通过串行端口操作 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);
}

最近我遇到了同样的问题,但我已经解决了。 有两种方法:

  1. 在论坛上,有些人建议将 ReadIntervalTimeout 和 ReadTotalTimeoutMultiplier 都设置为 MAXDWORD,如 MSDN 文档的 NOTES 部分所示。但在这种情况下,每次输入缓冲区中至少有一个字符时,函数都会返回。

  2. 我发现的最可靠的决定是将 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 事件驱动串行库。