异步重叠操作之串口

1 篇文章 0 订阅
1 篇文章 0 订阅
在工控行业,目前总线型通信依然占据半壁江山。常用的如 485 CAN 等。而面对小型系统时,尤其监控区域遍布在两公里范围内时,通过 485-232 或者 CAN-232 转换模块,使用上位机的串口与下属硬件通信组成系统便成了相对节约的一种设计方案。

         本文的重点在于讲解异步重叠操作串口的思想以及过程。

         所有的IO设备的操作都遵循:创建、参数设置、读写、关闭三个步骤。        

      知识补充:

     此文中需要使用到的几个结构体:

 1 typedef  struct _COMSTAT {
 2     DWORD fCtsHold :  1;
 3     DWORD fDsrHold :  1;
 4     DWORD fRlsdHold :  1;
 5     DWORD fXoffHold :  1;
 6     DWORD fXoffSent :  1;
 7     DWORD fEof :  1;
 8     DWORD fTxim :  1;
 9     DWORD fReserved :  25;
10     DWORD cbInQue;     // 输入缓冲区的字节数
11      DWORD cbOutQue;    // 输出缓冲区的字节数
12  } COMSTAT, *LPCOMSTAT;

 

typedef  struct  _COMMTIMEOUTS {
    DWORD ReadIntervalTimeout;           /*  Maximum time between read chars.  */
    DWORD ReadTotalTimeoutMultiplier;    /*  Multiplier of characters.         */
    DWORD ReadTotalTimeoutConstant;      /*  Constant in milliseconds.         */
    DWORD WriteTotalTimeoutMultiplier;   /*  Multiplier of characters.         */
    DWORD WriteTotalTimeoutConstant;     /*  Constant in milliseconds.         */
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

typedef  struct  _DCB {
    DWORD DCBlength;       /*  sizeof(DCB)                      */
    DWORD BaudRate;        /*  Baudrate at which running        */
    DWORD fBinary:  1 ;      /*  Binary Mode (skip EOF check)     */
    DWORD fParity:  1 ;      /*  Enable parity checking           */
    DWORD fOutxCtsFlow: 1 /*  CTS handshaking on output        */
    DWORD fOutxDsrFlow: 1 /*  DSR handshaking on output        */
    DWORD fDtrControl: 2 ;   /*  DTR Flow control                 */
    DWORD fDsrSensitivity: 1 /*  DSR Sensitivity               */
    DWORD fTXContinueOnXoff:  1 /*  Continue TX when Xoff sent  */
    DWORD fOutX:  1 ;        /*  Enable output X-ON/X-OFF         */
    DWORD fInX:  1 ;         /*  Enable input X-ON/X-OFF          */
    DWORD fErrorChar:  1 ;   /*  Enable Err Replacement           */
    DWORD fNull:  1 ;        /*  Enable Null stripping            */
    DWORD fRtsControl: 2 ;   /*  Rts Flow control                 */
    DWORD fAbortOnError: 1 /*  Abort all reads and writes on Error  */
    DWORD fDummy2: 17 ;      /*  Reserved                         */
    WORD wReserved;        /*  Not currently used               */
    WORD XonLim;           /*  Transmit X-ON threshold          */
    WORD XoffLim;          /*  Transmit X-OFF threshold         */
    BYTE ByteSize;         /*  Number of bits/byte, 4-8         */
    BYTE Parity;           /*  0-4=None,Odd,Even,Mark,Space     */
    BYTE StopBits;         /*  0,1,2 = 1, 1.5, 2                */
     char  XonChar;          /*  Tx and Rx X-ON character         */
     char  XoffChar;         /*  Tx and Rx X-OFF character        */
     char  ErrorChar;        /*  Error replacement char           */
     char  EofChar;          /*  End of Input character           */
     char  EvtChar;          /*  Received Event character         */
    WORD wReserved1;       /*  Fill for now.                    */
} DCB, *LPDCB;

      首先介绍此文中需要用到的各个API函数。

   函数名                    作用
CreateFile                打开串口
GetCommState              检测串口设置
SetCommState              设置串口
GetCommTimeouts           检测通信超时设置
SetCommTimeouts           设置通信超时参数
SetCommMask               设定被监控事件
WaitCommEvent             等待被监控事件发生
WaitForMultipleObjects    等待多个被监测对象的结果
WriteFile                 发送数据
ReadFile                  接收数据
GetOverlappedResult       返回最后重叠(异步)操作结果
PurgeComm                 清空串口缓冲区,退出所有相关操作
ClearCommError            更新串口状态结构体,并清除所有串口硬件错误
CloseHandle               关闭串行口

 

 CreateFile                打开串口

inline HANDLE CAtlTransactionManager::CreateFile(
    _In_z_ LPCTSTR lpFileName,    //  文件名
    _In_ DWORD dwDesiredAccess,   //  访问模式(GENERIC_WRITE/GENERIC_READ)。0表示仅允许获得与一个设备有关的信息
    _In_ DWORD dwShareMode,       // 共享模式(FILE_SHARE_READ/FILE_SHARE_WRITE)。0表述独占
    _In_opt_ LPSECURITY_ATTRIBUTES  lpSecurityAttributes,                 // 指向安全属性的指针
    _In_ DWORD dwCreationDisposition, // 创建属性 CREATE_NEW 创建如果已经存在则冲突 CREATE_ALWAYS 总是创建,存在则改写 OPEN_EXISTING 打开现有,由设备提出要求 OPEN_ALWAYS 打开,不存在则创建 TRUNCATE_EXISTING 将现有文件缩短为0长度
    _In_ DWORD dwFlagsAndAttributes, 文件属性
    _In_opt_ HANDLE hTemplateFile  //  用于复制文件句柄,文件模板。如果不为零,则新文件将从这个文件中复制扩展属性
);

 GetCommState              检测串口设置

 

WINBASEAPI
BOOL
WINAPI
GetCommState(
    __in  HANDLE hFile,       // 句柄
    __out LPDCB lpDCB         // 结构体,存储获得的信息
    );

 SetCommState              设置串口

 

WINBASEAPI
BOOL
WINAPI
SetCommState(
    __in HANDLE hFile,      // 文件句柄
    __in LPDCB lpDCB        // DCP结构体指针
    );

 GetCommTimeouts           检测通信超时设置

 

WINBASEAPI
BOOL
WINAPI
GetCommTimeouts(
    __in  HANDLE hFile,               // 文件句柄
    __out LPCOMMTIMEOUTS lpCommTimeouts  // 时间信息结构体指针
    );

 SetCommTimeouts           设置通信超时参数

 

WINBASEAPI
BOOL
WINAPI
SetCommTimeouts(
    __in HANDLE hFile,                 //  文件句柄
    __in LPCOMMTIMEOUTS lpCommTimeouts  //  时间信息结构体指针
    );

 

SetCommMask               设定被监控事件

 

WINBASEAPI
BOOL
WINAPI
SetCommMask(
    __in HANDLE hFile,     // 文件句柄
    __in DWORD dwEvtMask   // 监听事件类型 EV_BREAK 输入中断 EV_CTS CTS清除发送信号改变 EV_DSR 数据准备就绪信号改变  EV_ERR 总线信号发生错误 EV_RING 检测到令牌 EV_RLSD 探测到接收信号 EV_RXCHAR 有字符到达缓冲 EV_RXFLAG 有字符到达接收区,且该字符为DCB结构,用于指定串口特性 EV_TXEMPTY 最后一个字符从发送去发送完完毕
    );

 SetupComm              初始化通信设备参数

WINBASEAPI
BOOL
WINAPI
SetupComm(
    __in HANDLE hFile,     // 文件句柄
    __in DWORD dwInQueue,  // 输入缓冲区大小
    __in DWORD dwOutQueue  // 输出缓冲区大小
    );

 

WaitCommEvent             等待被监控事件发生   用来判断用SetCommMask设定的事件是否发生

 

WINBASEAPI
BOOL
WINAPI
WaitCommEvent(
    __in        HANDLE hFile,      // 文件句柄
    __inout     LPDWORD lpEvtMask,  // 监听的事件
    __inout_opt LPOVERLAPPED lpOverlapped  // 重叠结构体指针
    );  

 编码:

一、头文件

#include  " Windows.h "

 

二、 定义变量:

1  bool                m_bSendPending;         //  发送阻塞标志位
2  bool                m_bRecvPending;         //  读取阻塞标志位
3  OVERLAPPED            SendOverLapped;         //  发送OverLapped结构体
4  OVERLAPPED            RecvOverLapped;         //  接收overLapped结构体
5  HANDLE                m_hPort;             //  串口句柄


三、方法体

1. 打开串口

 1  //  创建端口
 2  //  参数:
 3  //      __in BSTR comName 通道名称
 4  //  返回:
 5  //      BOOL TRUE 函数执行成功 FALSE 函数执行失败
 6  BOOL CreatePort(BSTR comName)
 7 {
 8     m_hPort = CreateFile(comName, GENERIC_READ | GENERIC_WRITE,  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
 9      if (m_hPort == INVALID_HANDLE_VALUE)
10     {
11         CloseHandle(m_hPort);
12          return FALSE;
13     }
14      else
15          return  true;
16 }

 2.设置串口参数

 1  //  设置参数
 2  void SetParam()
 3 {
 4     DCB     dcb;
 5     GetCommState(m_hPort, &dcb);                             //  读取当前端口的DCB设置
 6      dcb.BaudRate = CBR_9600;
 7     dcb.ByteSize =  8;                                         //  库中的选择结果是5-8,表示5-8位长
 8      dcb.Parity = NOPARITY;
 9     dcb.StopBits = ONESTOPBIT;                                //  该参数如果开放,则设置不成功
10      SetCommState(m_hPort, &dcb);                             //  设置当前端口的DCB设置
11 
12     COMMTIMEOUTS comTimeouts;
13     GetCommTimeouts(m_hPort, &comTimeouts);
14     comTimeouts.ReadIntervalTimeout = MAXDWORD;                
15     comTimeouts.ReadTotalTimeoutMultiplier = MAXDWORD;                        
16     comTimeouts.ReadTotalTimeoutConstant =  0x0014;
17     comTimeouts.WriteTotalTimeoutMultiplier = MAXDWORD;
18     comTimeouts.WriteTotalTimeoutConstant =  0x0014;
19 
20      // WaitCommEvent             
21      SetCommMask(m_hPort, EV_RXCHAR | EV_TXEMPTY);
22     SetupComm(m_hPort,  0x04000x0400);
23     PurgeComm(m_hPort, PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_RXCLEAR);
24 }

 3. 发送数据

 1  //  发送数据
 2  //  参数:
 3  //      __BYTE* szBuffer 发送字节流
 4  //      __DWORD iExpectCount 期望发送字节数
 5  //  返回: 
 6  //      int 上次操作实际发送字节数 
 7  int WriteChar(BYTE* szBuffer,DWORD iExpectCount)
 8 {
 9     COMSTAT    ComStat;
10     DWORD    dwErrorFlag;
11     DWORD    dwWrittenCnt =  0;
12     BOOL    bRet;
13 
14      if (m_bSendPending)
15     {
16         bRet = GetOverlappedResult(m_hPort, &SendOverLapped, &dwWrittenCnt, FALSE);
17          if(bRet)
18         {
19             m_bSendPending = FALSE;
20         }
21          else
22         {
23              if (GetLastError() == ERROR_IO_INCOMPLETE)
24                  return  0;
25              else
26             {
27                 bRet = ClearCommError(m_hPort, &dwErrorFlag, &ComStat);
28                 m_bSendPending = FALSE;
29             }
30         }
31     }
32 
33      if (m_bSendPending == FALSE)
34     {
35         bRet = WriteFile(m_hPort, szBuffer, iExpectCount, &dwWrittenCnt, &SendOverLapped);  // 读字节写入缓存
36           if (bRet == FALSE)
37         {        
38              if (GetLastError() == ERROR_IO_PENDING)
39                 m_bSendPending = TRUE;
40              else
41                 bRet = ClearCommError(m_hPort, &dwErrorFlag, &ComStat);
42 
43              return  0;
44         }
45     }
46 
47      return (INT32)dwWrittenCnt;
48 }

 4.接收数据

 1  //  接收数据
 2  //  参数:
 3  //      __BYTE* szBuffer 接收数据寄存器
 4  //      __DWORD& iSize 接收寄存器的容量
 5  //  返回:
 6  //      int 返回实际接收字节数
 7  int ReceiveChar(BYTE* szBuffer,DWORD& iSize )
 8 {
 9     COMSTAT    ComStat;
10     DWORD    dwReadCnt =  0;
11     DWORD   dwErrorFlag;
12     BOOL    bRet;
13 
14      if (m_bRecvPending)
15     {
16         bRet = GetOverlappedResult(m_hPort, &RecvOverLapped, &dwReadCnt, FALSE);
17          if (bRet)
18         {
19             m_bRecvPending = FALSE;
20         }
21          else
22         {
23              if (GetLastError() == ERROR_IO_INCOMPLETE)
24                  return  0;
25              else
26             {
27                 bRet = ClearCommError(m_hPort, &dwErrorFlag, &ComStat);
28                 m_bRecvPending = FALSE;
29             }
30         }
31     }
32      if (m_bRecvPending == FALSE)
33     {
34         bRet = ReadFile(m_hPort, szBuffer, iSize, &dwReadCnt, &RecvOverLapped);  // 读字节写入缓存
35           if (bRet == FALSE)
36         {
37              if (GetLastError() == ERROR_IO_PENDING)
38                 m_bRecvPending = TRUE;
39              else
40                 bRet = ClearCommError(m_hPort, &dwErrorFlag, &ComStat);
41 
42              return  0;
43         }
44     }
45 
46      return (INT32)dwReadCnt;
47 }

 5.  关闭串口,释放资源

 1  void Close()
 2 {
 3 
 4      if (m_hPort != INVALID_HANDLE_VALUE)
 5     {
 6         CloseHandle(hPort);
 7         m_hPort = INVALID_HANDLE_VALUE;
 8     }
 9 
10      if (SendOverLapped.hEvent != INVALID_HANDLE_VALUE)
11     {
12         CloseHandle(SendOverLapped.hEvent);
13         SendOverLapped.hEvent = INVALID_HANDLE_VALUE;
14     }
15 
16      if (RecvOverLapped.hEvent != INVALID_HANDLE_VALUE)
17     {
18         CloseHandle(RecvOverLapped.hEvent);
19         RecvOverLapped.hEvent = INVALID_HANDLE_VALUE;
20     }
21 }

 

 备注:

      本文中相对改革点为发送与接收数据,均采用了辅助标志位isPending,从而每次首先检查是否已经投递异步重叠操作请求,如果已经请求则直接使用

GetOverlappedResult同步等待操作完成,获取操作结果之后再次投递异步重叠的请求,以下次获取操作结果。

      该种思路直接实现了单独两个线程实现首发的性能效果,而且避免了线程的空间资源消耗以及线程之间切换的时间性能消耗。


 

       欢迎大家评价,对文章不足之处,希望大家能给以补充


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值