分享

用Windows API 实现串行通信

 心不留意外尘 2016-06-05

http://blog.163.com/zhaojun_xf/blog/static/30050580200872465739835/

2008

刘树坤 刘金

  摘 要:介绍利用Windows API 在VC++ 5.0下实现微机串行通信程序的设计方法,并给出主要的通信程序代码。
  关键词:Windows API;串行通信;VC++
 

1 引言

  Windows 下的串行通信可有多种实现方法,如利用VB的MSCOMM.OCX控件,或利用Windows API等。在某些情况下,VB的MSCOMM.OCX控件不适合我们的要求,这时我们可以用Windows API 提供的通信函数来实现,以编写出可移植性强的串行通信程序。自Windows95以来,Windows 提供了大量的API,这些API几乎可以帮助我们快速、方便地实现Windows应用程序的各种功能。VC++是Windows应用程序开发的主流语言之一,它具有良好的图形设计界面并支持面向对象的程序设计方法。本文结合一个实例介绍VC++5.0下利用Windows API 的串行通信程序设计。

2 通信程序设计

  本文的实例是一台微机和多台单片机设备进行串行通信,通信协议为RS—485,微机和单片机组成一个总线式网络结构。由于采用这种总线式结构,为防止总线竞争(数据碰撞),只能由微机依次轮循总线上的各单片机设备来进行通信。
  对串行通信设备,Win32 API 可支持同步和异步两种I/O操作。同步操作方式程序设计相对比较简单,但I/O操作函数在I/O操作结束前不能返回,这将挂起调用线程,直到I/O操作结束。异步操作方式相对要复杂一些,但它可让耗时的I/O操作在后台进行,这样不会挂起调用线程,这在大数据量通信的情况下对改善调用线程的响应速度是相当有效的。异步操作方式特别适合同时对多个串口设备进行I/O操作和同时对一个串口设备进行读/写操作。两种操作方式的程序设计基本思想是相似的,考虑到本文实例的这种总线网络结构且通信数据量不大,将针对同步操作方式给出具体的通信程序设计,同时简单说明如何实现异步I/O操作。
2.1 串口设备的初始化
  通过CreateFile获得串口设备句柄并对其进行通信参数设置,包括设置输出/接收缓冲区大小、超时控制和事件监视等。
HANDLE hComDev=0;

//串行设备句柄;

BOOL bOpen=FALSE;

//串口打开标志;

HANDLE hEvent=0;

//线程同步事件句柄;

BOOL SetupSynCom()
{
 DCB dcb;
 COMMTIMEOUTS timeouts;
 if(bOpen) return FALSE;

//设备已打开;

 if((hComDev=CreateFile("COM1",GENERICREAD|GENERICWRITE,0,NULL,OPENEXISTING,FILEATTRIBUTENORMAL,NULL))==INVALIDHANDLEVALUE)

//打开COM1;

 return FALSE;
 //此处我们不使用超时控制,
 //将timeouts结构中各项的值赋成0;
 SetCommTimeouts(hComDev,&timeouts);

//设置超时控制;

 SetupComm(hComDev,1024,512);

//设置接收缓冲区和输出缓冲区的大小;

 GetCommState(hComDev,&dcb);

//获取缺省的dcb结构的值

 dcb.BaudRate=CBR9600;

//波特率为9600 bps;

 dcb.fParity=NOPARITY;

//无奇偶校验;

 dcb.ByteSize=8;

//数据位为8;

 dcb.StopBits=ONESTOPBIT;

//一个停止位;

 SetCommMask(hComDev,EVERR|EVRXCHAR);

//监视串口的错误和接收到字符两种事件;

 SetCommState(hComDev,&dcb);

//设置串口设备控制参数;

 bOpen=TRUE;

//设备已打开;

 hEvent=CreateEvent(NULL,FALSE,FALSE,"WatchEvent");

//创建人工重设、未发信号的事件;

 AfxBeginThread(CommWatchProc,pParam);

//创建一个事件监视线程来监视串口事件;

}
  在设置串口的DCB结构的参数时,我们没有必要设置每一个的值,首先读出DCB缺省的参数设置,然后只修改我们需要的参数,其它都取缺省值。由于我们对串口进行的同步I/O操作,除非指定进行监测的事件发生,否则WaitCommEvent函数不会返回,所以在串口设备初始化的最后,建立一个单独的监视线程来监视串口的事件,以免挂起当前调用线程,其中pParam可以是一个对事件进行处理的窗口类指针。
  如果要进行异步I/O操作,打开设备句柄时,CreateFile的第六个参数应增加FILEFLAGOVERLAPPED 标志。
