基于多线程技术的串口通信的设计与研究
摘要 本文以vc++ 6.0为开发平台,讲述了如何使用32位的windows api 串口通信函数,编程实现高效的多线程全双工串口通信,并在阐述中给出了相关函数或代码。实验证明,该方法有着较好的灵活性、可靠性与高效性。
关键词 vc++;串口通信;多线程;重叠i/o
中图分类号tp313        文献标识码a        文章编号 1674-6708(2010)24-0219-02
0 引言
串口是常用的计算机与外部设备之间的数据传输通道,由于使用其通信方便易行,且能实现数据的长远距离传输,故应用极其广泛。为此,根据不同的使用环境灵活地编写出串口通信处理程序是必要的工作。在windows,微软专门提供了相应的文件i/o函数和通信函数,以方便我们编写出所需的串口通信程序。目前,实现串口通信的方法有两种。第一种,使用vc++提供的串口通信控件mscomm;第二种,使用32位的api 通信函数。本文采用了第二种方法,且结合多线程技术,实现了更加灵活的串口通信程序设计。
1 编程实现过程
1.1 初始化端口
windows操作系统中,串行口是被作为文件来进行处理的,而不是直接对端口进行操作,为此我们使用某一个串口进行通信时,需首先调用api函数 createfile(szport,generic_read|generic_write,0,null,open_existing,file_attribute_normal | file_flag_overlapped,null );获取一个串口通信设备句柄hcom。其中,该函数的第一个参数szport标识将要开启的端口号,第二个参数规定了端口读写属性,剩下的参数中较重要的是设置file_flag_overlapped标识,即开启重叠i/o的方式。在该模式下,当我们调用读写函数时,即使操作还未完成,被调用的函数也会立即返回,这样费时的i/o操作会在后台进行,使系统在这段时间可以去干别的事情,提高了系统的执行效率,且借用多线程技术还能实现读、写的同时进行。
接下来,需要根据需求,调用setcommtimeouts(hcom, &m_commtimeouts);函数,设置commtimeouts类型的结构体变量m_commtimeouts。更改commtimeouts结构体里面的成员变量,可以设置串口读写超时时间,以实现系统若未在指定时间内读出或写入指定数量的字符,就不再继续,立即返回执行下一次的任务。然后,还需要分别调用setcommstatesetupcomm
函数,设置端口的速率、数据位、输入输出缓冲区大小等配置信息。至此,就完成了串口的初始化并开启操作。
1.2 串口读写的实现
1.2.1 多线程全双工通信
想要编写高效率的串口通信程序,除了设置重叠i/o,再结合多线程技术,可以起到更好的效果。所谓多线程,就是指要计算机并行的处理不同的事情。在vc中线程分为用户界面线程和工作者线程,其主要区别是前者能够提供界面和用户的交互,而后者没有界面,用于处理后台任务。本文在设计通信程序时,建立了两个工作者线程waitforsingleobject函数,一个用于不断监视数据的接收,一个用于数据的发送。这样,使程序实现了收发同时进行的全双工工作方式,在实际应用中更有效率。
另外,值得注意的是使用重叠i/o需要创建overlapped结构以供读写函数,readfilewritefile使用。而overlapped结构中最重要的成员是heventhevent是一个事件对象句柄,通过createevent函数来创建,被用作线程的同步对象使用。如果读写函数未完成操作就返回,那么会将hevent置为无信号状态。只有操作完成后(包括超时),hevent才会置为有信号状态,这样我们就可以通过hevent知晓当前通讯设备的读写状态。
实际程序运行时,若发现读、写函数的返回值为假,由于开启了重叠i/o方式,那么未必是指读写失败,此时需要通过getlasterror函数的返回值做进一步判断。若返回为error_io_pending,说明当前读或写操作还未完成,这时就可以挂起读、写线程,等待操作完成。而程序设计时有两种等待方法:一种是用waitforsingleobject等待函数来等待overlapped结构体中的成员hevent为有信号状态;另一种办法是调用getoverlappedresult函数等待,注意需要将该函数的bwait参数设为true,那么该函数将会一直等待hevent 事件,直到其有信号才返回。同时,利用getoverlappedresult还可以返回一个overlapped结构体,里面包含有实际发送、接收字节等重叠操作的结果。
下面将分别给出读、写线程的关键代码,并作适当分析。
1.2.2 串口读操作
dword winapi readdata(lpvoid data)/////////////接收数据工作者线程
{ ctestdlg *dlg=(ctestdlg *)data;
byte *lpbuffer=null;
dword dwerror=0;
comstat comstat ;
dword dwlength; //存储输入缓冲区字节数
dword dw_length;//存储实际读到的字节数
clearcommerror(dlg->hcom, &dwerror, &comstat ) ;
dwlength = comstat.cbinque ; //获取输入缓冲区有多少字节数据
if (dwlength > 0) //有数据时开始读
{ lpbuffer = new byte[dwlength];//申请内存空间,用于存放读到的数据
bool freadstat;//读操作状态
freadstat = readfile( dlg->hcom, lpbuffer,dwlength, &dw_length, &dlg->m_overlappedread);
if (!freadstat)//状态为假时
{ if (getlasterror() == error_io_pending)//确认读操作是否还未完成
{ //挂起该线程,等待数据读完
getoverlappedresult(dlg->hcom, &dlg->m_overlappedread, &dw_length, true );
}
}
cstringstr_t,str_final;
for (dword i=0;igetdlgitem(idc_edit_receive)-    >setwindowtext(str_final);//以字符串形式显示接收的数据
delete []lpbuffer;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。