标签:
串口通信简介
一般来说,计算机都有一个或多个串行端口,这些串口提供了外部设备与PC进行数据传输和通信的通道,在CPU和外设之间充当解释器的角色。当字符数据从CPU发送给外设时,这些字符数据将被转换成串行比特流数据;当接收数据时,比特流数据被转换为字符数据传递给CPU,再进一步说,在操作系统方面,Windows用通信驱动程序(COMM.DRV)调用API函数发送和接收数据;当用通信控件或声明调用API函数时,它们由COMM.DRV解释并传递给设备驱动程序。作为一个程序员,要编写通信程序,只需知道通信控件提供的Windows API通信函数的接口即可,换句话说,只需设定和监视通信控件的属性和事件即可。
串口通信方法一般有以下几种:
我在项目开发过程中用的是第三种方法——通过MSComm控件操作串口,下面是我使用此控件的笔记。
MSComm控件简介
MSComm 是 Microsoft 公司为简化Windows下串行端口编程而提供的ActiveX控件,它提供了一系列标准通讯命令的使用接口。MSComm 控件通过串行端口(serial port)传送和接收数据,为应用程序提供了串行通讯功能。在可视化编程盛行的今天,我们可以很方便的在Visual Basic(VB)、Visual C++(VC)、Delphi等语言及开发平台中应用。处理数据的方式有事件驱动(Event-driver)、查询法(Inquire)两种。
事件驱动法:在使用事件驱动法设计程序时,每当有新字符到达、端口状态变化或发生错误时,MSComm控件将触发OnComm事件,而应用程序在捕获该事件后,通过检查MSComm控件的CommEvent属性可以获知所发生的事件或错误,从而采取相应的操作。这种方法的优点是程序响应及时,可靠性高。
查询法:这种方法适合于较小的应用程序。在这种情况下,每当应用程序执行完某一串行口操作后,将不断检查MSComm控件的CommEvent属性以检查执行结果或者检查某一事件是否发生。例如,当程序向串行设备发送了某个命令后,可能只是在等待收到一个特定的响应字符串,而不是对收到的每一个字符都立刻响应并处理。
使用的每个MSComm控件都与一个串口对应。如果在应用程序中需要访问多个串口,必须使用多个MSComm控件,可以在Windows 控制面板中修改串口地址的中断地址。
MSComm控件的常用属性
MSComm控件的事件
MSCOMM控件只使用一个事件OnComm,用属性CommEvent的17个值来区分不同的触发时机,主要有以下几个:
另外十种情况是通信错误时产生,即错误代码。
基于VS2010下MFC的MSComm串口程序的实现
1、注册MSComm控件
我在网上下载了MSComm控件之后,将其放于项目目录下,并在当前目录建了个.bat批处理文件,其内容如下:
copy .\\MSCOMM\\MSCOMM.SRG %windir%\system32 copy .\\MSCOMM\\MSCOMM32.DEP %windir%\system32 copy .\\MSCOMM\\MSCOMM32.oca %windir%\system32 copy .\\MSCOMM\\mscomm32.ocx %windir%\system32 regsvr32 mscomm32.ocx
双击此文件,即可注册MSComm控件。
2、添加MSComm控件
首先将MSComm控件添加进VS2010工具箱,再给项目添加该ActiveX控件对应的“基于MFC的ATL类”,最后将工具箱中的MSComm控件(电话图标)拖至对话框即可。在对话框中添加MSComm控件后,其侧面会有白色,右击此控件,选择“编辑控件”,即可去除白色。
3、添加控件变量
在主对话框中添加与MSComm控件相关联的控件变量(成员对象),通过此成员变量可操作串口。
4、串口信息配置及打开串口
在对话框模板上右击MSComm控件,选择Property菜单项,即可设置MSComm控件各项属性。在调制解调器通讯的程序中,设置“Control”属性页中Handshaking项为“2-comRTS”,否则国内部分厂家modem不能正常通讯,其它接受缺省设置。另外亦可通过修改对话框类的OnInitDialog()函数来设置控件的属性。具体参考MSDN中的关于Comm Control的详细说明。
我程序的串口设置代码大致如下:
//*********************** 串口设置 **************************// m_ctrlComm.put_CommPort(port);//选择com口 m_ctrlComm.put_InputMode(1);//输入方式为二进制方式 m_ctrlComm.put_InBufferSize(1024);//输入缓冲区大小为1024byte m_ctrlComm.put_OutBufferSize(512);//输出缓冲区大小为512byte CString strBaudrate; strBaudrate.Format(_T("%ld"),baudrate); m_ctrlComm.put_Settings(strBaudrate+_T(",n,8,1"));//设置串口参数:9600波特率,无奇偶校验,8个数据位,1个停止位 if(!m_ctrlComm.get_PortOpen()) { /* HANDLE m_hCom; CString strCom; strCom.Format(_T("\\\\.\\COM%d"),(int)(m_ctrlComm.get__CommPort())); // 这里的CreateFile函数起了很大的作用,可以用来创建系统设备文件, //如果该设备不存在或者被占用,则会返回一个错误,即下面的 INVALID_HANDLE_VALUE , //据此可以判断可使用性。详细参见MSDN中的介绍。 m_hCom = CreateFile(strCom, 0, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if(m_hCom == INVALID_HANDLE_VALUE)//如果没有该设备,或者被其他应用程序在用 { int errornum=GetLastError(); if(errornum==2) strCom.Format(_T("端口%d 不存在"),(int)(m_ctrlComm.get__CommPort())); else if(errornum==5) strCom.Format(_T("端口%d被占用"),(int)(m_ctrlComm.get__CommPort())); AfxMessageBox(strCom); CloseHandle(m_hCom); // 关闭文件句柄,后面我们采用控件,不用API return ;//这是因为串口初始化封装在另一个函数里面在OnInitDialog调用。 } CloseHandle(m_hCom); // 关闭文件句柄,后面我们采用控件,不用API */ try { m_ctrlComm.put_PortOpen(true);//打开串口 } catch(COleDispatchException *e) { CString strError; strError.Format(_T("打开串口失败!\n\nError Number: %d \nError Message: %s"), e->m_wCode,e->m_strDescription); MessageBoxW(strError,_T("错误提示"),MB_ICONERROR); return; } } else { //MessageBox(_T("Cannot open serial port!")); } m_ctrlComm.put_RThreshold(1);//每当串口接收缓冲区有多余或等于1个字符时将引发一个接收数据的oncomm事件 m_ctrlComm.put_InputLen(0);//设置当前接收区数据长度为0 m_ctrlComm.get_Input();//预读缓冲区以清空残留数据
5、串口数据的读写
MSComm 类的读写函数比较简单:get_Input()和put_Output()。函数原形分别为VARIANT get_Input()和void put_Output(const VARIANT newValue),均使用VARIANT类型。但PC机发送和接收数据时习惯用字符串形式。MSDN中查阅VARIANT类型,可以用BSTR表示字符串,但所有的BSTR都包含宽字符,而只有Windows NT支持宽字符,Windows 9X并不支持。所以要完成一个适应各平台的串口应用程序必须解决这个问题,这里使用CByteArray解决之。
添加接收数据函数,在对话框中双击Comm Control,接受默认函数,则对话框类的成员函数为OnCommMscomm(),其大致代码如下:
CDataTypeConverter DTC; //电话图标可能有一半白边去不了,右击电话图标点击edit control就可以去掉 if(m_ctrlComm.get_CommEvent()==2)//事件值为2表示接收事件 { BYTE rxdata[255]={0};//设置BYTE数组 VARIANT variant_inp=m_ctrlComm.get_Input();//读缓冲区 COleSafeArray safearray_inp = variant_inp;//VARIANT型变量转换为COleSafeArray变量 long len=safearray_inp.GetOneDimSize();//得到有效数据长度 for(long k=0;k<len;k++) safearray_inp.GetElement(&k,rxdata+k);//转换为BYTE数组 m_ctrlComm.put_OutBufferCount(0);//清空发送缓冲区 m_ctrlComm.put_InBufferCount(0);//滑空接收缓冲区 safearray_inp.Clear(); for(long k=0;k<len;k++) { BYTE bt = *(char*)(rxdata+k);//字符型 short int intDec=(int)bt; CString strtemp=DTC.Dec2Hex(intDec); m_strDataRXTemp+=strtemp;//加入接收编辑框对应字符串 } m_strDataRX=m_strDataRXTemp; m_strDataRXTemp="";
}
其中,Dec2Hex()函数的代码如下:
CString CDataTypeConverter::Dec2Hex(unsigned int intDec) { CString strHex; char charHex[255]; sprintf(charHex,"%x",intDec); strHex=charHex; if(strHex.GetLength()==1) strHex=_T("0")+strHex; return strHex; }
发送数据的代码大致如下:
//UpdateData(true);//读取编辑框内容m_strDataTX //发送的字符串上表面为十六进制格式 CString m_strCtrlLightBL; m_strCtrlLightBL="55AA0AAA6B4310100000";//"55aa0aaa6b4310100000" CDataTypeConverter DTC; COleVariant m_OleVariant=DTC.HexM2OleVariant(m_strCtrlLightBL); m_ctrlComm.put_Output(m_OleVariant);//发送数据
其中,HexM2OleVariant()函数定义如下:
COleVariant CDataTypeConverter::HexM2OleVariant(CString strHexM) { BYTE bt[255]; short int len=strHexM.GetLength(); short int length=0; short int intDec; for(int n=0,i=0;n<len-1;n+=2,i++) { intDec=Hex2Dec(strHexM.Mid(n,2)); bt[i]=char(intDec); length=i+1; } CByteArray m_Array; m_Array.RemoveAll(); m_Array.SetSize(length); for(int i=0;i<length;i++) m_Array.SetAt(i,bt[i]); return COleVariant(m_Array); }
注意:接收数据时,RThreshold属性很重要,因为它影响着OnComm事件的触发条件,在程序中可以通过put_RThreshold()函数来设定RThreshold属性。
相关链接:
深入浅出VC++串口编程(五) 基于第三方类库:http://blog.csdn.net/nash635/article/details/5339704
标签:
原文地址:http://www.cnblogs.com/gaohongchen01/p/4432922.html