找回密码
 注册
搜索
查看: 1069|回复: 0

[讨论] 用VC 6.0实现串行通信的三种方法(二)

[复制链接]
发表于 2009-3-18 10:00:59 | 显示全部楼层 |阅读模式
方法二:在单线程中实现自定义的串口通信类
    控件简单易用,但由于必须拿到对话框中使用,在一些需要在线程中实现通信的应用场合,控件的使用显得捉襟见肘。此时,若能够按不同需要定制灵活的串口通信类将弥补控件的不足,以下将介绍如何在单线程中建立自定义的通信类。
    该通信类CSimpleComm需手动加入头文件与源文件,其基类为CObject,大致建立步骤如下:
    (1)打开串口,获取串口资源句柄
    通信程序从CreateFile处指定串口设备及相关的操作属性。再返回一个句柄,该句柄将被用于后续的通信操作,并贯穿整个通信过程。CreateFile()函数中有几个值得注意的参数设置:串口共享方式应设为0,串口为不可共享设备;创建方式必须为OPEN_EXISTING,即打开已有的串口。对于dwFlagAndAttribute参数,对串口有意义的值是FILE_FLAG_OVERLAPPED,该标志表明串口采用异步通信模式,可进行重叠操作;若值为NULL,则为同步通信方式,在同步方式下,应用程序将始终控制程序流,直到程序结束,若遭遇通信故障等因素,将导致应用程序的永久等待,所以一般多采用异步通信。
    (2)串口设置
    串口打开后,其属性被设置为默认值,根据具体需要,通过调用GetCommState(hComm,&dcb)读取当前串口设备控制块DCB(Device Control Block)设置,修改后通过SetCommState(hComm,&dcb)将其写入。再需注意异步读写的超时控制设置, 通过COMMTIMEOUTS结构设置超时,调用SetCommTimeouts(hComm,&timeouts)将结果写入。以下是温度监控程序中串口初始化成员函数:
BOOL  CSimpleComm::Open( )
     {
   DCB dcb;
m_hIDComDev=CreateFile( "COM2",
GENERIC_READ | GENERIC_WRITE,
0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_
NORMAL|FILE_FLAG_OVE    RLAPPED, NULL );        
//  打开串口,异步操作
if( m_hIDComDev == NULL ) return( FALSE );
dcb.DCBlength = sizeof( DCB );
GetCommState( m_hIDComDev, &dcb );  //  获得端口默认设置
dcb.BaudRate=CBR_4800;
dcb.ByteSize=8;
dcb.Parity= NOPARITY;
dcb.StopBits=(BYTE) ONESTOPBIT;
       ...... }
    (3)串口读写操作
    主要运用ReadFile()与WriteFile()API函数,若为异步通信方式,两函数中最后一个参数为指向OVERLAPPED结构的非空指针,在读写函数返回值为FALSE的情况下,调用GetLastError()函数,返回值为ERROR_IO_PENDING,表明I/O操作悬挂,即操作转入后台继续执行。此时,可以用WaitForSingleObject()来等待结束信号并设置最长等待时间,举例如下:
BOOL   bReadStatus;
    bReadStatus = ReadFile( m_hIDComDev, buffer,
dwBytesRead, &dwBytesRead,    &m_OverlappedRead );
    if(!bReadStatus)
  {
     if(GetLastError()==ERROR_IO_PENDING)
  {
       WaitForSingleObject(m_OverlappedRead.hEvent,1000);
   return ((int)dwBytesRead);
  }
  return(0);
  }
  return ((int)dwBytesRead);
    定义全局变量m_Serial作为新建通信类CSimpleComm的对象,通过调用类的成员函数即可实现所需串行通信功能。与方法一相比,方法二赋予串行通信程序设计较大的灵活性,端口的读写

    可选择较简单的查询式,或通过设置与外设数据发送时间间隔TimeCycle相同的定时器:SetTimer(1,TimeCycle,NULL),进行定时读取或发送。
    方法三 多线程下实现串行通信

    方法一,二适用于单线程通信。在很多工业控制系统中,常通过扩展串口连接多个外设,各外设发送数据的重复频率不同,要求后台实时无差错捕捉,采集,处理,记录各端口数据,这就需要在自定义的串行通信类中创建端口监视线程,以便在指定的事件发生时向相关的窗口发送通知消息。
    线程的基本概念可详见VC++参考书目,Windows内部的抢先调度程序在活动的线程之间分配CPU时间,Win 32 区分两种不同类型的线程,一种是用户界面线程UI(User Interface Thread),它包含消息循环或消息泵,用于处理接收到的消息;另一种是工作线程(Work Thread),它没有消息循环,用于执行后台任务。用于监视串口事件的线程即为工作线程。
    多线程通信类的编写在端口的配置,连接部分与单线程通信类相同,在端口配置完毕后,最重要的是根据实际情况,建立多线程之间的同步对象,如信号灯,临界区,事件等,相关细节可参考VC++ 中的同步类。
    一切就绪后即可启动工作线程:
