用MFC写串口.docx

上传人:b****7 文档编号:23710933 上传时间:2023-05-20 格式:DOCX 页数:34 大小:34.25KB
下载 相关 举报
用MFC写串口.docx_第1页
第1页 / 共34页
用MFC写串口.docx_第2页
第2页 / 共34页
用MFC写串口.docx_第3页
第3页 / 共34页
用MFC写串口.docx_第4页
第4页 / 共34页
用MFC写串口.docx_第5页
第5页 / 共34页
点击查看更多>>
下载资源
资源描述

用MFC写串口.docx

《用MFC写串口.docx》由会员分享,可在线阅读,更多相关《用MFC写串口.docx(34页珍藏版)》请在冰豆网上搜索。

用MFC写串口.docx

用MFC写串口

API串口编程资料

(1)

在用ReadFile和WriteFile读写串行口时,需要考虑超时问题。

如果在指定时间内没有读出或写入指定数量的字节,那么ReadFile或WriteFIle的操作就会结束。

要查询当前的超时设置应调用GetCommTimeouts函数,该函数会填充一个

COMMTIMEOUTS结构。

调用SetCommTimeouts函数可以用某一个COMMTIMEOUTS结构的内容来设置超时。

typedefstruct_COMMTIMEOUTS(  

  DWORDReadIntervalTimeout;   //读时间间隔超时

  DWORDReadTotalTimeoutMultiplier;//读时间系数

  DWORDReadTotalTimeoutConstant;   //读时间常数

  DWORDWriteTotalTimeoutMultiplier;//写时间系数

  DWORDWriteTotalTimeoutConstant;   //写时间常量   

)COMMTIMEOUTS,*LPCOMMTIMEOUTS;

(1)CreateFile打开串口:

  HANDLEhCom;

  DWORDdwError;

  hCom=CreateFile(

             "COM1",

             GENERIC_READ|GENERIC_WRITE,//允许读和写

             0,//独占方式NULL

             OPEN_EXITSTING,  //

             FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,//重叠方式

             NULL

             )

   if(hCom==INVALID_HANDLE_VALUE)

   {

       dwError=GetLastError();//得到错误信息

       //。

//处理错误

   }

       

   

//重叠I/O操作就是异步操作或非阻塞操作,即在执行一项操作时,若系统有别的操作请求,可以立即返回执行其他任务,这样程序就不会类似死机一样停在那里。

而NomOverLapped方式则正好相反,程序应该在于同步方式下如果有一个API函数在操作中,另一个会阻塞直到上一个操作完成,所以当读数据的线程停留在WaitCommEvent的时候,写操作WriteFile就停在原地等待。

所以如果程序没有特殊要求,则在调用CreateFile函数打开串口时,一般将错做方式设为使用OverLapped(异步)方式。

(2)SetupComm缓冲区分配函数

   函数声明:

      BOOLSetupComm(

           HANDLEhFile,//通信设备串口句柄

           DWORDdwInQueue,//输入缓冲区大小

           DWORDdwOutQueue//输出缓冲区大小

           );

    返回值:

   若调用成功,返回非零值;不成功,则为0,有错误时,可调用GetLastError函数得到错误信息

    说明:

   串口打开后,就可以使用SetupComm为其设置缓冲区大小,若不设置,则系统自动将其他设置成缺省大小。

设置时应避免穿冲区溢出,即比实际大小稍大一点。

   

(3)GetCommState获得串口当前配置函数

   函数声明:

     BOOLGetCommState(

           HANDLEhFile,

           LPDCB  lpDCB

           );

     返回值:

    

     如果函数调用成功,返回非0值;调用失败,返回0,这时可调用GetLastError得到有关错误信息。

     说明:

SetCommState函数通过DCB结果来定义串口配置,当前的串口配置由GetCommState函数得到。

在设置过程中,有时只需要更改几个变量的值,因此,DCB结构中其他值需要保持原值,以保证DCB中的其他成员变量值正确,这些原值就是右GetCommState得到的。

     如果设置时出现DCB成员变量XonChar与XoffChar相同,则SetCommState设置失败。

