我在Visual C ++ 2008中开发了一个应用程序,用于从COM端口定期(50ms)读取数据。为了定期读取数据,我将读取函数放在OnTimer
函数中,因为我不希望GUI的其余部分挂起,所以我在一个线程中调用了这个定时器函数。我已将代码放在下面。
应用程序运行正常,但它显示以下意外行为:在数据源(硬件设备甚至数据模拟器)停止发送数据后,我的应用程序继续接收数据一段时间,该比例与读取功能运行了多长时间(编辑:此超出期间与发送数据的时间段相同)。因此,如果我立即启动和停止数据流,这将反映在我的GUI上,但如果我启动数据流并在十秒后停止,我的GUI将继续显示数据10秒钟(已编辑)。
在用完所有调试尝试后,我做了以下观察:
abBuffer
,它只是一个固定大小的字节数组。我不认为这会增加大小,所以这些数据存储在某个地方。以下是我的主题和计时器代码:
UINT CMyCOMDlg::StartThread(LPVOID param)
{
THREADSTRUCT *ts = (THREADSTRUCT*)param;
ts->_this->SetTimer(1,50,0);
return 0;
}
//Timer function that is called at regular intervals
void CMyCOMDlg::OnTimer(UINT_PTR nIDEvent)
{
if(m_bCount==true)
{
DWORD NoBytesRead;
BYTE abBuffer[45];
if(ReadFile((m_hComm),&abBuffer,45,&NoBytesRead,0))
{
if(NoBytesRead==45)
{
if(abBuffer[0]==0x10&&abBuffer[1]==0x10||abBuffer[0]==0x80&&abBuffer[1]==0x80)
{
fnSetData(abBuffer);
}
else
{
CString value;
value.Append("Header match failed");
SetDlgItemText(IDC_RXRAW,value);
}
}
else
{
CString value;
value.Append(LPCTSTR(abBuffer),NoBytesRead);
value.Append("\r\nInvalid Packet Size");
SetDlgItemText(IDC_RXRAW,value);
}
}
else
{
DWORD dwError2 = GetLastError();
CString error2;
error2.Format(_T("%d"),dwError2);
SetDlgItemText(IDC_RXRAW,error2);
}
fnClear();
}
else
{
KillTimer(1);
}
CDialog::OnTimer(nIDEvent);
}
m_bCount
只是我用来杀死计时器的标志,ReadFile
函数是标准的Windows API调用。 ts
是一个包含指向主对话框类的指针的结构,即this
。
任何人都可以想到这可能发生的原因吗?我已经尝试了很多东西,而且我的代码也做得很少,我无法弄清楚这种意外行为发生在哪里。
编辑:
我正在添加下面使用的COM端口设置和超时:
dcb.BaudRate = CBR_115200;
dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT;
dcb.Parity = NOPARITY;
SetCommState(m_hComm, &dcb);
_param->_this=this;
COMMTIMEOUTS timeouts;
timeouts.ReadIntervalTimeout=1;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 10;
timeouts.WriteTotalTimeoutMultiplier = 1;
timeouts.WriteTotalTimeoutConstant = 1;
SetCommTimeouts(m_hComm, &timeouts);
答案 0 :(得分:0)
您正在OnTimer()函数中一次处理一条消息。由于定时器间隔为1秒,但数据源每50毫秒继续发送一次消息,因此您的应用程序无法及时处理所有消息。
您可以添加 while 循环,如下所示:
while(true)
{
if(::ReadFile(m_hComm, &abBuffer, sizeof(abBuffer), &NoBytesRead, 0))
{
if(NoBytesRead == sizeof(abBuffer))
{
...
}
else
{
...
break;
}
}
else
{
...
break;
}
}
但您的代码中还有另一个问题。如果您的软件在数据源仍在发送消息时检查消息,则 NoBytesRead 可能小于45.您可能希望将数据存储到消息缓冲区中,如 CString 或 std :: queue< unsigned char> 。
如果邮件末尾没有包含 NULL 的邮件,则将邮件传递给 CString 对象是不安全的。
此外,如果第一个字节从0x80开始, CString 会将其视为多字节字符串。它可能会导致错误。如果消息不是文字文本字符串,请考虑使用其他数据格式,如 std :: vector< unsigned char> 。
顺便说一句,您不需要在单独的线程中调用 SetTimer()。踢定时器不需要时间。另外,我建议您在 OnTimer()函数之外的某处调用 KillTimer(),以便代码更直观。
如果数据源持续不断地发送数据,则在打开/关闭COMM端口时可能需要使用 PurgeComm()。