您好,欢迎来到微智科技网。
搜索
您的当前位置:首页串口通讯

串口通讯

来源:微智科技网
串行通信的基本原理及用MFC实现串口通信编程vc++技术

在Windows应用程序的开发中,我们常常需要面临与外围数据源设备通信的问题。计算机和单片机(如MCS-51)都具有串行通信口,可以设计相应的串口通信程序,完成二者之间的数据通信任务。

实际工作中利用串口完成通信任务的时候非常之多。已有一些文章介绍串口编程的文章在计算机杂志上发表。但总的感觉说来不太全面,特别是介绍32位下编程的更少,且很不详细。笔者在实际工作中积累了较多经验,结合硬件、软件,重点提及比较新的技术,及需要注意的要点作一番探讨。希望对各位需要编写串口通信程序的朋友有一些帮助。

一.串行通信的基本原理

串行端口的本质功能是作为CPU和串行设备间的编码转换器。当数据从 CPU经过串行端口发送出去时,字节数据转换为串行的位。在接收数据时,串行的位被转换为字节数据。

在Windows环境(Windows NT、Win98、Windows2000)下,串口是系统资源的一部分。

应用程序要使用串口进行通信,必须在使用之前向操作系统提出资源申请要求(打开串口),通信完成后必须释放资源(关闭串口)。

串口通信程序的流程如下图:

二.串口信号线的接法

一个完整的RS-232C接口有22根线,采用标准的25芯插头座(或者9芯插头座)。25芯和9芯的主要信号线相同。以下的介绍是以25芯的RS-232C为例。

①主要信号线定义:

2脚:发送数据TXD; 3脚:接收数据RXD; 4脚:请求发送RTS; 5脚:清除发送CTS;

6脚:数据设备就绪DSR;20脚:数据终端就绪DTR; 8脚:数据载波检测DCD;

1脚:保护地; 7脚:信号地。

②电气特性:

数据传输速率最大可到20K bps,最大距离仅15m.

注:看了微软的MSDN 6.0,其Windows API中关于串行通讯设备(不一定都

是串口RS-232C或RS-422或RS-449)速率的设置,最大可支持到RS_256000,即256K bps! 也不知道到底是什么串行通讯设备?但不管怎样,一般主机和单片机的串口通讯大多都在9600 bps,可以满足通讯需求。

③接口的典型应用:

大多数计算机应用系统与智能单元之间只需使用3到5根信号线即可工作。这时,除了TXD、RXD以外,还需使用RTS、CTS、DCD、DTR、DSR等信号线。(当然,在程序中也需要对相应的信号线进行设置。)

以上接法,在设计程序时,直接进行数据的接收和发送就可以了,不需要 对信号线的状态进行判断或设置。(如果应用的场合需要使用握手信号等,需要对相应的信号线的状态进行监测或设置。)

三.16位串口应用程序的简单回顾

16位串口应用程序中,使用的16位的Windows API通信函数:

① OpenComm() 打开串口资源,并指定输入、输出缓冲区的大小(以字节计);

CloseComm() 关闭串口;

例:int idComDev;