2.2 数据发送
  我们用WriteFile来进行数据发送,对于同步I/O操作,它的最后一个参数可为NULL,而对异步I/O操作,它的最后一个参数必须是一个指向OVERLAPPED结构的指针,通过这个OVERLAPPED结构来获得当前操作状态。
BOOL WriteComm(LPCVOID lpSndBuffer,DWORD dwBytesToWrite)
{ //lpSndBuffer为发送数据缓冲区指针,dwBytesToWrite为
 将发送的字节长度;
 BOOL bWriteState;

//发送结果;

 DWORD dwBytesWritten;

//实际发送的字节数;

 if(!bOpen) return FALSE;

//设备未打开;

 bWriteState=WriteFile(hComDev,lpSndBuffer,dwBytesToWrite,&dwBytesWritten,NULL);
 if(!bWriteState || dwBytesToWrite!=dwBytesWritten)
  return FALSE;

//发送失败;

 else
  return TRUE;

//发送成功;

}
2.3 数据接收
  用ReadFile来从串口接收缓冲区读取数据,数据读取前,用ClearCommError函数获得接收缓冲区中的字节数。至于同步和异步读取的差别,同发送数据是一样的。
DWORD ReadComm(LPVOID lpInBuffer,DWORD dwBytesToRead)
{ //lpInBuffer为接收数据的缓冲区指针, dwBytesToRead为准备读取的数据长度(字节数);
 COMSTAT ComStat;

//串口设备状态结构;

 DWORD dwBytesRead,dwErrorFlags;
 if(!bOpen) return 0;

//设备未打开;

 ClearCommError(hComDev,&dwErrorFlags,&ComStat);

//读取串口设备的当前状态;

 dwBytesRead=min(dwBytesToRead,ComStat.cbInQue);

//应该读取的数据长度;

 if(dwBytesRead>0)
  if(!ReadFile(hComDev,lpInBuffer,dwBytesRead,&dwBytesRead,NULL))

//读取数据;

    dwBytesRead=0;
 return dwBytesRead;
}
2.4 事件监视线程
  事件监视线程对串口事件进行监视,当监视的事件发生时,监视线程可将这个事件发送(SendMessage)或张贴(PostMessage)到对事件进行处理的窗口类(由pParam指定)中去。
UINT CommWatchProc(LPVOID pParam)
{ DWORD dwEventMask=0;

//发生的事件;

 while(bOpen)
 { WaitCommEvent(hComDev, &dwEventMask, NULL);

//等待监视的事件发生;

  if ((dwEventMask & EVRXCHAR) == EVRXCHAR)
   //接收到字符事件,可以将此消息张贴到由pParam有指定的窗口类中进行处理;
  if(dwEventMask & EVERR)==EVERROR)

//发生错误事件,处理;

 }
 SetEvent(hEvent);

//发信号,指示监视线程结束;

 return 0;
}
2.5 关闭串口设备
  在整个应用程序结束时或不再使用串口设备时,应将串口设备关闭,包括取消事件监视,将设备打开标志bOpen置为FALSE以使事件监视线程结束,清除发送/接收缓冲区和关闭设备句柄。
void CloseSynComm()
{ if(!bOpen) return;
 bOpen=FALSE;

//结束事件监视线程;

 SetCommMask(hComDev,0);

//取消事件监视,此时监视线程中的WaitCommEvent将返回;

 WaitForSingleObject(hEvent,INFINITE);

//等待监视线程结束;

 CloseHandle(hEvent);

//关闭事件句柄;

 //停止发送和接收数据,并清除发送和接收缓冲区;
 PurgeComm(hComDev,PURGETXABORT|PURGERXABORT|PURGETXCLEAR|PURGERXCLEAR);
 CloseHandle(hComDev);

//关闭设备句柄;

}

3 结束语

  以上给出了用Windows API 设计串行通信的基本思想,对这个同步I/O操作的串行通信程序稍加改造就可进行异步I/O操作。在实际应用中,我们可以将这些串行通信函数和成员变量加到一个现成的CWnd类或其派生类中来实现串行通信,也可设计一个新的串行通信类来包含这些成员函数和成员变量。总之,利用WIN 3.2 API可以设计出灵活、能满足各种要求的串行通信程序。

作者简介:刘树坤 从事计算机网络通信和工业控制、检测的研究开发工作。
作者单位:刘树坤(西南计算中心 四川.绵阳621900)
     刘金(西南计算中心 四川.绵阳621900)

参考文献

[1] (美)Stefano Maruzzi.The Microsoft Windows 95开发人员指南[M].周靖,等译.北京:机械工业出版社,1997.
[2] 史惠康.VISUAL C++5.0实用编程技术[M]. 中国水利水电出版社,1998.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多