在设置8250时,数据位只能是5-8位。

     

(5)GetCommProperties获得串口属性函数

     函数声明:

     BOOLGetCommProperties(

          HANDLEhFile,  //通信设备句柄

          LPCOMMPROPlpCommProp  //指向COMMPROP结构

          );

     返回值:

     如果函数调用成功,返回非0值;调用失败,返回0,这时可调用GetLastError得到有关错误信息。

     说明:

     由该函数得到的串口(或其他通信设备,如并口等)信息可用与SetCommState,SetCommTimeouts和SetupComm三个函数去配置串口信息。

     COMMPROP结构说明:

     COMMPROP结构只涉及到本函数,因此在这里介绍。

     COMMPROP结构声明:

       typedefstruct_COMMPROP(

             WORDwPacketLength;   //数据簿大小

             WORDwPacketVersion;  //COMMPROP结构版本号

             DWORDdwServiceMask;  //设置一位掩码

             DWORDdwReservedl;    //reserved保留

             DWORDdwMaxTxQueue;   //驱动程序发送缓冲区最大允许长度(字节)

             DWORDdwMaxRxQueue;   //驱动程序接收缓冲区最大允许长度(字节)

             DWORDdwMaxBqud;      //最大波特率

             DWORDdwProvSubType;  //设置设备类型

             DWORDdwProvCapabiliteis;//支持功能

             DWORDdwSettableParams;   //可修改的通信参数

             DWORDdwSettableBaud;     //可设置的波特率

             WORD  wSettableData;      //可设置的数据位

             WORD  wSettableStopParity;//可设置的停止位

             DWORDdwCurrentTxQueue;   //当前发送缓冲区大小(字节)

             DWORDdwCurrentRxQueue;   //当前接收缓冲区大小(字节)

             DWORDdwProvSpec1;        //未定义

             DWORDdwProvSpec2;        //未定义

             WCHARwcProvChar[1];      //未定义

             );

(6)BuildCommDCB设备控制块DCB填充函数

  函数声明:

    BOOLBuildCommDCB(

         LPCTSTRlpDef,//设备控制字符串

         LPDCBlpDCB//DCB结构

        );

  返回值:

      若调用成功,返回非0零值;不成功,则为0,有错误时调用GetLastError函数得到错误信息

  说明:

      该函数用来获取指定通信设备的超时参数值。

  

请教用WriteFile()写串口的问题   

kin 

   

 

用WriteFile()写串口,用GetLastError()的得到的错误是ERROR_IO_PENDING

请问这是什么错误,我该如何改正?

多谢!

 

 2003-8-19 13:

50:

42   

harrymeng 

   

 

好好看一下下面的文章,相信你就可以搞定了,:

本文详细介绍了串行通信的基本原理,以及在Windows NT、Win98环境下用MFC实现串口(COM)通信的方法:

使用ActiveX控件或Win API.并给出用Visual C++6.0编写的相应MFC32位应用程序。

关键词:

串行通信、VC++6.0、ActiveX控件、Win API、MFC32位应用程序、事件驱动、非阻塞通信、多线程. 

 

  在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等信号线。

(当然,在程序中也需要对相应的信号线进行设置。

) 

 

         图 最简单的RS232-C信号线接法 

   以上接法,在设计程序时,直接进行数据的接收和发送就可以了,不需要   对信号线的状态进行判断或设置。

(如果应用的场合需要使用握手信号等,需要对相应的信号线的状态进行监测或设置。

) 

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

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

 

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

   CloseComm() 关闭串口; 

   例:

int idComDev; 

idComDev = OpenComm("COM1", 1024, 128); 

CloseComm(idComDev); 

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

   例:

DCB dcb; 

BuildCommDCB("COM1:

2400,n,8,1", &dcb); 

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\n"); 

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", GENERIC_READ | GENERIC_WRITE, // 允许读写 

         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 ); 

 

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 电力水利

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1