ReadComm()c 读串口数据据,怎么使它一次性读出

当前位置:&&技术问答>
关于linux串口读的问题
&&&&来源:&互联网& 发布时间:
&&&&本文导语:&
我写了一个程序读串口数据,串口数据应该是一个14个字节的数据,我设了一个while循环不停的读串口,结果读数据时不是一次把这个数据全读出来了,而是好几次读出来的,而且读了三遍,比如第一次读了1个字节,...
我写了一个程序读串口数据,串口数据应该是一个14个字节的数据,我设了一个while循环不停的读串口,结果读数据时不是一次把这个数据全读出来了,而是好几次读出来的,而且读了三遍,比如第一次读了1个字节,第二次读了13个字节,然后读了7个字节,又读了7个字节,就这样没两次读出的数据个数正好是14个,读了三遍。
读数据不是按照我预期想的一次读了出来,而且读的数据除了第一个字节正确以外,其余全变成了00,请问这是为什么?我怎么解决?
cpu运行速度快,串口传输速度慢。不停的读,当然会分几次读出了。
其余全变成了00
这应该是你程序写的有问题。再仔细检查一遍。
您可能感兴趣的文章:
本站(WWW.169IT.COM)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。本站(WWW.169IT.COM)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。
相关文章推荐
特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!
(C)9IT.COM,E-mail:www_169it_com#163.com(请将#改为@)刚才在看串口数据的接收问题。
这里的话,最开始的时候需要在打开串口后,对串口使用SetCommEvent函数去设置通知事件。
这里又涉及一个问题,就是一般SetCommEvent函数的第二个事件参数为
第一个参数不用想了,就是文件句柄。
响应这个事件的函数是:
WaitCommEvent
在一个已经完成了的串口类中,也是调用这个函数去响应消息的,而且在后台线程函数中,这个函数同时调用了两个时间等待函数。
第一个等待事件响应的就是这个函数,第二个则是著名的waitmultipleobjects函数。
然后在其代码注释中,那位大神说这个函数在调用之后不会持续等待,而是立刻返回,原因就是这个串口是以异步的方式打开的。
waitcommevent函数的返回值是一个BOOL值,其注释原文如下:
1 // If WaitCommEvent() returns TRUE, check to be sure there are
// actually bytes in the buffer to read.
// If you are reading more than one byte at a time from the buffer
// (which this program does not do) you will have the situation occur
// where the first byte to arrive will cause the WaitForMultipleObjects()
// function to stop waiting.
The WaitForMultipleObjects() function
// resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state
// as it returns.
// If in the time between the reset of this event and the call to
// ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again
// to the signeled state. When the call to ReadFile() occurs, it will
// read all of the bytes from the buffer, and the program will
// loop back around to WaitCommEvent().
// At this point you will be in the situation where m_OverlappedStruct.hEvent is set,
// but there are no bytes available to read.
If you proceed and call
// ReadFile(), it will return immediatly due to the async port setup, but
// GetOverlappedResults() will not return until the next character arrives.
// It is not desirable for the GetOverlappedResults() function to be in
// this state.
The thread shutdown event (event 0) and the WriteFile()
// event (Event2) will not work if the thread is blocked by GetOverlappedResults().
// The solution to this is to check the buffer with a call to ClearCommError().
// This call will reset the event handle, and if there are no bytes to read
// we can loop back through WaitCommEvent() again, then proceed.
// If there are really bytes to read, do nothing and proceed.
bResult = ClearCommError(port-&m_hComm, &dwError, &comstat);
if (comstat.cbInQue == 0)
其意思我大概翻译一下:
如果BResult函数返回值为真,则必须要检测缓冲区中是否有数据。
如果你一次性的从缓冲区中读出多个数据,那么下面的这种情况就会出现:第一个将要到来数据会导致waitmultipleobjects函数停止等待。然后这个函数会在其返回的时候会重置位于OVERLAPPED结构中的事件句柄,将其置为未触发状态。
如果在重置了事件并且调用readfile函数时有更多的数据来临了,OVERLAPPEED结构中的event事件会被再次触发。此时调用readfile函数会把所有的数据全部都读取进来,然后再循环到waitcommevent函数(因为触发了的事件)。
在这个时候你会面临一个问题,就是,数据接收事件已经触发,但是缓冲区内无数据可读。此时运行并调用readfile函数,此函数会立即因为串口是异步的而返回,但是GetOverlappedResult函数并不会返回而是等待下一次字符的到来。
对于GetOverlappedResult函数来说,这个并不是我们想要的结果。如果线程被阻塞在这个函数,那么线程关闭时间和写事件就都无法响应了(当然~)
对这个问题的解决方法是调用ClearCommError函数来检查缓冲区这个函数会重置事件,如果缓冲区中没有数据可读,那么我们就重新到waitcommevent函数去等待(就是跳过waitmultipleobjects函数)
-------------------分割线----------------------------
原文就在那里,我自己翻译的可能会有点问题,但是大致的意思应该是对的。
不过现在还是在奇怪,既然waitcommevent函数不等待,那么如果只检查了一次就跳到waitformultipleobjects函数,那么在这个函数等待的时候,如果来了字符串该怎么办???我还是不很清楚这两个时间是怎么关联起来的。
不过目前看的话,应该是和OVERLAPPED结构有很大的关系。
阅读(...) 评论()sponsored links
QT串口通信中数据控制
QT串口通信中数据控制
( 11:42:47)
一、关于数据接收。
我们想在程序中对接收的数据进行控制,但是readyRead() 信号是一旦有数据到来就发射的,
不过我们可以使用bytesAvailable() 函数来检查已经获得的字节数,从而对数据接收进行控
(1)我们在widget.cpp 中添加头文件包含:#include& &QDebug&&
& 然后在读串口函数中添加一行代码,如下:
void Widget::readMyCom() //读取串口数据并显示出来
&&& qDebug() && &read: &&&myCom-&bytesAvailable()&&&bytes&;
&&& //我们输出每次获得的字节数
&&& QByteArray temp = myCom-&readAll();
&&& ui-&textBrowser-&insertPlainText(temp);
程序运行如下:
可以看到,我们获得的数据并不是一次获得的。
(2)利用上面的结论,我们可以让串口缓冲区拥有了一定的数据后再读取。
void Widget::readMyCom()&
if(myCom-&bytesAvailable() &=8 )&
//如果可用数据大于或等于8字节再读取&
&&&&&&& qDebug() && &read: &&&myCom-&bytesAvailable()&&&bytes&;
&&&&&&& QByteArray temp = myCom-&readAll();
&&&&&&& ui-&textBrowser-&insertPlainText(temp);
我们发送了两次数据,可以看到,这样实现了每 8 个字节读取一次,而最后剩余的不够 8个字节的数据将会和后面的数据一起读出。
重要结论:我们发送一次数据,应该获得 37字节的数据,然后我们对比上面的结果,发现
了什么?是的,其实串口每次读取 8 字节的数据放到缓冲区,只有数据总数小于 8 字节时,
才会读取小于8 字节的数据。为了再次验证我们的结论,我们可以将上面程序中的“&=”
改为“==”,那么只有 8 的倍数才能读取数据(当然这里 37也可以),你可以测试一下。
串口通信中数据的奇偶校验位算法 奇偶校验位是一个表示给定位数的二进制数中 1 的个数是奇数还是偶数的二进制数.奇偶校验位是最简单的错误检测码.奇偶校验位有两种类型:偶校验位与奇校验位.如果一组给定数据位中 1 的个数是奇数,那么偶校验位就置为 1,从而使得总的 1 的个数是偶数.如果给定一组数据位中 1 的个数是偶数,那么奇校验位就置为 1,使得总的 1 的 ...
C#解决串口通信中接收数据时延迟处理与缓存处理的方法 时间: 14:04:29 来源:www.cnblogs.com 作者:杨少宁 - - 利用串口进行通信,当发送方(A)将数据写入串口后,通过无线或有线方式将数据传送给接收方(B),B通过调用串口读方法comm.read(参数)即可将数据读出.原理十分简单,但最近在利用串口处理SM-4 ...
最近需要编写一个基于linux的QT串口通信程序,进行简单的数据收发功能,参考网上的例子之后,发现大多是基于windows开发的,只对linux环境的开发做了简单的介绍,所以本文章直接以linux环境来讲述QT串口开发实例. 我的操作环境及QT编辑/编译器版本如下: OS: fedora 17 (linux-3.3.4) QT: Qt Creator 1.3 ...
本文章原创于www.yafeilinux.com 转载请注明出处. Qt串口通信专题教程 前言 去年我使用Qt编写串口通信程序时,将自己的学习过程写成了教程(Qt编写串口通信程序全程图文讲解),但是由于时间等原因,我只实现了Windows下的串口通信,并没有去做Linux下的.自从教程发布到网上后,就不断有人提出相关的问题,而其中问的最多的就是,怎样在Lin ...
使用网上的qextserialport-1.2类,自行开发多线程串口通信.开发的过程中,出现两个问题:
问题1:我用信号槽跨线程调用串口类MyCom 发送和接收数据,中间运行的时候,会内存错误,Qt error LNK2001: unresolved external symbol,导致程序崩溃.
解决方法:原因是我自己写了个串口类MyCom,继承了 ...用MFC实现串口编程 > >用MFC实现串口编程  分类:
11:16 782人阅读 (0)
用MFC实现串口编程龚建伟评论:本文既写了在Windows中怎样用VC控件MSComm,又说明了API技术编程方法,在写用MSComm控件时,数据类型的转换说得不是太明白,初次涉猎串口编程的朋友恐怕看了还是编不出来;直接从底层编写的部分值得一读,说得较为详细,但你得先从VC教本上看一看什么是线程。一.串行通信的基本原理二.串口信号线的接法三.16位串口应用程序的简单回顾四.在MFC下的32位串口应用程序㈠ 使用ActiveX控件㈡ 使用32位的API 通信函数本文详细介绍了串行通信的基本原理,以及在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等信号线。(当然,在程序中也需要对相应的信号线进行设置。)以上接法,在设计程序时,直接进行数据的接收和发送就可以了,不需要对信号线的状态进行判断或设置。(如果应用的场合需要使用握手信号等,需要对相应的信号线的状态进行监测或设置。)三.16位串口应用程序的简单回顾16位串口应用程序中,使用的16位的Windows API通信函数:① OpenComm() 打开串口资源,并指定输入、输出缓冲区的大小(以字节计);CloseComm() 关闭串口;例:int idComDidComDev = OpenComm("COM1", );CloseComm(idComDev);② BuildCommDCB() 、setCommState()填写设备控制块DCB,然后对已打开的串口进行参数配置;例:DCBBuildCommDCB("COM1:2400,n,8,1", &dcb);SetCommState(&dcb);③ ReadComm 、WriteComm()对串口进行读写操作,即数据的接收和发送.例:char *m_pRReadComm(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-----&RegisteredActiveX Controls---&选择Components: Microsoft Communications Control,version 6.0 插入到当前的Workspace中。结果添加了类CMSComm(及相应文件:mscomm.h和mscomm.cpp )。⑵.在MainFrm.h中加入MSComm控件。protected:CMSComm m_ComP在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);  //选择COM1m_ComPort. SetInBufferSize(1024); //设置输入缓冲区的大小,Bytesm_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 vR  if(m_commCtrl.GetCommEvent()==2) {k=m_commCtrl.GetInBufferCount(); //接收到的字符数目if(k&0) {vResponse=m_commCtrl.GetInput(); //readSaveData(k,(unsigned char*) vResponse.parray-&pvData);} // 接收到字符,MSComm控件发送事件 }。。。。。 // 处理其他MSComm控件}void CMainFrame::OnCommSend() {。。。。。。。。 // 准备需要发送的命令,放在TxData[]中CByteAarray.RemoveAll();array.SetSize(Count);for(i=0;i&Ci++)array.SetAt(i, TxData[i]);m_ComPort.SetOutput(COleVariant(array)); // 发送数据}请大家认真关注第⑷、⑸中内容,在实际工作中是重点、难点所在。㈡ 使用32位的API 通信函数:可能很多朋友会觉得奇怪:用32位API函数编写串口通信程序,不就是把16位的API换成32位吗?16位的串口通信程序可是多年之前就有很多人研讨过了……此文主要想介绍一下在API串口通信中如何结合非阻塞通信、多线程等手段,编写出高质量的通信程序。特别是在CPU处理任务比较繁重、与外围设备中有大量的通信数据时,更有实际意义。⑴.在中MainFrm.cpp定义全局变量HANDLE    hC // 准备打开的串口的句柄HANDLE    hCommWatchT//辅助线程的全局函数⑵.打开串口,设置串口hCom =CreateFile( "COM2", GENERIC_READ | GENERIC_WRITE, // 允许读写0,          // 此项必须为0NULL,         // no security attrsOPEN_EXISTING,    //设置产生方式FILE_FLAG_OVERLAPPED, // 我们准备使用异步通信NULL );请大家注意,我们使用了FILE_FLAG_OVERLAPPED结构。这正是使用API实现非阻塞通信的关键所在。ASSERT(hCom!=INVALID_HANDLE_VALUE); //检测打开串口操作是否成功SetCommMask(hCom, EV_RXCHAR|EV_TXEMPTY );//设置事件驱动的类型SetupComm( hCom, ) ; //设置输入、输出缓冲区的大小PurgeComm( hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR| PURGE_RXCLEAR ); //清干净输入、输出缓冲区COMMTIMEOUTS CommTimeO //定义超时结构,并填写该结构…………SetCommTimeouts( hCom, &CommTimeOuts ) ;//设置读写操作所允许的超时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 ComS DWORD dwLClearCommError(hCom, &dwErrorFlags, &ComStat ) ;dwLength = ComStat.cbInQ //输入缓冲区有多少数据?if (dwLength & 0) {BOOL fReadSfReadStat = 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  fWriteS 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 finisheddwBytesSent += dwBytesW }综上,我们使用了多线程技术,在辅助线程中监视串口,有数据到达时依靠事件驱动,读入数据并向主线程报告(发送数据在主线程中,相对说来,下行命令的数据 总是少得多);并且,WaitCommEvent()、ReadFile()、WriteFile()都使用了非阻塞通信技术,依靠重叠 (overlapped)读写操作,让串口读写操作在后台运行。依托vc6.0丰富的功能,结合我们提及的技术,写出有强大控制能力的串口通信应用程序。就个人而言,我更偏爱API技术,因为控制手段要灵活的多,功能也要强大得多。Serial Communications in Win32Allen DenverMicrosoft Windows Developer SupportDecember 11, 1995Allen seldom eats breakfast, but if he had to pick a favorite, Win32 serial communications would be the top choice.AbstractSerial communications in Microsoft Win32 is significantly different from serial communications in 16-bit Microsoft Windows? Those familiar with 16-bit serial communications functions will have to relearn many parts of the system to program serial communications properly. This article will help to accomplish this. Those unfamiliar with serial communications will find this article a helpful foundation for development efforts.This article assumes the reader is familiar with the fundamentals of multiple threading and synchronization in Win32. In addition, a basic familiarity of the Win32 heap functions is useful to fully comprehend the memory management methods used by the sample, MTTTY, included with this article. For more information regarding these functions, consult the Platform SDK documentation, the Microsoft Win32 SDK Knowledge Base, or the Microsoft Developer Network Library. Application programming interfaces (APIs) that control user interface features of windows and dialog boxes, though not discussed here, are useful to know in order to fully comprehend the sample provided with this article. Readers unfamiliar with general Windows programming practices should learn some of the fundamentals of general Windows programming before taking on serial communications. In other words, get your feet wet before diving in head first.IntroductionThe focus of this article is on application programming interfaces (APIs) and methods that are compatible with Microsoft?Windows NT?and Windows 95; therefore, APIs supported on both platforms are the only ones discussed. Windows 95 supports the Win32?Telephony API (TAPI) and Windows NT 3. therefore, this discussion will not include TAPI. TAPI does deserve mention, however, in that it very nicely implements modem interfacing and call controlling. A production application that works with modems and makes telephone calls should implement these features using the TAPI interface. This will allow seamless integration with the other TAPI-enabled applications that a user may have. Furthermore, this article does not discuss some of the configuration functions in Win32, such as GetCommProperties.The article is broken into the following sections: Opening a port, reading and writing (nonoverlapped and overlapped), serial status (events and errors), and serial settings (DCB, flow control, and communications time-outs).The sample included with this article, MTTTY: Multithreaded TTY, implements many of the features discussed here. It uses three threads in its implementation: a user interface thread that does memory management, a writer thread that controls all writing, and a reader/status thread that reads data and handles status changes on the port. The sample employs a few different data heaps for memory management. It also makes extensive use of synchronization methods to facilitate communication between threads.Opening a PortThe CreateFile function opens a communications port. There are two ways to call CreateFile to open the communications port: overlapped and nonoverlapped. The following is the proper way to open a communications resource for overlapped operation:HANDLE hChComm = CreateFile( gszPort,GENERIC_READ | GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,0);if (hComm == INVALID_HANDLE_VALUE)// abortRemoval of the FILE_FLAG_OVERLAPPED flag from the call to CreateFile specifies nonoverlapped operation. The next section discusses overlapped and nonoverlapped operations.The Platform SDK documentation states that when opening a communications port, the call to CreateFile has the following requirements:· fdwShareMode must be zero. Communications ports cannot be shared in the same manner that files are shared. Applications using TAPI can use the TAPI functions to facilitate sharing resources between applications. For Win32 applications not using TAPI, handle inheritance or duplication is necessary to share the communications port. Handle duplication is beyond the s please refer to the Platform SDK documentation for more information.· fdwCreate must specify the OPEN_EXISTING flag.· hTemplateFile parameter must be NULL.One thing to note about port names is that traditionally they have been COM1, COM2, COM3, or COM4. The Win32 API does not provide any mechanism for determining what ports exist on a system. Windows NT and Windows 95 keep track of installed ports differently from one another, so any one method would not be portable across all Win32 platforms. Some systems even have more ports than the traditional maximum of four. Hardware vendors and serial-device-driver writers are free to name the ports anything they like. For this reason, it is best that users have the ability to specify the port name they want to use. If a port does not exist, an error will occur (ERROR_FILE_NOT_FOUND) after attempting to open the port, and the user should be notified that the port isn’t available.Reading and WritingReading from and writing to communications ports in Win32 is very similar to file input/output (I/O) in Win32. In fact, the functions that accomplish file I/O are the same functions used for serial I/O. I/O in Win32 can be done either of two ways: overlapped or nonoverlapped. The Platform SDK documentation uses the terms asynchronous and synchronous to connote these types of I/O operations. This article, however, uses the terms overlapped and nonoverlapped.Nonoverlapped I/O is familiar to most developers because this is the traditional form of I/O, where an operation is requested and is assumed to be complete when the function returns. In the case of overlapped I/O, the system may return to the caller immediately even when an operation is not finished and will signal the caller when the operation completes. The program may use the time between the I/O request and its completion to perform some “background?work.Reading and writing in Win32 is significantly different from reading and writing serial communications ports in 16-bit Windows. 16-bit Windows only has the ReadComm and WriteComm functions. Win32 reading and writing can involve many more functions and choices. These issues are discussed below.Nonoverlapped I/ONonoverlapped I/O is very straightforward, though it has limitations. An operation takes place while the calling thread is blocked. Once the operation is complete, the function returns and the thread can continue its work. This type of I/O is useful for multithreaded applications because while one thread is blocked on an I/O operation, other threads can still perform work. It is the responsibility of the application to serialize access to the port correctly. If one thread is blocked waiting for its I/O operation to complete, all other threads that subsequently call a communications API will be blocked until the original operation completes. For instance, if one thread were waiting for a ReadFile function to return, any other thread that issued a WriteFile function would be blocked.One of the many factors to consider when choosing between nonoverlapped and overlapped operations is portability. Overlapped operation is not a good choice because most operating systems do not support it. Most operating systems support some form of multithreading, however, so multithreaded nonoverlapped I/O may be the best choice for portability reasons.Overlapped I/OOverlapped I/O is not as straightforward as nonoverlapped I/O, but allows more flexibility and efficiency. A port open for overlapped operations allows multiple threads to do I/O operations at the same time and perform other work while the operations are pending. Furthermore, the behavior of overlapped operations allows a single thread to issue many different requests and do work in the background while the operations are pending.In both single-threaded and multithreaded applications, some synchronization must take place between issuing requests and processing the results. One thread will have to be blocked until the result of an operation is available. The advantage is that overlapped I/O allows a thread to do some work between the time of the request and its completion. If no work can be done, then the only case for overlapped I/O is that it allows for better user responsiveness.Overlapped I/O is the type of operation that the MTTTY sample uses. It creates a thread that is responsible for reading the port’s data and reading the port’s status. It also performs periodic background work. The program creates another thread exclusively for writing data out the port.Note Applications sometimes abuse multithreading systems by creating too many threads. Although using multiple threads can resolve many difficult problems, creating excessive threads is not the most efficient use of them in an application. Threads are less a strain on the system than processes but still require system resources such as CPU time and memory. An application that creates excessive threads may adversely affect the performance of the entire system. A better use of threads is to create a different request queue for each type of job and to have a worker thread issue an I/O request for each entry in the request queue. This method is used by the MTTTY sample provided with this article.An overlapped I/O operation has two parts: the creation of the operation and the detection of its completion. Creating the operation entails setting up an OVERLAPPED structure, creating a manual-reset event for synchronization, and calling the appropriate function (ReadFile or WriteFile). The I/O operation may or may not be completed immediately. It is an error for an application to assume that a request for an overlapped operation always yields an overlapped operation. If an operation is completed immediately, an application needs to be ready to continue processing normally. The second part of an overlapped operation is to detect its completion. Detecting completion of the operation involves waiting for the event handle, checking the overlapped result, and then handling the data. The reason that there is more work involved with an overlapped operation is that there are more points of failure. If a nonoverlapped operation fails, the function just returns an error-return result. If an overlapped operation fails, it can fail in the creation of the operation or while the operation is pending. You may also have a time-out of the operation or a time-out waiting for the signal that the operation is complete.ReadingThe ReadFile function issues a read operation. ReadFileEx also issues a read operation, but since it is not available on Windows 95, it is not discussed in this article. Here is a code snippet that details how to issue a read request. Notice that the function calls a function to process the data if the ReadFile returns TRUE. This is the same function called if the operation becomes overlapped. Note the fWaitingOnRead flag that is it indicates whether or not a read operation is overlapped. It is used to prevent the creation of a new read operation if one is outstanding.DWORD dwRBOOL fWaitingOnRead = FALSE;OVERLAPPED osReader = {0};// Create the overlapped event. Must be closed before exiting// to avoid a handle leak.osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);if (osReader.hEvent == NULL)// Error creat abort.if (!fWaitingOnRead) {// Issue read operation.if (!ReadFile(hComm, lpBuf, READ_BUF_SIZE, &dwRead, &osReader)) {if (GetLastError() != ERROR_IO_PENDING) // read not delayed?// Err report it.elsefWaitingOnRead = TRUE;}else {// read completed immediatelyHandleASuccessfulRead(lpBuf, dwRead);}}The second part of the overlapped operation is the detection of its completion. The event handle in the OVERLAPPED structure is passed to the WaitForSingleObject function, which will wait until the object is signaled. Once the event is signaled, the operation is complete. This does not mean that it was completed successfully, just that it was completed. The GetOverlappedResult function reports the result of the operation. If an error occurred, GetOverlappedResult returns FALSE and GetLastError returns the error code. If the operation was completed successfully, GetOverlappedResult will return TRUE.Note GetOverlappedResult can detect completion of the operation, as well as return the operation’s failure status. GetOverlappedResult returns FALSE and GetLastError returns ERROR_IO_INCOMPLETE when the operation is not completed. In addition, GetOverlappedResult can be made to block until the operation completes. This effectively turns the overlapped operation into a nonoverlapped operation and is accomplished by passing TRUE as the bWait parameter.Here is a code snippet that shows one way to detect the completion of an overlapped read operation. Note that the code calls the same function to process the data that was called when the operation completed immediately. Also note the use of the fWaitingOnRead flag. Here it controls entry into the detection code, since it should be called only when an operation is outstanding.#define READ_TIMEOUT 500 // millisecondsDWORD dwRif (fWaitingOnRead) {dwRes = WaitForSingleObject(osReader.hEvent, READ_TIMEOUT);switch(dwRes){// Read completed.case WAIT_OBJECT_0:if (!GetOverlappedResult(hComm, &osReader, &dwRead, FALSE))// Err report it.else// Read completed successfully.HandleASuccessfulRead(lpBuf, dwRead);// Reset flag so that another opertion can be issued.fWaitingOnRead = FALSE;case WAIT_TIMEOUT:// Operation isn't complete yet. fWaitingOnRead flag isn't// changed since I'll loop back around, and I don't want// to issue another read until the first one finishes.//// This is a good time to do some background work.default:// Error in the WaitForSingleO abort.// This indicates a problem with the OVERLAPPED structure's// event handle.}}WritingTransmitting data out the communications port is very similar to reading in that it uses a lot of the same APIs. The code snippet below demonstrates how to issue and wait for a write operation to be completed.BOOL WriteABuffer(char * lpBuf, DWORD dwToWrite){OVERLAPPED osWrite = {0};DWORD dwWDWORD dwRBOOL fR// Create this write operation's OVERLAPPED structure's hEvent.osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);if (osWrite.hEvent == NULL)// error creating overlapped event handlereturn FALSE;// Issue write.if (!WriteFile(hComm, lpBuf, dwToWrite, &dwWritten, &osWrite)) {if (GetLastError() != ERROR_IO_PENDING) {// WriteFile failed, but isn't delayed. Report error and abort.fRes = FALSE;}else// Write is pending.dwRes = WaitForSingleObject(osWrite.hEvent, INFINITE);switch(dwRes){// OVERLAPPED structure's event has been signaled.case WAIT_OBJECT_0:if (!GetOverlappedResult(hComm, &osWrite, &dwWritten, FALSE))fRes = FALSE;else// Write operation completed successfully.fRes = TRUE;default:// An error has occurred in WaitForSingleObject.// This usually indicates a problem with the// OVERLAPPED structure's event handle.fRes = FALSE;}}}else// WriteFile completed immediately.fRes = TRUE;CloseHandle(osWrite.hEvent);return fR}Notice that the code above uses the WaitForSingleObject function with a time-out value of INFINITE. This causes the WaitForSingleObject function to wait forever until the op this may make the thread or program appear to be “hung?when, in fact, the write operation is simply taking a long time to complete or flow control has blocked the transmission. Status checking, discussed later, can detect this condition, but doesn’t cause the WaitForSingleObject to return. Three things can alleviate this condition:· Place the code in a separate thread. This allows other threads to execute any functions they desire while our writer thread waits for the write to be completed. This is what the MTTTY sample does.· Use COMMTIMEOUTS to cause the write to be completed after a time-out period has passed. This is discussed more fully in the “Communications Time-outs?section later in this article. This is also what the MTTTY sample allows.· Change the WaitForSingleObject call to include a real time-out value. This causes more problems because if the program issues another operation while an older operation is still pending, new OVERLAPPED structures and overlapped events need to be allocated. This type of recordkeeping is difficult, particularly when compared to using a “job queue?design for the operations. The “job queue?method is used in the MTTTY sample.Note: The time-out values in synchronization functions are not communications time-outs. Synchronization time-outs cause WaitForSingleObject or WaitForMultipleObjects to return WAIT_TIMEOUT. This is not the same as a read or write operation timing out. Communications time-outs are described later in this article.Because the WaitForSingleObject function in the above code snippet uses an INFINITE time-out, it is equivalent to using GetOverlappedResult with TRUE for the fWait parameter. Here is equivalent code in a much simplified form:BOOL WriteABuffer(char * lpBuf, DWORD dwToWrite){OVERLAPPED osWrite = {0};DWORD dwWBOOL fR// Create this writes OVERLAPPED structure hEvent.osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);if (osWrite.hEvent == NULL)// Error creating overlapped event handle.return FALSE;// Issue write.if (!WriteFile(hComm, lpBuf, dwToWrite, &dwWritten, &osWrite)) {if (GetLastError() != ERROR_IO_PENDING) {// WriteFile failed, but it isn't delayed. Report error and abort.fRes = FALSE;}else {// Write is pending.if (!GetOverlappedResult(hComm, &osWrite, &dwWritten, TRUE))fRes = FALSE;else// Write operation completed successfully.fRes = TRUE;}}else// WriteFile completed immediately.fRes = TRUE;CloseHandle(osWrite.hEvent);return fR}GetOverlappedResult is not always the best way to wait for an overlapped operation to be completed. For example, if an application needs to wait on another event handle, the first code snippet serves as a better model than the second. The call to WaitForSingleObject is easy to change to WaitForMultipleObjects to include the additional handles on which to wait. This is what the MTTTY sample application does.A common mistake in overlapped I/O is to reuse an OVERLAPPED structure before the previous overlapped operation is completed. If a new overlapped operation is issued before a previous operation is completed, a new OVERLAPPED structure must be allocated for it. A new manual-reset event for the hEvent member of the OVERLAPPED structure must also be created. Once an overlapped operation is complete, the OVERLAPPED structure and its event are free for reuse.The only member of the OVERLAPPED structure that needs modifying for serial communications is the hEvent member. The other members of the OVERLAPPED structure should be initialized to zero and left alone. Modifying the other members of the OVERLAPPED structure is not necessary for serial communications devices. The documentation for ReadFile and WriteFile state that the Offset and OffsetHigh members of the OVERLAPPED structure must be updated by the application, or else results are unpredictable. This guideline should be applied to OVERLAPPED structures used for other types of resources, such as files.Serial StatusThere are two methods to retrieve the status of a communications port. The first is to set an event mask that causes notification of the application when the desired events occur. The SetCommMask function sets this event mask, and the WaitCommEvent function waits for the desired events to occur. These functions are similar to the 16-bit functions SetCommEventMask and EnableCommNotification, except that the Win32 functions do not post WM_COMMNOTIFY messages. In fact, the WM_COMMNOTIFY message is not even part of the Win32 API. The second method for retrieving the status of the communications port is to periodically call a few different status functions. Polling is, of course, neither efficient nor recommended.Communications EventsCommunications events can occur at any time in the course of using a communications port. The two steps involved in receiving notification of communications events are as follows:· SetCommMask sets the desired events that cause a notification.· WaitCommEvent issues a status check. The status check can be an overlapped or nonoverlapped operation, just as the read and write operations can be.Note: The word event in this context refers to communications events only. It does not refer to an event object used for synchronization.Here is an example of the SetCommMask function:DWORD dwStoredFdwStoredFlags = EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RING |\EV_RLSD | EV_RXCHAR | EV_RXFLAG | EV_TXEMPTY ;if (!SetCommMask(hComm, dwStoredFlags))// error setting communications maskA description of each type of event is in Table 1.Table 1. Communications Event FlagsEvent Flag DescriptionEV_BREAK A break was detected on input.EV_CTS The CTS (clear-to-send) signal changed state. To get the actual state of the CTS line, GetCommModemStatus should be called.EV_DSR The DSR (data-set-ready) signal changed state. To get the actual state of the DSR line, GetCommModemStatus should be called.EV_ERR A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. To find the cause of the error, ClearCommError should be called.EV_RING A ring indicator was detected.EV_RLSD The RLSD (receive-line-signal-detect) signal changed state. To get the actual state of the RLSD line, GetCommModemStatus should be called. Note that this is commonly referred to as the CD (carrier detect) line.EV_RXCHAR A new character was received and placed in the input buffer. See the “Caveat?section below for a discussion of this flag.EV_RXFLAG The event character was received and placed in the input buffer. The event character is specified in the EvtChar member of the DCB structure discussed later. The “Caveat?section below also applies to this flag.EV_TXEMPTY The last character in the output buffer was sent to the serial port device. If a hardware buffer is used, this flag only indicates that all data has been sent to the hardware. There is no way to detect when the hardware buffer is empty without talking directly to the hardware with a device driver.After specifying the event mask, the WaitCommEvent function detects the occurrence of the events. If the port is open for nonoverlapped operation, then the WaitCommEvent function does not contain an OVERLAPPED structure. The function blocks the calling thread until the occurrence of one of the events. If an event never occurs, the thread may block indefinitely.Here is a code snippet that shows how to wait for an EV_RING event when the port is open for nonoverlapped operation:DWORD dwCommEif (!SetCommMask(hComm, EV_RING))// Error setting communications maskreturn FALSE;if (!WaitCommEvent(hComm, &dwCommEvent, NULL))// An error occurred waiting for the event.return FALSE;else// Event has occurred.return TRUE;Note The Microsoft Win32 SDK Knowledge Base documents a problem with Windows 95 and the EV_RING flag. The above code never returns in Windows 95 because the EV_RING event is not de Windows NT properly reports the EV_RING event. Please see the Win32 SDK Knowledge Base for more information on this bug.As noted, the code above can be blocked forever if an event never occurs. A better solution would be to open the port for overlapped operation and wait for a status event in the following manner:#define STATUS_CHECK_TIMEOUT 500 // MillisecondsDWORD dwRDWORD dwCommEDWORD dwStoredFBOOL fWaitingOnStat = FALSE;OVERLAPPED osStatus = {0};dwStoredFlags = EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RING |\EV_RLSD | EV_RXCHAR | EV_RXFLAG | EV_TXEMPTY ;if (!SetCommMask(comHandle, dwStoredFlags))// error setting abortreturn 0;osStatus.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);if (osStatus.hEvent == NULL)//
abortreturn 0;for ( ; ; ) {// Issue a status event check if one hasn't been issued already.if (!fWaitingOnStat) {if (!WaitCommEvent(hComm, &dwCommEvent, &osStatus)) {if (GetLastError() == ERROR_IO_PENDING)bWaitingOnStatusHandle = TRUE;else// error in WaitCommE abort}else// WaitCommEvent returned immediately.// Deal with status event as appropriate.ReportStatusEvent(dwCommEvent);}// Check on overlapped operation.if (fWaitingOnStat) {// Wait a little while for an event to occur.dwRes = WaitForSingleObject(osStatus.hEvent, STATUS_CHECK_TIMEOUT);switch(dwRes){// Event occurred.case WAIT_OBJECT_0:if (!GetOverlappedResult(hComm, &osStatus, &dwOvRes, FALSE))// An error occurred in the // call GetLastError to find out what it was// and abort if it is fatal.else// Status event is stored in the event flag// specified in the original WaitCommEvent call.// Deal with the status event as appropriate.ReportStatusEvent(dwCommEvent);// Set fWaitingOnStat flag to indicate that a new// WaitCommEvent is to be issued.fWaitingOnStat = FALSE;case WAIT_TIMEOUT:// Operation isn't complete yet. fWaitingOnStatusHandle flag// isn't changed since I'll loop back around and I don't want// to issue another WaitCommEvent until the first one finishes.//// This is a good time to do some background work.DoBackgroundWork();default:// Error in the WaitForSingleO abort// This indicates a problem with the OVERLAPPED structure's// event handle.CloseHandle(osStatus.hEvent);return 0;}}}CloseHandle(osStatus.hEvent);The code above very closely resembles the code for overlapped reading. In fact, the MTTTY sample implements its reading and status checking in the same thread using WaitForMultipleObjects to wait for either the read event or the status event to become signaled.There are two interesting side effects of SetCommMask and WaitCommEvent. First, if the communications port is open for nonoverlapped operation, WaitCommEvent will be blocked until an event occurs. If another thread calls SetCommMask to set a new event mask, that thread will be blocked on the call to SetCommMask. The reason is that the original call to WaitCommEvent in the first thread is still executing. The call to SetCommMask blocks the thread until the WaitCommEvent function returns in the first thread. This side effect is universal for ports open for nonoverlapped I/O. If a thread is blocked on any communications function and another thread calls a communications function, the second thread is blocked until the communications function returns in the first thread. The second interesting note about these functions is their use on a port open for overlapped operation. If SetCommMask sets a new event mask, any pending WaitCommEvent will complete successfully, and the event mask produced by the operation is NULL.CaveatUsing the EV_RXCHAR flag will notify the thread that a byte arrived at the port. This event, used in combination with the ReadFile function, enables a program to read data only after it is in the receive buffer, as opposed to issuing a read that waits for the data to arrive. This is particularly useful when a port is open for nonoverlapped operation because the program does not need to po the program is notified of the incoming data by the occurrence of the EV_RXCHAR event. Initial attempts to code this solution often produce the following pseudocode, including one oversight covered later in this section:DWORD dwCommEDWORD dwRchar chRif (!SetCommMask(hComm, EV_RXCHAR))// Error setting communications event mask.for ( ; ; ) {if (WaitCommEvent(hComm, &dwCommEvent, NULL)) {if (ReadFile(hComm, &chRead, 1, &dwRead, NULL))// A process it.else// An error occurred in the ReadFile call.}else// Error in WaitCommEvent.}The above code waits for an EV_RXCHAR event to occur. When this happens, the code calls ReadFile to read the one byte received. The loop starts again, and the code waits for another EV_RXCHAR event. This code works fine when one or two bytes arrive in quick succession. The byte reception causes the EV_RXCHAR event to occur. The code reads the byte. If no other byte arrives before the code calls WaitCommEvent again, the next byte to arrive will cause the WaitCommEvent function to indicate the occurrence of the EV_RXCHAR event. If another single byte arrives before the code has a chance to reach the WaitCommEvent function, then all is fine, too. The first by the arrival of the second byte causes the EV_RXCHAR flag to be set internally. When the code returns to the WaitCommEvent function, it indicates the occurrence of the EV_RXCHAR event and the second byte is read from the port in the ReadFile call.The problem with the above code occurs when three or more bytes arrive in quick succession. The first byte causes the EV_RXCHAR event to occur. The second byte causes the EV_RXCHAR flag to be set internally. The next time the code calls WaitCommEvent, it indicates the EV_RXCHAR event. Now, a third byte arrives at the communications port. This third byte causes the system to attempt to set the EV_RXCHAR flag internally. Because this has already occurred when the second byte arrived, the arrival of the third byte goes unnoticed. The code eventually will read the first byte without a problem. After this, the code will call WaitCommEvent, and it indicates the occurrence of the EV_RXCHAR event (from the arrival of the second byte). The second byte is read, and the code returns to the WaitCommEvent function. The third byte waits in the system’s internal receive buffer. The code and the system are now out of sync. When a fourth byte finally arrives, the EV_RXCHAR event occurs, and the code reads a single byte. It reads the third byte. This will continue indefinitely.The solution to this problem seems as easy as increasing the number of bytes requested in the read operation. Instead of requesting a single byte, the code could request two, ten, or some other number of bytes. The problem with this idea is that it still fails when two or more extra bytes above the size of the read request arrive at the port in quick succession. So, if two bytes are read, then four bytes arriving in quick succession would cause the problem. Ten bytes requested would still fail if twelve bytes arrived in quick succession.The real solution to this problem is to read from the port until no bytes are remaining. The following pseudocode solves the problem by reading in a loop until zero characters are read. Another possible method would be to call ClearCommError to determine the number of bytes in the buffer and read them all in one read operation. This method requires more sophisticated buffer management, but it reduces the number of reads when a lot of data arrives at once.DWORD dwCommEDWORD dwRchar chRif (!SetCommMask(hComm, EV_RXCHAR))// Error setting communications event maskfor ( ; ; ) {if (WaitCommEvent(hComm, &dwCommEvent, NULL)) {do {if (ReadFile(hComm, &chRead, 1, &dwRead, NULL))// A process it.else// An error occurred in the ReadFile call.} while (dwRead);}else// Error in WaitCommEvent}The above code does not work correctly without setting the proper time-outs. Communications time-outs, discussed later, affect the behavior of the ReadFile operation in order to cause it to return without waiting for bytes to arrive. Discussion of this topic occurs later in the “Communications Time-outs?section of this article.The above caveat regarding EV_RXCHAR also applies to EV_RXFLAG. If flag characters arrive in quick succession, EV_RXFLAG events may not occur for all of them. Once again, the best solution is to read all bytes until none remain.The above caveat also applies to other events not related to character reception. If other events occur in quick succession some of the notifications will be lost. For instance, if the CTS line voltage starts high, then goes low, high, and low again, an EV_CTS event occurs. There is no guarantee of how many EV_CTS events will actually be detected with WaitCommEvent if the changes in the CTS line happen quickly. For this reason, WaitCommEvent cannot be used to keep track of the state of the line. Line status is covered in the “Modem Status?section later in this article.Error Handling and Communications StatusOne of the communications event flags specified in the call to SetCommMask is possibly EV_ERR. The occurrence of the EV_ERR event indicates that an error condition exists in the communications port. Other errors can occur in the port that do not cause the EV_ERR event to occur. In either case, errors associated with the communications port cause all I/O operations to be suspended until removal of the error condition. ClearCommError is the function to call to detect errors and clear the error condition.ClearCommError also provides communications status indicating why tran it also indicates the number of bytes waiting in the transmit and receive buffers. The reason why transmission may stop is because of errors or to flow control. The discussion of flow control occurs later in this article.Here is some code that demonstrates how to call ClearCommError:COMSTAT comSDWORD dwEBOOL fOOP, fOVERRUN, fPTO, fRXOVER, fRXPARITY, fTXFULL;BOOL fBREAK, fDNS, fFRAME, fIOE, fMODE;// Get and clear current errors on the port.if (!ClearCommError(hComm, &dwErrors, &comStat))// Report error in ClearCommError.// Get error flags.fDNS = dwErrors & CE_DNS;fIOE = dwErrors & CE_IOE;fOOP = dwErrors & CE_OOP;fPTO = dwErrors & CE_PTO;fMODE = dwErrors & CE_MODE;fBREAK = dwErrors & CE_BREAK;fFRAME = dwErrors & CE_FRAME;fRXOVER = dwErrors & CE_RXOVER;fTXFULL = dwErrors & CE_TXFULL;fOVERRUN = dwErrors & CE_OVERRUN;fRXPARITY = dwErrors & CE_RXPARITY;// COMSTAT structure contains information regarding// communications status.if (comStat.fCtsHold)// Tx waiting for CTS signalif (comStat.fDsrHold)// Tx waiting for DSR signalif (comStat.fRlsdHold)// Tx waiting for RLSD signalif (comStat.fXoffHold)// Tx waiting, XOFF char rec'dif (comStat.fXoffSent)// Tx waiting, XOFF char sentif (comStat.fEof)// EOF character receivedif (comStat.fTxim)// Character waiting for Tx; char queued with TransmitCommCharif (comStat.cbInQue)// comStat.cbInQue bytes have been received, but not readif (comStat.cbOutQue)// comStat.cbOutQue bytes are awaiting transferModem Status (a.k.a. Line Status)The call to SetCommMask may include the flags EV_CTS, EV_DSR, EV_RING, and EV_RLSD. These flags indicate changes in the voltage on the lines of the serial port. There is no indication of the actual status of these lines, just that a change occurred. The GetCommModemStatus function retrieves the actual state of these status lines by returning a bit mask indicating a 0 for low or no voltage and 1 for high voltage for each of the lines.Please note that the term RLSD (Receive Line Signal Detect) is commonly referred to as the CD (Carrier Detect) line.Note The EV_RING flag does not work in Windows 95 as mentioned earlier. The GetCommModemStatus function, however, does detect the state of the RING line.Changes in these lines may also cause a flow-control event. The ClearCommError function reports whether transmission is suspended because of flow control. If necessary, a thread may call ClearCommError to detect whether the event is the cause of a flow-control action. Flow control is covered in the “Flow Control?section later in this article.Here is some code that demonstrates how to call GetCommModemStatus:DWORD dwModemSBOOL fCTS, fDSR, fRING, fRLSD;if (!GetCommModemStatus(hComm, &dwModemStatus))// Error in GetCommModemSfCTS = MS_CTS_ON & dwModemSfDSR = MS_DSR_ON & dwModemSfRING = MS_RING_ON & dwModemSfRLSD = MS_RLSD_ON & dwModemS// Do something with the flags.Extended FunctionsThe driver will automatically change the state of control lines as necessary. Generally speaking, changing status lines is under the control of a driver. If a device uses communications port control lines in a manner different from RS-232 standards, the standard serial communications driver will not work to control the device. If the standard serial communications driver will not control the device, a custom device driver is necessary.There are occasions when standard control lines are under the control of the application instead of the serial communications driver. For instance, an application may wish to implement its own flow control. The application would be responsible for changing the status of the RTS and DTR lines. EscapeCommFunction directs a communications driver to perform such extended operations. EscapeCommFunction can make the driver perform some other function, such as setting or clearing a BREAK condition. For more information on this function, consult the Platform SDK documentation, the Microsoft Win32 SDK Knowledge Base, or the Microsoft Developer Network (MSDN) Library.Serial SettingsDCB SettingsThe most crucial aspect of programming serial communications applications is the settings in the Device-Control Block (DCB) structure. The most common errors in serial communications programming occur in initializing the DCB structure improperly. When the serial communications functions do not behave as expected, a close examination of the DCB structure usually reveals the problem.There are three ways to initialize a DCB structure. The first method is to use the function GetCommState. This function returns the current DCB in use for the communications port. The following code shows how to use the GetCommState function:DCB dcb = {0};if (!GetCommState(hComm, &dcb))// Error getting current DCB settingselse// DCB is ready for use.The second method to initialize a DCB is to use a function called BuildCommDCB. This function fills in the baud, parity type, number of stop bits, and number of data bits members of the DCB. The function also sets the flow-control members to default values. Consult the documentation of the BuildCommDCB function for details on which default values it uses for flow-control members. Other members of the DCB are unaffected by this function. It is the program's duty to make sure the other members of the DCB do not cause errors. The simplest thing to do in this regard is to initialize the DCB structure with zeros and then set the size member to the size, in bytes, of the structure. If the zero initialization of the DCB structure does not occur, then there may be nonzero values in
this produces an error when trying to use the DCB later. The following function shows how to properly use this method:DCBFillMemory(&dcb, sizeof(dcb), 0);dcb.DCBlength = sizeof(dcb);if (!BuildCommDCB("9600,n,8,1", &dcb)) {// Couldn't build the DCB. Usually a problem// with the communications specification string.return FALSE;}else// DCB is ready for use.The third method to initialize a DCB structure is to do it manually. The program allocates the DCB structure and sets each member with any value desired. This method does not deal well with changes to the DCB in future implementations of Win32 and is not recommended.An application usually needs to set some of the DCB members differently than the defaults or may need to modify settings in the middle of execution. Once proper initialization of the DCB occurs, modification of individual members is possible. The changes to the DCB structure do not have any effect on the behavior of the port until execution of the SetCommState function. Here is a section of code that retrieves the current DCB, changes the baud, and then attempts to set the configuration:DCBFillMemory(&dcb, sizeof(dcb), 0);if (!GetCommState(hComm, &dcb)) // get current DCB// Error in GetCommStatereturn FALSE;// Update DCB rate.dcb.BaudRate = CBR_9600 ;// Set new state.if (!SetCommState(hComm, &dcb))// Error in SetCommState. Possibly a problem with the communications// port handle or a problem with the DCB structure itself.Here is an explanation of each of the members of the DCB and how they affect other parts of the serial communications functions.Note Most of this information is from the Platform SDK documentation. Because documentation is the official word in what the members actually are and what they mean, this table may not be completely accurate if changes occur in the operating system.Table 2. The DCB Structure MembersMember DescriptionDCBlength Size, in bytes, of the structure. Should be set before calling SetCommState to update the settings.BaudRate Specifies the baud at which the communications device operates. This member can be an actual baud value, or a baud index.fBinary Specifies whether binary mode is enabled. The Win32 API does not support nonbinary mode transfers, so this member should be TRUE. Trying to use FALSE will not work.fParity Specifies whether parity checking is enabled. If this member is TRUE, parity checking is performed and parity errors are reported. This should not be confused with the Parity member, which controls the type of parity used in communications.fOutxCtsFlow Specifies whether the CTS (clear-to-send) signal is monitored for output flow control. If this member is TRUE and CTS is low, output is suspended until CTS is high again. The CTS signal is under control of the DCE (usually a modem), the DTE (usually the PC) simply monitors the status of this signal, the DTE does not change it.fOutxDsrFlow Specifies whether the DSR (data-set-ready) signal is monitored for output flow control. If this member is TRUE and DSR is low, output is suspended until DSR is high again. Once again, this signal is under the control of the DCE; the DTE only monitors this signal.fDtrControl Specifies the DTR (data-terminal-ready) input flow control. This member can be one of the following values:Value MeaningDTR_CONTROL_DISABLE Lowers the DTR line when the device is opened. The application can adjust the state of the line with EscapeCommFunction.DTR_CONTROL_ENABLE Raises the DTR line when the device is opened. The application can adjust the state of the line with EscapeCommFunction.DTR_CONTROL_HANDSHAKE Enables DTR flow-control handshaking. If this value is used, it is an error for the application to adjust the line with EscapeCommFunction.fDsrSensitivity Specifies whether the communications driver is sensitive to the state of the DSR signal. If this member is TRUE, the driver ignores any bytes received, unless the DSR modem input line is high.fTXContinueOnXoff Specifies whether transmission stops when the input buffer is full and the driver has transmitted the XOFF character. If this member is TRUE, transmission continues after the XOFF character has been sent. If this member is FALSE, transmission does not continue until the input buffer is within XonLim bytes of being empty and the driver has transmitted the XON character.fOutX Specifies whether XON/XOFF flow control is used during transmission. If this member is TRUE, transmission stops when the XOFF character is received and starts again when the XON character is received.fInX Specifies whether XON/XOFF flow control is used during reception. If this member is TRUE, the XOFF character is sent when the input buffer comes within XoffLim bytes of being full, and the XON character is sent when the input buffer comes within XonLim bytes of being empty.fErrorChar Specifies whether bytes received with parity errors are replaced with the character specified by the ErrorChar member. If this member is TRUE and the fParity member is TRUE, replacement occurs.fNull Specifies whether null bytes are discarded. If this member is TRUE, null bytes are discarded when received.fRtsControl Specifies the RTS (request-to-send) input flow control. If this value is zero, the default is RTS_CONTROL_HANDSHAKE. This member can be one of the following values:Value MeaningRTS_CONTROL_DISABLE Lowers the RTS line when the device is opened. The application can use EscapeCommFunction to change the state of the line.RTS_CONTROL_ENABLE Raises the RTS line when the device is opened. The application can use EscapeCommFunction to change the state of the line.RTS_CONTROL_HANDSHAKE Enables RTS flow-control handshaking. The driver raises the RTS line, enabling the DCE to send, when the input buffer has enough room to receive data. The driver lowers the RTS line, preventing the DCE to send, when the input buffer does not have enough room to receive data. If this value is used, it is an error for the application to adjust the line with EscapeCommFunction.RTS_CONTROL_TOGGLE Specifies that the RTS line will be high if bytes are available for transmission. After all buffered bytes have been sent, the RTS line will be low. If this value is set, it would be an error for an application to adjust the line with EscapeCommFunction. This value is ignored in Windows 95; it causes the driver to act as if RTS_CONTROL_ENABLE were specified.fAbortOnError Specifies whether read and write operations are terminated if an error occurs. If this member is TRUE, the driver terminates all read and write operations with an error status (ERROR_IO_ABORTED) if an error occurs. The driver will not accept any further communications operations until the application has acknowledged the error by calling the ClearCommError function.fDummy2 R do not use.wReserved N must be set to zero.XonLim Specifies the minimum number of bytes allowed in the input buffer before the XON character is sent.XoffLim Specifies the maximum number of bytes allowed in the input buffer before the XOFF character is sent. The maximum number of bytes allowed is calculated by subtracting this value from the size, in bytes, of the input buffer.Parity Specifies the parity scheme to be used. This member can be one of the following values:Value MeaningEVENPARITY EvenMARKPARITY MarkNOPARITY No parityODDPARITY OddStopBits Specifies the number of stop bits to be used. This member can be one of the following values:Value MeaningONESTOPBIT 1 stop bitONE5STOPBITS 1.5 stop bitsTWOSTOPBITS 2 stop bitsXonChar Specifies the value of the XON character for both transmission and reception.XoffChar Specifies the value of the XOFF character for both transmission and reception.ErrorChar Specifies the value of the character used to replace bytes received with a parity error.EofChar Specifies the value of the character used to signal the end of data.EvtChar Specifies the value of the character used to cause the EV_RXFLAG event. This setting does not actually cause anything to happen}

我要回帖

更多关于 labview读取串口数据 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信