idComDev = OpenComm(\"COM1\

CloseComm(idComDev);

② BuildCommDCB() 、setCommState()填写设备控制块DCB,然后对已打开的串口进行参数配置;

例:DCB dcb;

BuildCommDCB(\"COM1:2400,n,8,1\

SetCommState(&dcb);

③ ReadComm 、WriteComm()对串口进行读写操作,即数据的接收和发送.

例:char *m_pRecieve; int count;

ReadComm(idComDev,m_pRecieve,count);

Char wr[30]; int count2;

WriteComm(idComDev,wr,count2);

16位下的串口通信程序最大的特点就在于:串口等外部设备的操作有自己特有的API函数;而32位程序则把串口操作(以及并口等)和文件操作统一起来了,使用类似的操作。

四.在MFC下的32位串口应用程序

32位下串口通信程序可以用两种方法实现:利用ActiveX控件;使用API 通信函数。

使用ActiveX控件,程序实现非常简单,结构清晰,缺点是欠灵活;使用API 通信函数的优缺点则基本上相反。

以下介绍的都是在单文档(SDI)应用程序中加入串口通信能力的程序。

㈠ 使用ActiveX控件:

VC++ 6.0提供的MSComm控件通过串行端口发送和接收数据,为应用程序提供串行通信功能。使用非常方便,但可惜的是,很少有介绍MSComm控件的资料。

⑴.在当前的Workspace中插入MSComm控件。

Project菜单------>Add to Project---->Components and Controls----->Registered

ActiveX Controls--->选择Components: Microsoft Communications Control,

version 6.0 插入到当前的Workspace中。

结果添加了类CMSComm(及相应文件:mscomm.h和mscomm.cpp )。

⑵.在MainFrm.h中加入MSComm控件。

protected:

CMSComm m_ComPort;

在Mainfrm.cpp::OnCreare()中:

DWORD style=WS_VISIBLE|WS_CHILD; if (!m_ComPort.Create(NULL,style,CRect(0,0,0,0),this,ID_COMMCTRL)){

TRACE0(\"Failed to create OLE Communications Control \");

return -1; // fail to create

}

⑶.初始化串口

m_ComPort.SetCommPort(1); //选择COM?

m_ComPort. SetInBufferSize(1024); //设置输入缓冲区的大小,Bytes

m_ComPort. SetOutBufferSize(512); //设置输入缓冲区的大小,Bytes//

if(!m_ComPort.GetPortOpen()) //打开串口

m_ComPort.SetPortOpen(TRUE);

m_ComPort.SetInputMode(1); //设置输入方式为二进制方式

m_ComPort.SetSettings(\"9600,n,8,1\"); //设置波特率等参数

m_ComPort.SetRThreshold(1); //为1表示有一个字符引发一个事件

m_ComPort.SetInputLen(0);

⑷.捕捉串口事项。MSComm控件可以采用轮询或事件驱动的方法从端口获取数据。我们介绍比较使用的事件驱动方法:有事件(如接收到数据)时通知程序。在程序中需要捕获并处理这些通讯事件。

在MainFrm.h中:

protected:

afx_msg void OnCommMscomm();

DECLARE_EVENTSINK_MAP()

在MainFrm.cpp中:

BEGIN_EVENTSINK_MAP(CMainFrame,CFrameWnd )

ON_EVENT(CMainFrame,ID_COMMCTRL,1,OnCommMscomm,VTS_NONE)

//映射ActiveX控件事件

END_EVENTSINK_MAP()

⑸.串口读写. 完成读写的函数的确很简单,GetInput()和SetOutput()就可。两个函数的原型是:

VARIANT GetInput();及 void SetOutput(const VARIANT& newValue);都要使用VARIANT类型(所有Idispatch::Invoke的参数和返回值在内部都是作为VARIANT对象处理的)。

无论是在PC机读取上传数据时还是在PC机发送下行命令时,我们都习惯于使用字符串的形式(也可以说是数组形式)。查阅VARIANT文档知道,可以用BSTR表示字符串,但遗憾的是所有的BSTR都是包含宽字符,即使我们没有定义_UNICODE_UNICODE也是这样! WinNT支持宽字符, 而Win95并不支持。为解决上述问题,我们在实际工作中使用CbyteArray,给出相应的部分程序如下:

void CMainFrame::OnCommMscomm(){

VARIANT vResponse; int k;

if(m_commCtrl.GetCommEvent()==2) {

k=m_commCtrl.GetInBufferCount(); //接收到的字符数目

if(k>0) {

vResponse=m_commCtrl.GetInput(); //read

SaveData(k,(unsigned char*) vResponse.parray->pvData);

} // 接收到字符,MSComm控件发送事件 }

。。。。。 // 处理其他MSComm控件 }

void CMainFrame::OnCommSend() {

。。。。。。。。 // 准备需要发送的命令,放在TxData[]中

CByteArray array;

array.RemoveAll();

array.SetSize(Count);

for(i=0;i

array.SetAt(i, TxData[i]);

m_ComPort.SetOutput(COleVariant(array)); // 发送数据 }

请大家认真关注第⑷、⑸中内容,在实际工作中是重点、难点所在。

㈡ 使用32位的API 通信函数:

可能很多朋友会觉得奇怪:用32位API函数编写串口通信程序,不就是把16位的API换成32位吗?16位的串口通信程序可是多年之前就有很多人研讨过了……

此文主要想介绍一下在API串口通信中如何结合非阻塞通信、多线程等手段,编写出高质量的通信程序。特别是在CPU处理任务比较繁重、与外围设备中有大量的通信数据时,更有实际意义。

⑴.在中MainFrm.cpp定义全局变量

HANDLE hCom; // 准备打开的串口的句柄

HANDLE hCommWatchThread ;//辅助线程的全局函数

⑵.打开串口,设置串口

hCom =CreateFile( \"COM2\允许读写

0, // 此项必须为0

NULL, // no security attrs

OPEN_EXISTING, //设置产生方式

FILE_FLAG_OVERLAPPED, // 我们准备使用异步通信

NULL );

请大家注意,我们使用了FILE_FLAG_OVERLAPPED结构。这正是使用API实现非阻塞通信的关键所在。

ASSERT(hCom!=INVALID_HANDLE_VALUE); //检测打开串口操作是否成功

SetCommMask(hCom, EV_RXCHAR|EV_TXEMPTY );//设置事件驱动的类型

SetupComm( hCom, 1024,512) ; //设置输入、输出缓冲区的大小

PurgeComm( hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR

| PURGE_RXCLEAR ); //清干净输入、输出缓冲区

COMMTIMEOUTS CommTimeOuts ; //定义超时结构,并填写该结构

…………

SetCommTimeouts( hCom, &CommTimeOuts ) ;//设置读写操作所允许的超时

DCB dcb ; // 定义数据控制块结构

GetCommState(hCom, &dcb ) ; //读串口原来的参数设置

dcb.BaudRate =9600; dcb.ByteSize =8; dcb.Parity = NOPARITY;

dcb.StopBits = ONESTOPBIT ;dcb.fBinary = TRUE ;dcb.fParity = FALSE;

SetCommState(hCom, &dcb ) ; //串口参数配置

上述的COMMTIMEOUTS结构和DCB都很重要,实际工作中需要仔细选择参数。

⑶启动一个辅助线程,用于串口事件的处理。

Windows提供了两种线程,辅助线程和用户界面线程。区别在于:辅助线程没有窗口,所以它没有自己的消息循环。但是辅助线程很容易编程,通常也很有用。

在次,我们使用辅助线程。主要用它来监视串口状态,看有无数据到达、通信有无错误;而主线程则可专心进行数据处理、提供友好的用户界面等重要的工作。

hCommWatchThread=

CreateThread( (LPSECURITY_ATTRIBUTES) NULL, //安全属性

0,//初始化线程栈的大小,缺省为与主线程大小相同

(LPTHREAD_START_ROUTINE)CommWatchProc, //线程的全局函数

GetSafeHwnd(), //此处传入了主框架的句柄

0, &dwThreadID );

ASSERT(hCommWatchThread!=NULL);

⑷为辅助线程写一个全局函数,主要完成数据接收的工作。请注意OVERLAPPED结构的使用,以及怎样实现了非阻塞通信。

UINT CommWatchProc(HWND hSendWnd){

DWORD dwEvtMask=0 ;

SetCommMask( hCom, EV_RXCHAR|EV_TXEMPTY );//有哪些串口事件需要监视?

WaitCommEvent( hCom, &dwEvtMask, os );// 等待串口通信事件的发生

检测返回的dwEvtMask,知道发生了什么串口事件:

if ((dwEvtMask & EV_RXCHAR) == EV_RXCHAR){ // 缓冲区中有数据到达

COMSTAT ComStat ; DWORD dwLength;

ClearCommError(hCom, &dwErrorFlags, &ComStat ) ;

dwLength = ComStat.cbInQue ; //输入缓冲区有多少数据?

if (dwLength > 0) {

BOOL fReadStat ;

fReadStat = ReadFile( hCom, lpBuffer,dwLength, &dwBytesRead;

&READ_OS( npTTYInfo ) ); //读数据

注:我们在CreareFile()时使用了FILE_FLAG_OVERLAPPED,现在ReadFile()也必须使用

LPOVERLAPPED结构.否则,函数会不正确地报告读操作已完成了.

使用LPOVERLAPPED结构, ReadFile()立即返回,不必等待读操作完成,实现非阻塞通信.此时, ReadFile()返回FALSE, GetLastError()返回ERROR_IO_PENDING.

if (!fReadStat){

if (GetLastError() == ERROR_IO_PENDING){

while(!GetOverlappedResult(hCom,

&READ_OS( npTTYInfo ), & dwBytesRead, TRUE )){

dwError = GetLastError();

if(dwError == ERROR_IO_INCOMPLETE) continue;

//缓冲区数据没有读完,继续

…… ……

::PostMessage((HWND)hSendWnd,WM_NOTIFYPROCESS,0,0);//通知主线程,串口收到数据 }

所谓的非阻塞通信,也即异步通信。是指在进行需要花费大量时间的数据读写操作(不仅仅是指串行通信操作)时,一旦调用ReadFile()、WriteFile(), 就能立即返回,而让实际的读写操作在后台运行;相反,如使用阻塞通信,则必须在读或写操作全部完成后才能返回。由于操作可能需要任意长的时间才能完成,于是问题就出现了。

非常阻塞操作还允许读、写操作能同时进行(即重叠操作?),在实际工作中非常有用。

要使用非阻塞通信,首先在CreateFile()时必须使用FILE_FLAG_OVERLAPPED;然后在 ReadFile()时lpOverlapped参数一定不能为NULL,接着检查函数调用的返回值,调用GetLastError(),看是否返回ERROR_IO_PENDING。如是,最后调用GetOverlappedResult()返回重叠操作(overlapped operation)的结果;WriteFile()的使用类似。

⑸.在主线程中发送下行命令。

BOOL fWriteStat ; char szBuffer[count];

…………//准备好发送的数据,放在szBuffer[]中

fWriteStat = WriteFile(hCom, szBuffer, dwBytesToWrite,

&dwBytesWritten, &WRITE_OS( npTTYInfo ) ); //写数据

注:我们在CreareFile()时使用了FILE_FLAG_OVERLAPPED,现在WriteFile()

也必须使用 LPOVERLAPPED结构.否则,函数会不正确地报告写操作已完成了.

使用LPOVERLAPPED结构,WriteFile()立即返回,不必等待写操作完成,实现非阻塞通信.此时, WriteFile()返回FALSE, GetLastError()返回ERROR_IO_PENDING.

int err=GetLastError();

if (!fWriteStat) {

if(GetLastError() == ERROR_IO_PENDING){

while(!GetOverlappedResult(hCom, &WRITE_OS( npTTYInfo ),

&dwBytesWritten, TRUE )) {

dwError = GetLastError();

if(dwError == ERROR_IO_INCOMPLETE){

// normal result if not finished

dwBytesSent += dwBytesWritten; continue; }

......................

综上,我们使用了多线程技术,在辅助线程中监视串口,有数据到达时依靠事件驱动,读入数据并向主线程报告(发送数据在主线程中,相对说来,下行命令的数据总是少得多);并且,WaWaitCommEvent()、ReadFile()、WriteFile()都使用了非阻塞通信技术,依靠重叠(overlapped)读写操作,让串口读写操作在后台运行。

VC基于MSCOMM控件串口通讯

在mfc中进行串口通讯最简单的方法莫过于在对话框中使用MSCOMM控件了,MSComm通信控件提供了一系列标准通信命令的接口,它允许建立串口连接,可以连接到其他通信设备(如Modem).

还可以发送命令、进行数据交换以及监视和响应在通信过程中可能发生的各种错误和事件,从而可以用它创建全双工 、事件驱动的、高效实用的通信程序。 一、用MSComm控件通信 1.串口通信基础知识

一般悦来,计算机都有一个或多个串行端口,它们依次为com1、Com2、…,这些串口还提供了外部设备与pC进行数据传输和

皿信的通道。这些串口在CPU和外设之间充当解释器的角色。当字符数据从CPU发送给外设时,这些字符数据将被转换成串行比特

流数据;当接收数据时,比特流数据被转换为字符数据传递给CPU,再进一步说,在操作系统方面,Windows用通信驱动程序

(COMM.DRV)调用API函数发送和接收数据,当用通信控件或声明调用API函数时,它门由COMM. DRV解释并传递给设备驱动程序, 作为一个vB程序员,要编写通信程序.只需知道通信控件提供给Windows通信AP1函数的接口即可.换句话说,只需设定和监视通 信控件的属性和事件即可。 2.使用Mscomm控件

在开始使用MSComm控件之前。需要先了解其属性、事件或错误 属性 描述

CommPort 设置或返回通信端口号

Settings 以字符串的形式设置或返回波特率、奇偶校验、数据位和停止位 PortOpen 设置或返回通信端口的状态。也可以打开和关闭端口 Input 返回和删除接收缓冲区中的字符 Output 将字符串写入发送缓冲区

CommEvent属性为通信事件或错误返回下列值之一。在该控件的对象库中也可以找到这些常量。 常量 值 描述

ComEventBreak 1001 收到了断开信号

ComEventCTSTO 1002 Clear To Send Timeout。在发送字符时,在系统指定的事1件内,CTS(Clear To Send)线是低电平

ComEventDSRTO 1003 Data Set Ready Timeout。在发送字符时,在系统指定的事件内,DSR(Data Set Ready)线是低电平

ComEventFrame 1004 数据帧错误。硬件检测到一个数据帧错误

ComEventOverrun 1006 端口溢出。硬件中的字符尚未读,下一个字符又到达,并且丢失 ComEventCDTO 1007 Carrier Detect Time。在发送字符时,在系统指定的事件内,CD(Carrier Detec线是低电平。CD

也称为RLSD(Receive Line Singal Detect,接收线信号检测) ComEventRxOver 1008 接收缓冲区溢出。在接收缓冲区中没有空间 ComEventRxParity 1009 奇偶校验错。硬件检测到奇偶校验错误7

ComEventTxFull 1010 发送缓冲区满。在对发送字符排队时,发送缓冲区满

ComEventDCB 1011 检取端口DCB(Device Control Blick)时发生了没有预料到的错误

通信事件包含了下面的设置: 常量 值 描述

ComEvSend 1 发送缓冲区中的字符数比Sthreshold值低

ComEvReceive 2 接收到了Rthreshold个字符。持续产生该事件,直到使用了Input属性删除了接收缓冲区中的数据

ComEvCTS 3 CTS(Clear To Send)线改变

ComEvDSR 4 DSR(Data Set Ready)线改变。当DSR从1到0改变时,该事件发生 ComEvCD 5 CD(Carrier Detect)线改变ComEvRing6检测到响铃信号。一些URAT(Universal AsynchronousReciver-

-Transmitters,通用异步收发器)不支持该事件 ComEvEOF 7 收到了EOF字符(ASCII字符26)

Error消息(MSComm控件)下表列出了MSComm控件可捕获的错误消息: 常量 值 描述

ComInvalidPropertyValue 380 无效的属性值 ComSetNotSupported 383 属性只读 ComGetNotSupported 394 属性只读

ComPortOpen 8000 端口打开时该存在无效 8001 超时设置必须比0值大 ComPortInvalid 8002 无效的端口号 8003 属性只在运行时有效 8004 属性在运行时是只读的 ComPortAleadyOpen 8005 端口已经打开 8006 设备标识符无效或不支持 8007 不支持设备的波特率 8008 指定的字节大小无效 8009 缺省参数错误

8010 硬件不可用(被其他设备锁住) 8011 函数不能分配队列 ComNoOpen 8012 设备没有打开 8013 设备已经打开

8014 不能使用通信通知

ComSetCommStateFailed 8015 不能设置通信状态 8016 不能设置通信事件屏蔽

ComPortNotOpen 8018 该存在只在端口打开是有效 8019 设备忙

ComReadError 8020 通信设备读错误

ComDCBError 8021 检取端口设备控制块时出现内部错误

注意在使用的时候一定要保证两个通讯串口的设置是相同的,否则受到的信息将会产生错误! 由于取值位数的不同,有可能发送的信息要读很多次才能组合成需要的信息!

1。建立mfc工程。 将控件加进来:打开

“Project->Add To Project->Components and Controls->Registered Activex Controls”,然后选择控件:Microsoft Communication Control,version 6.0插入到当前的工程中。这样就将类 CMSComm 的相关文件 mscomm.cpp 和 mscomm.h 一并加入到了工程中。编程时只需将控件对话中的 MSComm 控件拖至你的应用对话框中就OK了 2。定义串口对象:

CMSComm m_MSComm;

3。串口初始化:

DWORD style=WS_VISIBLE;

m_MSComm.Create(NULL,style,CRect(0,0,0,0),this,IDC_MSCOMM); if(m_MSComm.GetPortOpen()) //如果串口是打开的,则行关闭串口 {

m_MSComm.SetPortOpen(FALSE); }

m_MSComm.SetCommPort(1); //选择COM1

m_MSComm.SetInBufferSize(1024); //接收缓冲区 m_MSComm.SetOutBufferSize(1024);//发送缓冲区

m_MSComm.SetInputLen(0);//设置当前接收区数据长度为0,表示全部读取 m_MSComm.SetInputMode(1);//以二进制方式读写数据

m_MSComm.SetRThreshold(1);//接收缓冲区有1个及1个以上字符时,将引发接收数据的OnComm事件

m_MSComm.SetSettings(\"9600,n,8,1\");//波特率9600无检验位,8个数据位,1个停止位 if(!m_MSComm.GetPortOpen())//如果串口没有打开则打开 m_MSComm.SetPortOpen(TRUE);//打开串口 else {

m_MSComm.SetOutBufferCount(0);

AfxMessageBox(\"Open The Serial Port 1 Failurre!\"); }

4。串口数据读写:

MSComm 类的读写函数比较简单:GetInput()和SetOutput()。函数原形分别为VARIANT GetInput和void SetOutput(const VARIANT newValue),均使用VARIANT类型。但PC机发送和接收数据时习惯用字符串形式。MSDN中查阅VARIANT类型,可以用BSTR表示字符串,但所有的BSTR都包含宽字符,而只有Windows NT支持宽字符,Windows 9X并不支持。所以要完成一个适应各平台的串口应用程序必须解决这个问题。这里使用CbyteArray即可解决之。

发数据:在对话框对加入 按钮 控件并给你添加消息 void CTest_mscommDlg::OnSend() {

// TODO: Add your control notification handler code here int i,Count;

CString m_SendData; m_SendData=\"Hello!\";

Count=m_SendData.GetLength(); CByteArray m_Array;

m_Array.RemoveAll(); m_Array.SetSize(Count);

for (i=0; i收数据:给串口控件添加消息

void CTest_mscommDlg::OnOnCommMscomm() {

VARIANT m_input; char *str,*str1; int k,nEvent,i;

CString str2,m_RcvData;

nEvent=m_MSComm.GetCommEvent(); switch(nEvent) {

case 2:

k=m_MSComm.GetInBufferCount(); //接收缓冲区的字符数目 if(k>0) {

m_input=m_MSComm.GetInput();

str=(char*)(unsigned char*)m_input.parray->pvData; } i=0; str1=str; while(i { i++; str1++; }

*str1='{post.abstract}';

str2=(const char*)str; //清除字符串中的不必要字符 m_RcvData=(const char *)str; }

//数据显示处理 m_disp+=m_RcvData;

m_Array.SetAt(i, m_SendData[i]);

m_MSComm.SetOutput(COleVariant(m_Array)); // 发送数据

UpdateData(false); }

=====================================

摘要:本文介绍了在Microsoft Visual C++ 6.0环境下通过对Active X控件的编程来实现串口的通信的一般方法。

一、 引言

当我们在Windows操作系统下开发串行通信程序时通常不得不面对许多复杂的API函数,因为在Windows操作系统下不能直接对设备端口进行操作,也不能在系统级(Ring 3级别)使用任何DOS或BIOS中断,如要对端口进行编程则只能以文件的形式来对端口进行操作,这就使开发人员不得不面对非常烦琐的API函数编程。本文对此提出了另外一种封装性很好的使用Microsoft Visual C++ 6.0自带的\"Microsoft Communications Control,version 6.0\"Active X控件的编程方法,通过对该控件的正确使用我们可以比较轻松地编写出所需的串行通信程序。

下面,我们将结合一个实际的程序示例来对此方法进行说明。本程序的编程环境是Windows 98和Microsoft Visual C++ 6.0。在本程序示例中对为避免阻塞而对线程的使用以及在使用中遇到的一些问题也做了详细的介绍。

二、 程序的设计实现

在开始进行代码编程前,首先以在工程中插入组件或控件的方式将Active X控件\"Microsoft

Communications Control,version 6.0\"加入到工程中来,此时将会在工程中添加一个关于此控件的新类使用该控件的一些方法和属性时不能象使用类一样简单的声明一个实例对象,而要通ClassWizard为该控件和一个成员变量建立起绑定关系,在此我们将该控件同变量m_Comm相绑定后就可以通过该控件提供的方法来对串口的各种通讯参数进行设置了。为了编程方便起见,也可以在资源视图中直接对该控件的属性进行设置,如无特别要求,对下表所列属性进行设置就基本可以满足编程要求了。现将常用的属性列表如下:

属性 设定值 属性说明

CommPort 1 串口号,一般从1到4

InBufferSize 30720 接收缓冲区大小,为保持程序的稳定,建议设得值足够大 InputMode 0-Text 接收数据的类型,0表示文本类型,1表示二进制类型 InputLen 0 从接收缓冲区读取的字节数,0表示全部读取 OutBufferSize 512 发送缓冲区大小

Settings 4800,n,8,1 串口的参数设置,依次为波特率、奇偶校验(n-无校验,e-偶校验,o-奇校验)、数据位数停止位数

RThreshold 1 设定当接收几个字符时触发OnComm事件,0表示不产生事件, 1表示每接收一个字符就产生一个事件

SThreshold 0 设定在触发OnComm事件前,发送缓冲区内所允许的最少的字符数, 0表示发送数据时不产生事件,1表示当发送缓冲区空时产生OnComm事件

我们要求能在程序启动的同时就打开串口以便即时对从串口到达的数据进行接收、处理。一般来说可以将下面的打开端口的代码写在OnCreate()、OnInitialUpdate()、InitInstance ()等程序入口函数中:

……

if(!m_Comm.GetPortOpen()) //检测是否已经打开过端口 m_Comm.SetPortOpen(TRUE); //如没有打开则将端口打开 ……

接下来的工作就是对数据的发送与接收了,这也是本文所要介绍的重点所在。发送数据的代码原则上是可以写到一个成员函数中被直接调用的,但这并不是一个良好的编程习惯:我们应当把比较耗时的操作,如文件拷贝、打印、端口传输等工作放到一个单独的线程当中,以避免其在工作时会引起整个进程的阻塞以提高整个系统对CPU的利用率。例如我们可以在视类中菜单或按钮的响应函数中用

AfxBeginThread(WriteProc,this)函数来开启一个名为\"WriteProc\"的线程,由于在线程中还需要使用视类的函数和变量,为了不产生新的视类的实例对象,我们通过该函数的第二个参数将指向当前的视类的指针this作为参数传递给线程。在线程中可以用如下两种方法之中的一种调用视类的成员函数:

((COLECommView*) pParam)->DoSendProc();

或是:

COLECommView* view=(COLECommView*) pParam; View->DoSendProc();

其中从pParam传来的变量就是指向视类的指针。在线程中通过调用视类中的DoSendProc函数来完成对数据的发送,正是由于该函数是被全局的线程所调用的,我们就不可以使用取编辑框上的数据时通常所用的UpdateData()函数了,取而带之的是API 函数GetDlgItemText(),取到输入的数据后通过控件的SetOutput() 方法就把数据从串口发出去了,其中发送数据必须经ColeVariant类将其转换为通用的VARIANT型变量。实现 主要代码如下:

……

char a[255];

HWND hwnd=GetSafeHwnd();

::GetDlgItemText(hwnd,IDC_ED99v1,a,255); int i=0; CString str;

while(a[i]!='{post.abstract}') {

str.Format(\"%c\ m_SendData+=str; i++; }

str.Format(\"%c\ m_SendData+=str;

m_Comm.SetOutput(COleVariant(m_SendData)); ……

至于数据的接收,我们可以通过让MS Comm控件响应其OnComm事件来完成,通过ClassWizar加入其对事件的响应后,通过下面的事件映射,当有字符到达时便会通知 OnComm()函数去处理,从而实现数据的异步接收:

……

BEGIN_EVENTSINK_MAP(COLECommView, CFormView) //{{AFX_EVENTSINK_MAP(COLECommView)

ON_EVENT(COLECommView, IDC_MSCOMM1, 1 /* OnComm */, OnComm, VTS_NONE) //}}AFX_EVENTSINK_MAP END_EVENTSINK_MAP() ……

void COLECommView::OnComm() {

VARIANT Input;

if(m_Comm.GetCommEvent()==2)//接收缓冲区内有字符 {

Input=m_Comm.GetInput();//读取缓冲区内的数据 CString msg=Input.bstrVal; CString str;

str.Format(\"%c\ if(msg.Right(1)==str) {

m_RecvData+=msg;

m_History.AddString(m_RecvData); m_RecvData=\"\"; } else

m_RecvData+=msg; } }

当数据被接收到接收缓冲区后,对于字符可以从VARIANT型结构变量的bstrVal成员变量中获取VARIANT数据结构相当复杂,并牵扯到COM(Component Object Model,组件对象模型)中的一些概念具体详情请参阅Microsoft Corpration发布的Msdn

阅读全文(93次) / 评论 / 丢小纸条 / 文件夹: VC学习 / 添加到: del.icio.us 百度搜藏 雅虎收藏+ / 订阅到: Google Live!

工业控制--vc++串口通讯方法(WINAPI实现) 痞子 @ 2007-03-19 11:03

总所周之,利用串口进行数据通讯在在通讯通讯领域重占有着重要的地位。利用RS232-RS485进行数据信号的采集和传递是VC编程的又一大热点。串口通讯在通讯软件重有着十分广泛的应用。如电话、传真视频和各种控制等。在各种开发工具中间,VC由于功能强大和灵活,同时也得到了Microsoft的最大支持,所以在一般进行涉及硬件操作的通讯编程重,大都推荐使用VC作为开发工具。然而工业控制串口通讯这个又不同于一般的串口通讯程序,因为控制外围设备传送的大都是十六进制数据(BYTE类型),所以,为了提高程序的运行稳定性,我们在编写程序进行通讯时可以不考虑传送BYTE类型数据的工作。 串口通讯目前流行的方法大概有两种:一是利用Microsoft提供的CMSCOMM控件进行通讯,不过现在很多程序员都觉应该放弃这种方式。二是利用WINAPI函数进行编程,这种编程的难度最高,要求你要掌握很多的API函数。三是利用现在网络上面提供的一些串口通讯控件进行编写,比如CSerial类等

程序实现:

我在经过许多的项目的开发和实践中发现,采用WIN API函数进行串口的开发能够给程序员很大的控件,并且程序运也很稳定。所以我将与串口接触的函数进行封装,然后在各个工程中进行调用,效果还是比较好的,现将各个函数和调用方法列举出来,希望对各位有所帮助。 一、设置串口相关工作

#define MAXBLOCK 2048 #define XON 0x11 #define XOFF 0x13

BOOL SetCom(HANDLE &m_hCom, const char *m_sPort, int BaudRate, int Databit, CString parity, CString stopbit) {

COMMTIMEOUTS TimeOuts; ///串口输出时间 超时设置 DCB dcb; ///与端口匹配的设备

m_hCom=CreateFile(m_sPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); // 以重叠方式打开串口

if(m_hCom==INVALID_HANDLE_VALUE) {

AfxMessageBox(\"设置串口部分,串口打开失败\"); /////重叠方式 异步通信(INVALID_HANDLE_VALUE)函数失败。 return FALSE; }

SetupComm(m_hCom,MAXBLOCK,MAXBLOCK); //设置缓冲区 memset(&TimeOuts,0,sizeof(TimeOuts));

TimeOuts.ReadIntervalTimeout=MAXDWORD; // 把间隔超时设为最大,把总超时设为0将导致ReadFile立即返回并完成操作

TimeOuts.ReadTotalTimeoutMultiplier=0; //读时间系数 TimeOuts.ReadTotalTimeoutConstant=0; //读时间常量

TimeOuts.WriteTotalTimeoutMultiplier=50; //总超时=时间系数*要求读/写的字符数+时间常量 TimeOuts.WriteTotalTimeoutConstant=2000; //设置写超时以指定WriteComm成员函数中的

SetCommTimeouts(m_hCom, &TimeOuts); //GetOverlappedResult函数的等待时间*/

if(!GetCommState(m_hCom, &dcb)) ////串口打开方式、端口、波特率 与端口匹配的设备 {

AfxMessageBox(\"GetCommState Failed\"); return FALSE; }

dcb.fParity=TRUE; //允许奇偶校验 dcb.fBinary=TRUE; if(parity==\"NONE\")

dcb.Parity=NOPARITY; if(parity==\"ODD\")

dcb.Parity=ODDPARITY; if(parity==\"EVEN\")

dcb.Parity=EVENPARITY; if(stopbit==\"1\")//设置波特率 dcb.StopBits=ONESTOPBIT; //if(stopbit==\"0\")//设置波特率 // dcb.StopBits=NONESTOPBIT; if(stopbit==\"2\")//设置波特率

dcb.StopBits=TWOSTOPBITS; BOOL m_bEcho=FALSE; /// int m_nFlowCtrl=0;

BOOL m_bNewLine=FALSE; /// dcb.BaudRate=BaudRate; // 波特率 dcb.ByteSize=Databit; // 每字节位数 // 硬件流控制设置

dcb.fOutxCtsFlow=m_nFlowCtrl==1;

dcb.fRtsControl=m_nFlowCtrl==1 ?RTS_CONTROL_HANDSHAKE:RTS_CONTROL_ENABLE; // XON/XOFF流控制设置(软件流控制!) dcb.fInX=dcb.fOutX=m_nFlowCtrl==2; dcb.XonChar=XON; dcb.XoffChar=XOFF; dcb.XonLim=50; dcb.XoffLim=50;

if(SetCommState(m_hCom, &dcb))

return TRUE; ////com的通讯口设置 else {

AfxMessageBox(\"串口已打开,设置失败\"); return FALSE; } }

二、读串口操作:

int ReadCom(HANDLE hComm, BYTE inbuff[], DWORD &nBytesRead, int ReadTime) {

DWORD lrc; ///纵向冗余校验 DWORD endtime; /////////jiesuo

static OVERLAPPED ol; int ReadNumber=0;

int numCount=0 ; //控制读取的数目 DWORD dwErrorMask,nToRead; COMSTAT comstat;

ol.Offset=0; ///相对文件开始的字节偏移量

ol.OffsetHigh=0; ///开始传送数据的字节偏移量的高位字,管道和通信时调用进程可忽略 ol.hEvent=NULL; ///标识事件,数据传送完成时设为信号状态 ol.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);

endtime=GetTickCount()+ReadTime;//GetTickCount()取回系统开始至此所用的时间(毫秒) for(int i=0;i<2000;i++) inbuff[i]=0; Sleep(ReadTime);

ClearCommError(hComm,&dwErrorMask,&comstat); nToRead=min(2000,comstat.cbInQue); if(int(nToRead)<2) goto Loop;

if(!ReadFile(hComm,inbuff,nToRead,&nBytesRead,&ol)) {

if((lrc=GetLastError())==ERROR_IO_PENDING) {

///////////////////

endtime=GetTickCount()+ReadTime;//GetTickCount()取回系统开始至此所用的时间(毫秒) while(!GetOverlappedResult(hComm,&ol,&nBytesRead,FALSE))//该函数取回重叠操作的结果 {

if(GetTickCount()>endtime) break; } } }

return 1; Loop: return 0; }

三、写串口命令

int WriteCom(HANDLE hComm, BYTE Outbuff[], int size, int bWrite[]) {

DWORD nBytesWrite,endtime,lrc; static OVERLAPPED ol;

DWORD dwErrorMask,dwError; COMSTAT comstat;

ol.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); ol.Offset=0; ol.OffsetHigh=0;

ol.hEvent=NULL; ///标识事件,数据传送完成时,将它设为信号状态 ClearCommError(hComm,&dwErrorMask,&comstat);

if(!WriteFile(hComm,Outbuff,size,&nBytesWrite,&ol)) {

if((lrc=GetLastError())==ERROR_IO_PENDING) {

endtime=GetTickCount()+1000;

while(!GetOverlappedResult(hComm,&ol,&nBytesWrite,FALSE)) {

dwError=GetLastError(); if(GetTickCount()>endtime) {

AfxMessageBox(\"写串口时间过长,目前串口发送缓冲区中的数据数目为空\"); break; }

if(dwError=ERROR_IO_INCOMPLETE)

continue; //未完全读完时的正常返回结果 else {

// 发生错误,尝试恢复!

ClearCommError(hComm,&dwError,&comstat); break; } } } }

FlushFileBuffers(hComm);

PurgeComm(hComm,PURGE_TXCLEAR); bWrite=0; return 1; }

四、调用方法很简单,只需要将你的串口参数进行简单的设置就可以了。比如: BOOL Main_OpenCom()//设置COM {

int Boundrate=9600;//波特率 CString StopBits=\"1\";//停止位 int DataBits=8;//数据位

CString Parity=\"ODD\";//奇偶校验 CString m_Port=\"COM1\";

return SetCom(m_hCom1,m_Port,Boundrate,DataBits,Parity,StopBits); }

void Main() {

int SIZE;

DWORD BytestoRead=52*Count+6;//要11个字节 int BWRITE[2];

int ReadTime=2000;

BYTE Outbuff[12]={0xff,0x00,0xea,0xff,0xea,0xff,0,0,0,0,0,0}; SIZE=sizeof(Outbuff);

WriteCom(m_hCom,Outbuff,SIZE,BWRITE);

ReadCom(m_hCom,m_Inbuff,BytestoRead,ReadTime); //进行湘阴的解包处理 }

有了上面的函数的封装,相信大家编程应该能够方便快捷一些吧。

阅读全文(30次) / 评论 / 丢小纸条 / 文件夹: VC学习 / 添加到: del.icio.us 百度搜藏 雅虎收藏+ / 订阅到: Google Live!

用VC++6.0实现PC机与单片机之间的数据交换 作 者:方梓 出 处:摘自《C51BBS离线版光盘》

工业控制领域(如DCS系统),经常涉及到串行通信问题。为了实现微机和单片机之间的数据交换,人们用各种不同方法实现串行通信,如DOS下采用汇编语言或C语言,但在Windows 环境下却存在一些困难和不足。在Windows操作系统已经占据统治地位的情况下(何况有些系统根本不支持DOS如Windows2000)开发Windows 环境下串行通信技术就显得日益重要。VC++6.0是微软公司于1998年推出的一种开发环境,以其强大的功能,友好的界面,32位面向对象的程序设计及Active X的灵活性而受广大软件开发者的青睐,被广泛应用于各个领域。应用VC++开发串行通信目前通常有如下几种方法:一是利用Windows API通信函数;二是利用VC的标准通信函数inp、inpw、inpd、outp、outpw、outpd等直接对串口进行操作;三是使用Microsoft Visual C++的通信控件(MSComm);四是利用第三方编写的通信类。以上几种方法中第一种使用面较广,但由于比较复杂,专业化程度较高,使用较困难;第二种需要了解硬件电路结构原理;第三种方法看来较简单,只需要对串口进行简单配置,但是由于使用令人费解的VARIANT 类,使用也不是很容易;第四种方法是利用一种用于串行通信的

CSerial类(这种类是由第三方提供),只要理解这种类的几个成员函数,就能方便的使用。笔者利用CSerial类很方便地实现了在固定式EBM气溶胶灭火系统分区启动器(单片机系统)与上位机的通信。以下将结合实例,给出实现串行通信的几种方法。

1 Windows API通信函数方法

 与通信有关的Windows API函数共有26个,但主要有关的有:  CreateFile() 用 “comn”(n为串口号)作为文件名就可以打开串口。

ReadFile() 读串口。

    

 

 

WriteFile() 写串口。

CloseHandle() 关闭串口句柄。

初始化时应注意CreateFile()函数中串口共享方式应设为0,串口为不可共享设备,其它与一般文件读写类似。以下给出API实 现的源代码。

1.1 发送的例程 //声明全局变量

HANDLE m_hIDComDev;

OVERLAPPED m_OverlappedRead, m_Over lappedWrite;

//初始化串口

void CSerialAPIView::OnInitialUpdate()

{

CView::OnInitialUpdate(); Char szComParams[50]; DCB dcb;

Memset(&m_OverlappedRead, 0, sizeof (OVERLAPPED)); Memset(&m_OverlappedWrite, 0, sizeof (OVERLAPPED));

m_hIDComDev = NULL;

m_hIDComDev = CreateFile(“COM2”, GENERIC_READ│GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL│FILE_FLAG_OVERLAPPED, NULL);

if (m_hIDComDev == NULL)

{

AfxMessageBox(“Can not open serial port!”); goto endd; }

memset(&m_OverlappedRead, 0, sizeof (OVERLAPPED));

memset(&m_OverlappedWrite, 0, sizeof (OVERLAPPED)); COMMTIMEOUTS CommTimeOuts;

CommTimeOuts.ReadIntervalTimeout=0×FFFFFFFF; CommTimeOuts.ReadTotalTimeoutMultiplier = 0; CommTimeOuts.ReadTotalTimeoutConstant = 0; CommTimeOuts.WriteTotalTimeoutMultiplier = 0; CommTimeOuts.WriteTotalTimeoutConstant = 5000; SetCommTimeouts(m_hIDComDev, &CommTimeOuts); Wsprintf(szComparams, “COM2:9600, n, 8, 1”);

m_OverlappedRead. hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

m_OverlappedWrite. hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

dcb. DCBlength = sizeof(DCB);

GetCommState(m_hIDComDev, &dcb); dcb. BaudRate = 9600;

dcb. ByteSize= 8;

unsigned char ucSet;

ucSet = (unsigned char) ((FC_RTSCTS&FC_DTRDSR) != 0);

ucSet = (unsigned char) ((FC_RTSCTS&FC_RTSCTS) ! = 0); ucSet = (unsigned char) ((FC_RTSCTS&FC_XONXOFF) ! = 0);

 

 

    

if (!SetCommState(m_hIDComDev, &dcb)‖

!SetupComm(m_hIDComDev,10000,10000)‖ m_OverlappedRead. hEvent ==NULL‖ m_OverlappedWrite. hEvent ==NULL) {

DWORD dwError = GetLastError();

if (m_OverlappedRead. hEvent != NULL) CloseHandle(m_OverlappedRead. hEvent); if (m_OverlappedWrite. hEvent != NULL) CloseHandle(m_OverlappedWrite. hEvent);

CloseHandle(m_hIDComDev);

}

endd: ; }

//发送数据

void CSerialAPIView::OnSend()

{

char szMessage[20] = “thank you very much”;

DWORD dwBytesWritten;

for (int i=0; i{

WriteFile(m_hIDComDev, (LPSTR)&szMessage[i], 1, &dwBytesWritten, &m_OverlappedWrite);

if (WaitForSingleObject(m_OverlapperWrite, hEvent,

1000))dwBytesWritten = 0; else {

GentOverlappedResult(m_hIDComDev, &m_OverlappedWrite, &dwBytesWritten, FALSE);

m_OverlappedWrite. Offset += dwBytesWritten; }

dwBytesWritten++;

} }

1.2 接收例程

DCB ComDcb; //设备控制块

HANDLE hCom; //global handle

hCom = CreateFile (\"COM1\

GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

if (hCom==INVALID_HANDLE_VALUE)

{

AfxMessageBox(\"无法打开串行口\"); } else {

 

            

 

 

COMMTIMEOUTS CommTimeOuts ;

SetCommMask(hCom, EV_RXCHAR ) ;

SetupComm(hCom, 4096, 4096 ) ; /*设置收发缓冲区 尺寸为4K */ PurgeComm(hCom, PURGE_TXABORT| PURGE_RXABORT | PURGE_TXCLEAR| PURGE_RXCLEAR ) ; //清收发缓冲区

//以下初始化结构变量CommTimeOuts, 设置超时参数

CommTimeOuts.ReadIntervalTimeout = 0×FFFFFFFF ;

CommTimeOuts.ReadTotalTimeoutMultiplier = 0;

CommTimeOuts.ReadTotalTimeoutConstant = 4000; CommTimeOuts.WriteTotalTimeoutMultiplier = 0; CommTimeOuts.WriteTotalTimeoutConstant = 4000;

SetCommTimeouts(hCom, &CommTimeOuts ); //设置超时参数 ComDcb.DCBlength = sizeof( DCB );

GetCommState( hCom, &ComDcb ); //获取当前参数 ComDcb.BaudRate =9600; //波特率 ComDcb.ByteSize = 8; //数据位

ComDcb.Parity = 0; /*校验 0~4=no, odd, even, mark, space */ SetCommState(hCom, &ComDcb ) ;

} //设置新的通信参数 接收可用定时器或线程等 DWORD dRead,dReadNum;

unsigned char buff [200];

dRead=ReadFile(hCom, buff, 100, &dReadNum, NULL); //接收100个字

符,

//dReadNum为实际接收字节数 2 利用端口函数直接操作

这种方式主要是采用两个端口函数_inp(), _outp()实现对串口的读写,

其中读端口函数的原型为:

int _inp(unsigned shot port)

该函数从端口读取一个字节,端口号为0~65535。 写端口的函数原型为:

int _outp(unsigned shot port, int databyte) 该函数向指定端口写入一个字节。

不同的计算机串口地址可能不一样,通过向串口的控制及收发寄存器

进行读写,可以实现灵活的串口通信功能,由于涉及具体的硬 件电路讨论比较复杂,在此不加赘述。

3 MSComm控件

MSComm控件是微软开发的专用通信控件,封装了串口的所有功能,

使用很方便,但在实际应用中要小心对其属性进行配置。下面详 细说明该类应用方法。

3.1 MSComm控件的属性

CommPort:设置串口号,类型 short :1-comm1 2-comm2.

Settings:设置串口通信参数,类型 CString :B波特率,P奇偶性(N无校验,E偶校验,O奇校验),D字节有效位数,S停止位。

PortOpen:设置或返回串口状态,类型 BOOL:TURE打开,FALSE关闭。

InputMode:设置从接收缓冲区读取数据的格式,类型 long: 0-Text 1-Bin。 Input:从接收缓冲区读取数据,类型 VARIANT。

   

      

   

InBufferCount:接收缓冲区中的字节数,类型:short。 InBufferSize:接收缓冲区的大小,类型:short。 Output:向发送缓冲区写入数据,类型:VARIANT。

OutBufferCount:发送缓冲区中的字节数,类型:short。 OutBufferSize:发送缓冲区的大小,类型:short。

InputLen:设置或返回Input读出的字节数,类型:short。 CommEvent:串口事件,类型:short。

3.2 程序示例 串口初始化

if (!m_comm.GetPortOpen())m_comm.SetPortOpen(TURE); /*打开串口*/ m_comm.SetSettings(\"4800,n,8,1\"); /*串口参数设置*/

m_comm.SetInputMode(0); /*设置TEXT缓冲区输入方式*/

m_comm.SetRthresHold(1); /*每接收一个字符则激发OnComm()事件*/

接收数据

m_comm.SetInputLen(1); /*每次读取一个字符

VARINAT V1=m_comm.GetInput();

/*读入字符*/

m_V1=V1.bstrval; 发送字符

m_comm.SetOutput(Colevariant (\"Hello\"); /*发送 “Hello” */

3.3 注意

SetOutput方法可以传输文本数据或二进制数据。用SetOutput方法

传输文本数据,必须定义一个包含一个字符串的Variant。

发送二进制数据,必须传递一个包含字节数组的Variant 到 Output 属性。正常情况下,如果发送一个 ANSI 字符串到应用程序,

可以以文本数据的形式发送。如果发送包含嵌入控制字符、Null 字符等的数据,要以二进制形式发送。此处望引起读者注意,笔 者曾经在此犯错。

4 VC++类CSerial

4.1 串行通信类CSerial简介

Cserial 是由MuMega Technologies公司提供的一个免费的VC++类,可

方便地实现串行通信。以下为该类定义的说明部分。

class CSerial

{

public: CSerial(); ~CSerial();

BOOL Open( int nPort = 2, int nBaud = 9600 ); BOOL Close( void );

int ReadData( void *, int );

int SendData( const char *, int ); int ReadDataWaiting( void );

BOOL IsOpened( void ){ return( m_bOpened ); } protected:

BOOL WriteCommByte( unsigned char ); HANDLE m_hIDComDev;

   

 

 

       

OVERLAPPED m_OverlappedRead, m_OverlappedWrite; BOOL m_bOpened;

}

4.2 串行通信类Cserial 成员函数简介

1. CSerial::Cserial是类构造函数,不带参数,负责初始化所有类成

员变量。

2. CSerial:: Open这个成员函数打开通信端口。带两个参数,第一个是

埠号,有效值是1到4,第二个参数是波特率,返回一个布 尔量。

3. CSerial:: Close函数关闭通信端口。类析构函数调用这个函数,所

以可不用显式调用这个函数。

4. CSerial:: SendData函数把数据从一个缓冲区写到串行端口。它所带

的第一个参数是缓冲区指针,其中包含要被发送的资料; 这个函数返回已写到端口的实际字节数。

5. CSerial:: ReadDataWaiting函数返回等待在通信端口缓冲区中的数

据,不带参数。 6. CSerial:: ReadData函数从端口接收缓冲区读入数据。第一个参数是void*缓冲区指针,资料将被放入该缓冲区;第二个参 数是个整数值,给出缓冲区的大小。

4.3 应用VC类的一个实例

1. 固定式EBM气溶胶灭火系统简介 固定式EBM气溶胶灭火装置分区

启动器是专为EBM灭火装置设计的自动控制设备。可与两线制感温、感烟探测器配套使用,当监

测部位发生火情时,探测器发出电信号给分区启动器,经逻辑判断后发出声、光报警,延时后自动启动EBM灭火装置。为了便于火灾

事故的事后分析,需对重要的火警事件和关键性操作进行记录,记录应能从PC机读出来;PC机能控制、协调整个系统的工作,这些

都涉及通信。本例中启动器采用RS-485通信接口,系统为主从式网络,PC机为上位机。具体的通信协议为:

(1)下位机定时向上传送记录的事件;

(2)应答发送,即PC机要得到最新事件记录,而传送时间未到时,PC机发送命令,下位机接收命令后,把最新记录传给上位机;

(3)上位机发送其它命令如校时、启动、停止、手/自动等。

2. 通信程序设计 部分上位机程序

(1)发送命令字程序,代码如下 void CCommDlg::OnSend()

{

CSerial Serial;

//构造串口类,初始化串行口

if (Serial.Open(2,9600)) //if-1 //打开串行口2,波特率为9600bps

{

static char szMessage[]=\"0\";

//命令码(可定义各种命令码)

int nBytesSent;

 

 

 

 

int count=0; resend:

nBytesSent=Serial.SendData(szMessage,strlen(szMessage)); //发送命令码

char rdMessage [20];

if (Serial.ReadDataWaiting()) //if-2 {

Serial.ReadData(rdMessage,88);

//rdMessage 定义接收字节存储区,为全局变量//

if ((rdMessage[0]!=0x7f)&&(count<3))

{

count++; goto resend }

if(count>=3)

MessageBox(“发送命令字失败”); }

else //if-2

MessageBox(\"接收数据错误\");

}

else //if-1

MessageBox(\"串行口打开失败\"); }

下位机通信程序: #include

#include #include

#define count 9

#define com_code 0x00 #define com_code1 0xff

unsigned char buffer[count];

int po,year,month,date,hour; int minute,second,recordID ; int sum;

main()

{ „

/*初始化串口和定时器*/ TMOD=0×20;

TH1=0×fd; TR1=0×01; ET1=0×00; ES=1; EA=1;

/*待发送数据送缓冲区*/

    

   

buffer[0] = 0×ff; //数据特征码

buffer[1] = count+1; //数据长度 buffer[2] = year; //年 buffer[3] = month; //月 buffer[4] = date; //日 buffer[5] = hour; //时 buffer[6] = minute; //分 buffer[7] = second; //秒

buffer[8] = recordID; //事件号

for(po=0;posum+=buffer[po];

buffer[9]=sum; //校验和

}

/*发送中断服务程序*/

void send(void) interrupt 4 using 1

{

int i;

RI=0;

EA=0; do {

for(i=0;i<=count;i++) {

SBUF=buffer[i]; //发送数据和校验和// while(TI==0); TI=0; }

while(RI==0); RI=0;

} while(SBUF!=0); //主机接收不正确,重新发送//

EA=1; Return;

}

5 应用总结

根据不同需要,选择合适的方法。我们选用的用VC++类实现的上位

机和下位机的串行通信方法具有使用简单、编写程序方便的

特点。经过半年多应用于EBM灭火系统的情况来看,该方法实现的系统运行稳定可靠,是一种值得推广的简单易行的通信方法。

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- 7swz.com 版权所有 赣ICP备2024042798号-8

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务