CWinThrea *CommThread = AfxBegin
Thread(CommWatchThread,  // 线程函数名
(LPVOID) m_pTTYInfo,                       // 传递的参数
THREAD_PRIORITY_ABOVE_NORMAL,       // 设置线程优先级
  (UINT) 0,                                  //  最大堆栈大小
  (DWORD) CREATE_SUSPENDED ,           //  创建标志
(LPSECURITY_ATTRIBUTES) NULL);         //  安全性标志
    同时,在串口事件监视线程中:
if(WaitCommEvent(pTTYInfo->idComDev,&dwEvtMask,NULL))
  {
if((dwEvtMask  & pTTYInfo->dwEvtMask )== pTTYInfo->dwEvtMask)
{
  WaitForSingleObject(pTTYInfo->hPostEvent,0xFFFFFFFF);
      ResetEvent(pTTYInfo->hPostEvent);    // 置同步事件对象为非信号态
    ::PostMessage(CSampleView,ID_COM1_DATA,0,0);  // 发送通知消息
   }
  }
    用PostMessage()向指定窗口的消息队列发送通知消息,相应地,需要在该窗口建立消息与成员函数间的映射,用ON_MESSAGE将消息与成员函数名关联。
BEGIN_MESSAGE_MAP(CSampleView, CView)
//{{AFX_MSG_MAP(CSampleView)
ON_MESSAGE(ID_COM1_DATA, OnProcessCom1Data)  
ON_MESSAGE(ID_COM2_DATA, OnProcessCom2Data)  
.....
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
    然后在各成员函数中完成对各串口数据的接收处理,但必须保证在下一次监测到有数据到来之前,能够完成所有的中间处理工作。否则将造成数据的捕捉错误。
    多线程的实现可以使得各端口独立,准确地实现串行通信,使串口通信具有更广泛的灵活性与严格性,且充分利用了CPU时间。但在具体的实时监控系统中如何协调多个线程,线程之间以何种方式实现同步也是在多线程串行通信程序实现的难点。
    以VC++ 6.0 为工具,实现串行通信的三种方法各有利弊,根据不同需要,选择合适的方法,将达到事半功倍的效果。在温度监控系统中,笔者采用了方法二,在Window 98 ,Windows 95 上运行稳定,取得了良好的效果。
CSampleView:: OnTimer(UINT nIDEvent)
     {
       char  InputData[30];
       m_Serial.ReadData(InputData,30);
       // 数据处理
     }
    若对端口数据的响应时间要求较严格,可采用事件驱动I/O读写,Windows定义了9种串口通信事件,较常用的有:
    EV_RXCHAR: 接收到一个字节,并放入输入缓冲区。
    EV_TXEMPTY: 输出缓冲区中的最后一个字符发送出去。
    EV_RXFLAG: 接收到事件字符(DCB结构中EvtChar成员),放入输入缓冲区。
    在用SetCommMask()指定了有用的事件后,应用程序可调用WaitCommEvent()来等待事件的发生。SetCommMask(hComm,0)可使WaitCommEvent()中止。
高级模式
B Color Image Link Quote Code Smilies

本版积分规则

Archiver|手机版|小黑屋|52RD我爱研发网 ( 沪ICP备2022007804号-2 )

GMT+8, 2024-11-23 22:19 , Processed in 0.044665 second(s), 17 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表