https://blog.csdn.net/mushiqi4223/article/details/78618774
1、现象。
最近在做一个上位机与下位机的串口通信程序,使用的是MFC下调用CserialPort类的方法。上位机和下位机的程序编写好后,在调试过程中发现一个问题:串口连接后,点击按钮发送数据,第一次发送下位机接收后,第二次发送下位机就没有反应了。
2、分析。
最初我以为是下位机的接收有问题,于是通过串口助手代替上位机发送指令,下位机正常接收,所以就排除了下位机的问题。
既然确定是上位机的程序有问题,那就分析串口发送的程序。
void CmoveRobotbyfishDlg::OnClickedFront()
{
// TODO: Add your control notification handler code here
if (m_bSerialPortOpened)
{
char buffer[3] ;
buffer[0] = {0x61};
buffer[1] = {0x0d};
buffer[2] = {0x0a};
m_SerialPort.WriteToPort((LPSTR)(LPCTSTR)buffer, 3);
m_strEditSendMsg += " front \r\n";
this->SetDlgItemText(IDC_EDIT_SENDMSG, m_strEditSendMsg);
m_sendMsgShw.LineScroll(m_sendMsgShw.GetLineCount());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
上面代码是按钮对应的消息响应函数,通过WriteToPort函数发送3个char变量组成的数组,并在文本框中显示一些信息。
之前在网上看了一些帖子,有说是因为CserialPort类采用的是异步通信,因而引起线程相关的问题,试着在WriteToPort后面加了sleep(100)和AfxMessageBox(“”),但是还是没有解决问题。
于是只好查看WriteToPort函数了,该函数位于SerialPort.cpp文件中,在函数里面看到是通过SetEvent(m_hWriteEvent)函数触发写消息。再对SerialPort.cpp文件观察找到了监视线程(CommThread(LPVOID pParam))。
UINT CSerialPort::CommThread(LPVOID pParam)
{
......
......
Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);
......
switch (Event)
{
case 0:{......}
case 1: // read event
{......}
case 2: // write event
{
// Write character event from port
WriteChar(port);
break;
}
......
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
其中Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);是对读写等事件对象的判断,当返回值为2时,就进入case2的情况进行写操作。对 m_hEventArray变量跟踪发现它是一个HANDLE数组,在InitPort函数中,可以找到该变量的赋值情况。
// initialize the event objects响应优先级设置
m_hEventArray[0] = m_hShutdownEvent; // highest priority
m_hEventArray[1] = m_ov.hEvent; //包含读事件
m_hEventArray[2] = m_hWriteEvent;
- 1
- 2
- 3
- 4
这几个变量按顺序表示各种事件的响应优先级,可以看到包含读事件的m_ov.hEvent排在写事件m_hWriteEvent的前面,因此读的优先级高于写的优先级。通过断点调试发现程序执行一个写事件后自行进入一个读事件,也就是一直在等待读取信息,这时如果还要向串口写数据,由于读事件的优先级更高,所以不会响应。(到这里可以试着在下位机接收数据时加上一段给上位机返回发送数据的代码,会发现可以连续向下位机发送信息了。但是我们并不希望接收下位机的数据,所以得要另寻办法咯。)
3、解决方法
除了上面说到的下位机接收到数据自动返回一个值的办法以外,可以试着从修改InitPort初始化函数中的优先级入手。将写事件的优先级设置高于读事件。
// initialize the event objects响应优先级设置
m_hEventArray[0] = m_hShutdownEvent; // highest priority
m_hEventArray[1] = m_hWriteEvent;//m_hEventArray[1] = m_ov.hEvent;
m_hEventArray[2] = m_ov.hEvent;//m_hEventArray[2] = m_hWriteEvent;
- 1
- 2
- 3
- 4
同时由于数组m_hEventArray[3]发生了变化,所以Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE)中的返回值1和2也会交换,Event = 1时表示写事件,Event = 2表示读事件,因此switch (Event)判断中需要把case 1和case 2交换一下,就像下面。
UINT CSerialPort::CommThread(LPVOID pParam)
{
......
......
Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);
......
switch (Event)
{
case 0:{......}
case 2: // read event
{......}
case 1: // write event
{
// Write character event from port
WriteChar(port);
break;
}
......
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
好了,经过修改后,在调试运行,发现可以连续发送数据了。第一次写记录,希望以后能继续写下去,自己对串口的研究不是很深入,如果上面有什么地方错了,也希望大家指正。