[ MSComm 简介 ]
Visual C++为我们提供了一种好用的ActiveX控件Microsoft Communications Control(即MSComm)来支持应用程序对串口的访问,在应用程序中插入MSComm控件后就可以较为方便地实现对通过计算机串口收发数据。 要使用ActiveX控件MSComm,程序员必须将其添加入工程。
1.CSerialPort类和MSComm该怎么选择
这里首先要澄清一个疑问:为什么已经有了CSerialPort类,我们还要去研究MSComm控件?这是因为CSerialPort类有局限(非MODEM应用),要对MODEM进行控制,还需要对类进行改写。而MSComm控件是微软提供的,功能相对来说更完善,而且还可以对中文进行处理。
2.MSComm控件消息处理及基本属性
串行通信处理方式:
MSComm控件通信功能的实现,还是间接调用Windows API编程的结构。他提供了一系列标准通信命令的使用接口,利用它可以建立与串口的连接,并可以通过串口连接到其他设备,发出命令,交换数据以及见识和响应串行连接中发生的事故与错误。
目前,MSComm空间提供了“事件驱动”和“查询法”两种处理通讯问题的方法。
数据按驱动方式,是处理串行端口交互作用的一种非常有效的方法。在很多情况下,事件发生的时候要进行通知,这样我们就可以应用MSComm控件的OnComm()事件来捕获这些通信事件(当然啦,OnComm事件还可以检查和处理通信错误)。在实际的通信编程过程中,我们也正式在OnComm()事件处理函数中添加自己处理的代码,基于事件驱动-消息响应的方式可靠性极高!
查询方式,实质上还是事件驱动,在某些情况下该种方式可能更便捷。在程序的每个关键功能之后可以通过检查CommEvent属性值来查询事件和错误。
注意事项:
在使用MSComm控件时,1个MSComm控件只能同时对应一个串口。如果应用程序需要访问和控制多个串口,那么必须使用多个MSComm控件。
MSComm控件的属性介绍:
MSComm有很多重要的属性,但常用的属性只要如下表所示:
CommPort 设置并返回通信端口号
Settings 以字符串的形式设置并返回波特率、奇偶校验、
数据位、停止位
PortOpen 设置并返回通信端口状态/打开和关闭串口
Input 从接收缓冲区返回和删除字符
Output 向传输缓冲区写一个字符串1.CommPort属性
功能:设置并返回通信端口号。
2.RThreshold属性
功能:在MSComm控件设置CommEvent属性为comEvReceive并产生OnComm之前,设置并返回的要接收的字符数。
说明:接收缓冲区收到nNewValue个字符产生OnComm事件。当接收字符后,若nNewValue设置为0(缺省值),则不产生OnComm事件。例如:当nNewValue = 1 时,则接受缓冲区手袋一个字符就会使得CMSComm控件产生OnComm消息。
3.CTSHolding属性
功能:确定是否可以通过查询Clear To Send(CTS)线的状态发送数据。Clear To Send是调制解调器发送到相关联计算机的信号,指示传输可以进行。该属性在设计的时候无效,在运行的时候为只读。
4.SThreshold属性
功能:MSComm控件设置CommEvent属性为comEvSend并产生OnComm事件之前,设置并返回传输缓冲区中允许的最小字符数。
5.Handshaking属性
功能:设置或返回硬件握手状态,即设定串口通信设备之间的流控制。
6. InputMode属性
功能:设置或返回传输数据类型。
7.CDHolding属性
功能:通过查询Carrier Detect(CD)线的状态确定当前是否有传输。Carrier Detect是从调制解调器发送到相连计算机的一个信号,只是调制解调器正在联机,该属性设计时无效,运行时为只读。
8.DSRHolding属性
功能:确定Data Set Ready(DSR)线的状态。Data Set Ready信号由调制解调器发送到相连的计算机,只是做好操作准备。该属性在设计的时候无效,运行时为只读。
9.Setting属性
功能:设置并返回波特率、奇偶校验、数据位、停止位参数。
10.InputLen属性
功能:设置并返回Input属性从接收缓冲区读取的字符数。
11. InBufferSize属性
功能:设置或返回输入缓冲区的大小
12.InBufferCount属性
功能:设置或返回输入缓冲区内等待读取的字节个数
13.OutBufferSize属性
功能:设置或返回发送缓冲区的大小
14.OutBufferCount属性
功能:返回发送缓冲区的字节数,或者清空发送缓存区15.InPut属性
功能:从接收缓冲区内读取数据。
16.OutPut属性
功能:向发送缓冲区写数据,或者返回发送缓冲区当前的数据。
17.PortOpen属性
功能:用于打开或关闭串口,或者返回串口的开闭状态。
18.EOFEnable属性
功能:确定在输入过程中MSComm控件是否寻找文件结尾字符(EndOfFile),如果找到EOF字符,将停止输入并激活OnComm事件,此时CommEvent属性设置为ComEvEOF。
19. DTREnable属性
功能:设置或返回Data Terminal Ready(DTR)线状态。确定在通信时是否DTR线有效。DTR是计算机发送到调制解调器的信号,指示计算机在等待接受传输。
20. RTSEnable属性
功能:确定是否是Request To Send(RTS)有效,一般情况下,由计算机发送RTS信号到连接好的调制解调器,已请示允许发送数据。
21.Break属性
功能:设置或清除中断信号状态。该属性在设计时无效。
22.CommID属性
功能:返回一个说明通行设备的句柄。该属性在设计时无效,在运行时为只读。
23.NULLDiscard属性
功能:确定Null字符是否是从端口传送到接收缓冲区。
24.OnComm事件和CommEvent属性
功能:设置或返回无论何时当CommEvent属性值发生变化时,就会产生OnComm事件,标志发生了一个通信事件或一个错误。CommEvent属性值表示错误或者是事件类型,通常在程序中的时间消息处理函数中对CommEvent事件进行处理。
[ 添加MSComm ]
在项目菜单栏中选择“工具”菜单,在弹出的窗口中选择“选择工具箱选项”在“COM组件”中选择“Microsoft Communications Control,Version 6.0(SP6)”前的单选框,然后点击“确定”按钮将MSComm添加到工具箱。
如下图所示是已经添加完成MSComm控件后的工具箱,工具箱中会出现一个电话式样的图标,该图标就是MSComm控件。
将MSComm控件拖入到对话框上面,并将其属性中的ID改为“IDC_MSCOMMPORT”。如下图所示。
对MSComm控件增加变量“m_MSCommControl”。
在对话框中增加5个Combo-Box Control控件用于设置串口通信的串口号、通信波特率、校验位、停止位和数据位。
序号 控件ID 功能 1 IDC_COMBOMSCPORT 串口选择 2 IDC_COMBOMSCSEQ 波特率选择 3 IDC_COMBOMSCCHKBIT 校验位选择 4
IDC_COMBOMSCSTOPBIT 停止位选择 5 IDC_COMBOMSCDATABIT 数据位选择
为5个Combo-Box Control控件添加变量。
序号 控件ID 变量名称 1 IDC_COMBOMSCPORT m_MscPort 2 IDC_COMBOMSCSEQ m_MscBaud 3 IDC_COMBOMSCCHKBIT m_MscCheckBit 4
IDC_COMBOMSCSTOPBIT m_MscStopBit 5 IDC_COMBOMSCDATABIT m_MscDataBitLength
【打开串口 】
在对话框上增加一个按钮,按钮功能为打开串口,按钮ID:IDC_BUTTONMSCOPENPORT,按钮的双击事件如下:
void CMFCApplicationMSCommPortDlg::OnBnClickedButtonmscopenport()
{
int m_McsPortNum;
m_McsPortNum = 0;
CString m_strMscBaud, m_strMscCkBit, m_strMscStopBit, m_strMscDataBit;
CString m_PortSetStr;
switch (m_MscPort.GetCurSel())
{
case 0:
m_McsPortNum = 1;
break;
case 1:
m_McsPortNum = 2;
break;
case 2:
m_McsPortNum = 3;
break;
case 3:
m_McsPortNum = 4;
break;
case 4:
m_McsPortNum = 5;
break;
case 5:
m_McsPortNum = 6;
break;
case 6:
m_McsPortNum = 7;
break;
case 7:
m_McsPortNum = 8;
break;
case 8:
m_McsPortNum = 9;
break;
default:
break;
}
switch (m_MscCheckBit.GetCurSel())
{
case 0:
m_strMscCkBit=_T("n");
break;
case 1:
m_strMscCkBit=_T("o");
break;
case 2:
m_strMscCkBit=_T("e");
break;
default:
break;
}
GetDlgItemText(IDC_COMBOMSCSEQ,m_strMscBaud);
GetDlgItemText(IDC_COMBOMSCSTOPBIT, m_strMscStopBit);
GetDlgItemText(IDC_COMBOMSCDATABIT, m_strMscDataBit);
unsigned char *ch;
ch = NULL;
int lt = 0;
printf("m_McsPortNum=%d\n", m_McsPortNum);
ch=CstrToConstChar(m_strMscBaud, lt);
printf("m_strMscBaud=%s\n", ch);
ch = CstrToConstChar(m_strMscStopBit, lt);
printf("m_strMscStopBit=%s\n", ch);
ch = CstrToConstChar(m_strMscDataBit, lt);
printf("m_strMscDataBit=%s\n", ch);
ch = CstrToConstChar(m_strMscCkBit, lt);
printf("m_strMscCkBit=%s\n", ch);
m_PortSetStr = m_strMscBaud +_T(",") +m_strMscCkBit + _T(",") + m_strMscDataBit + _T(",") + m_strMscStopBit;
ch = CstrToConstChar(m_PortSetStr, lt);
printf("put_Settings(%s)\n", ch);
m_MSCommControl.put_InBufferSize(2048);
m_MSCommControl.put_OutBufferSize(2048);
m_MSCommControl.put_Settings(m_PortSetStr);
m_MSCommControl.put_InputMode(1);
if (m_MSCommControl.get_PortOpen())
{
m_MSCommControl.put_PortOpen(FALSE);
}
m_MSCommControl.put_CommPort(m_McsPortNum);
if (!m_MSCommControl.get_PortOpen())
{
m_MSCommControl.put_PortOpen(TRUE);
m_MscPortStatusFlag.RefreshWindow(SENSOR_ON);
printf("COM%d is Opened Succeed\n", m_McsPortNum);
SetTimer(1, 500, NULL);
m_MscSendManual.EnableWindow(TRUE);
m_MscSendManual.SetButtonTextColor(COLOR_BUTTON_DOWN_TEXT, COLOR_BUTTON_UP_TEXT);
m_MscPortOpen.EnableWindow(FALSE);
m_MscPortOpen.SetButtonTextColor(COLOR_BUTTON_DISABLE_TEXT, COLOR_BUTTON_DISABLE_TEXT);
m_MscPortClose.EnableWindow(TRUE);
m_MscPortClose.SetButtonTextColor(COLOR_BUTTON_DOWN_TEXT, COLOR_BUTTON_UP_TEXT);
m_MscSendManual.Invalidate();
m_MscPortOpen.Invalidate();
m_MscPortClose.Invalidate();
}
else
{
MessageBox(_T("Can not open serial port"),_T("信息提示!") ,MB_ICONERROR|MB_OKCANCEL);
printf("COM%d Can not open !\n", m_McsPortNum);
}
m_MSCommControl.put_RThreshold(2);
m_MSCommControl.put_SThreshold(2);
m_MSCommControl.put_InputLen(0);
m_MSCommControl.get_Input();
}
void CMFCApplicationMSCommPortDlg::OnBnClickedButtonmscclose()
{
if (m_MSCommControl.get_PortOpen())
{
m_MSCommControl.put_PortOpen(FALSE);
KillTimer(1);
m_MscPortStatusFlag.RefreshWindow(SENSOR_OFF);
m_MscSendStatusFlag.RefreshWindow(SENSOR_OFF);
m_MscStatusRecvStatusFlag.RefreshWindow(SENSOR_OFF);
m_MscSendManual.EnableWindow(FALSE);
m_MscSendManual.SetButtonTextColor(COLOR_BUTTON_DISABLE_TEXT, COLOR_BUTTON_DISABLE_TEXT);
m_MscPortClose.EnableWindow(FALSE);
m_MscPortClose.SetButtonTextColor(COLOR_BUTTON_DISABLE_TEXT, COLOR_BUTTON_DISABLE_TEXT);
m_MscPortOpen.EnableWindow(TRUE);
m_MscPortOpen.SetButtonTextColor(COLOR_BUTTON_DOWN_TEXT, COLOR_BUTTON_UP_TEXT);
m_MscSendManual.Invalidate();
m_MscPortOpen.Invalidate();
m_MscPortClose.Invalidate();
}
printf("COM%d is Closed Succeed\n", m_MSCommControl.get_CommPort());
}
【串口事件】
为MSComm控件增加OnComm处理函数:在类向导中选择“命令”、选择ID为IDC_MSCOMMPORT的控件为其增加OnComm消息处理函数。
void CMFCApplicationMSCommPortDlg::OnOncommMscommport()
{
VARIANT m_Variant_Rec;
COleSafeArray m_SafeArray_Rec;
LONG m_DataLenth, nCount;
const int m_RecByteLenth = 2048;
BYTE m_RecDataByte[m_RecByteLenth];
CString m_TemDataStr, strRevBuff, strSendBuff,strRevByte,strSendByte,strRxCount;
if (m_MSCommControl.get_CommEvent() == 1)
{
//strSendByte.Format(_T("%d"), m_MSCommControl.get_OutBufferCount());
//printf("get_OutBufferCount=%d\n", m_MSCommControl.get_OutBufferCount());
//m_SendByteCount.SetWindowText(_T("get_OutBufferCount:") + strSendByte);
m_SendMsg = TRUE;
PostMessage(WM_SENDMSG, WPARAM(m_SendMsg), 0);
printf("get_CommEvent=%d\n", m_MSCommControl.get_CommEvent());
}
else if (m_MSCommControl.get_CommEvent() == 2)
{
m_SendMsg = FALSE;
PostMessage(WM_SENDMSG, WPARAM(m_SendMsg), 0);
}
if (m_MSCommControl.get_CommEvent()==2)
{
strRevBuff.Format(_T("%d"), m_MSCommControl.get_InBufferSize());
printf("get_InBufferSize=%d\n", m_MSCommControl.get_InBufferSize());
strSendBuff.Format(_T("%d"), m_MSCommControl.get_OutBufferSize());
printf("get_OutBufferSize=%d\n", m_MSCommControl.get_OutBufferSize());
strRevByte.Format(_T("%d"), m_MSCommControl.get_InBufferCount());
printf("get_InBufferCount=%d\n", m_MSCommControl.get_InBufferCount());
InCount= InCount+m_MSCommControl.get_InBufferCount();
printf("InCount=%I64u\n", InCount);
strRxCount.Format(_T("%I64u"), InCount);
m_RxCount.SetWindowText(_T("RxCount:") + strRxCount);
m_InPutBuffSize.SetWindowText(_T("get_InBufferSize:") + strRevBuff);
m_OutPutBuffSize.SetWindowText(_T("get_OutBufferSize:") + strSendBuff);
m_RecByteCount.SetWindowText(_T("get_InBufferCount:") + strRevByte);
m_Variant_Rec = m_MSCommControl.get_Input();
m_SafeArray_Rec = m_Variant_Rec;
m_DataLenth = m_SafeArray_Rec.GetOneDimSize();
for (nCount = 0; nCount < m_DataLenth; nCount++)
m_SafeArray_Rec.GetElement(&nCount,m_RecDataByte+ nCount);
for (nCount = 0; nCount < m_DataLenth; nCount++)
{
BYTE m_Buff = *(char*)(m_RecDataByte + nCount);
if (m_RevByHexEnable==TRUE)
{
m_TemDataStr.Format(_T("%X"), m_Buff);
m_strRec += m_TemDataStr+_T(" ");
}
else
{
m_TemDataStr.Format(_T("%C"), m_Buff);
m_strRec += m_TemDataStr;
}
}
m_RecvMsg = TRUE;
PostMessage(WM_RECVMSG, WPARAM(m_RecvMsg), 0);
printf("get_CommEvent=%d\n", m_MSCommControl.get_CommEvent());
m_strRec += _T("\r\n");
UpdateData(FALSE);
}
else if (m_MSCommControl.get_CommEvent() == 1)
{
m_RecvMsg = FALSE;
PostMessage(WM_RECVMSG, WPARAM(m_RecvMsg), 0);
}
m_RevConEdit.LineScroll(m_RevConEdit.GetLineCount());
m_SendConEdit.LineScroll(m_SendConEdit.GetLineCount());
if (m_RevConEdit.GetLineCount()>=50)
{
m_RevConEdit.SetSel(0, -1);
m_RevConEdit.Clear();
m_strRec = _T("");
}
if (m_SendConEdit.GetLineCount() >= 50)
{
m_SendConEdit.SetSel(0, -1);
m_SendConEdit.Clear();
m_MscSendData= _T("");
}
m_MSCommControl.put_InBufferCount(0);
m_MSCommControl.put_OutBufferCount(0);
}
【例程测试】
通过Configure Virtual Serial Port Driver 虚拟串口工具先虚拟出一对虚拟串口,如下图所示为“COM2”和“COM3”。
网上下载一个串口调试助手,通过调试助手打开“COM4”。选择自动发送,并在发送输入框中输入要发送的信息本例以“ABCDEFGHIJKLMN”为例子进行说明。
运行例程,打开串口“COM2”选择相应的通信参数后。点击按钮“MSC打开串口”。如下图所示。并在发送输入框中输入信息“This is Com2 Send Test Demo!”,从串口调试助手中可以看到自己开发的串口程序所发送的信息,也能看到串口调试助手发送给自己开发串口成的信息。
选择“RevHex”单选框可以选择已16进制进行接收如下图所示,其他功能可以在后续继续完善,比如增加CRC16校验等功能。
MSComm串口通信通过虚拟串口能很大程度上方便两个互不开放源码的程序之间通过串口的方式发送数据,或者两台IPC之间进行通信,开发成本低,开发周期短只要稍微对MFC熟悉的人都可以通过简单的例程来完成整个复杂的通信过程。
完整代码:
代码内有封装好的一些控件库,封装过程和源码在往期的例程中都有。
https://download.csdn.net/download/lzc881012/86502422?spm=1001.2014.3001.5503https://download.csdn.net/download/lzc881012/86502422?spm=1001.2014.3001.5503