当前位置: >>
使用Win32API实现Windows下异步串口通讯
使用 Win32API 实现 Windows 下异步串口通讯关键词: 关键词 Win32API 串口通讯下异步串口通讯 通讯( 使用 Win32API 实现 Windows 下异步串口通讯(上)- -目录: 1. 异步非阻塞串口通讯的优点 2. 异步非阻塞串口通讯的基本原理 3. 异步非阻塞串口通讯的基础知识 4. 异步非阻塞串口通讯的实现步骤
一,异步非阻塞串口通讯的优点 读写串行口时,既可以同步执行,也可以重叠(异步)执行. 在同步执行时,函数直到操作完成后才返回.这意味着在同步执行时线程会被阻塞,从而导致效率下降. 在重叠执行时,即使操作还未完成,调用的函数也会立即返回.费时的 I/O 操作在后台进行,这样线程就 可以干别的事情. 例如,线程可以在不同的句柄上同时执行 I/O 操作,甚至可以在同一句柄上同时进行读写操作.&重叠&一词 的含义就在于此. 二,异步非阻塞串口通讯的基本原理 首先,确定要打开的串口名,波特率,奇偶校验方式,数据位,停止位,传递给 CreateFile()函数打开特定 串口; 其次,为了保护系统对串口的初始设置,调用 GetCommTimeouts()得到串口的原始超时设置; 然后, 初始化 DCB 对象, 调用 SetCommState() 设置 DCB, 调用 SetCommTimeouts()设置串口超时控制; 再次,调用 SetupComm()设置串口接收发送数据的缓冲区大小,串口的设置就基本完成,之后就可以启动 读写线程了. 三,异步非阻塞串口通讯的基础知识 下面来介绍并举例说明一下编写异步非阻塞串口通讯的程序中将会使用到的几个关键函数 CreateFile() 功能:打开串口设备 函数原型 HANDLE CreateFile( LPCTSTR lpFileName, // 串口名称字符串;如: &COM1& 或 &COM2& DWORD dwDesiredAccess, // 设置读写属性(访问模式 );一般为 GENERIC_READ|GENERIC_WRI TE, DWORD dwShareMode, // 共享模式;&必须&为 0, 即不能共享 LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全属性;一般为 NULL DWORD dwCreationDistribution, // 创建方式,串口设置必须设置此值; 在这里&必须&为 OPEN_EXIST ING DWORD dwFlagsAndAttributes, // 文件属性和标志;在这里我们设置成 FILE_FLAG_OVERLAPPED ,实现异步 I/O HANDLE hTemplateFile // 临时文件的句柄,通常为 NULL ); 说明: 如果调用成功,那么该函数返回文件的句柄,如果调用失败,则函数返回 INVALID_HANDLE_VALUE. Forexample: Handle m_hComm = CreateFile(com1,GENERIC_READ||GENERIC_WRITE,0,NULL,OPEN_EXISTIN G,FILE_FLAG_OVERLAPPED,0); CloseHandle(); 功能:关闭串口 BOOL CloseHandle( HANDLE hObject // handle to object to close ) 这个,我想就不多说了吧! GetCommState() () 功能:获得串口状态 BOOL GetCommState( HANDLE hFile, // handle of communications device LPDCB lpDCB // address of device-control block structure ); SetCommState() 功能:设置串口状态 BOOL SetCommState( HANDLE hFile, // handle of communications device LPDCB lpDCB // address of device-control block structure ); 说明: 在打开通信设备句柄后,常常需要对串行口进行一些初始化工作.这需要通过一个 DCB 结构来进行.DC B 结构包含了诸如波特率,每个字符的数据位数,奇偶校验和停止位数等信息.在查询或配置置串行口的 属性时,都要用 DCB 结构来作为缓冲区. 调用 GetCommState 函数可以获得串口的配置,该函数把当前配置填充到一个 DCB 结构中.一般在用 Cr eateFile 打开串行口后,可以调用 GetCommState 函数来获取串行口的初始配置.要修改串行口的配置, 应该先修改 DCB 结构,然后再调用 SetCommState 函数用指定的 DCB 结构来设置串行口 Forexample: DCB memset(&dec,0,dizeof(dcb)); if(!GetCommState(HComm,&dcb))//获取当前 DCB 配置 return FALSE; dcb.BaudRate = CBR_9600;//修改数据传输率 ............if(SetCommState(hComm,&dcb))//设置新参数 ...... //错误处理BuildCommDCB() 功能:初始化 DCB 结构 BOOL BuildCommDCB( LPCTSTR lpDef, // pointer to device-control string LPDCB lpDCB // pointer to device-control block ); Forexample: DCB memset(&dcb,0,sizeof(dcb)); dcb.DCBlength = sizeof(dcb); if(!BuildCommDCb(&9600,n,8,1&,&dcb))//&baud=9600 parity=N data=8 stop=1& { ...... //参数修改错误 return FALSE; } else { ...... //己准备就绪 } 说明:功能同上面的例子. SetupComm() 功能:设置 I/O 缓冲区的大小 函数原型: BOOL SetupComm( HANDLE hFile, // handle to communications deviceDWORD dwInQueue, // size of input buffer DWORD dwOutQueue // size of output buffer ); 说明: 除了在 DCB 中的设置外, 程序一般还需要设置 I/O 缓冲区的大小和超时. Windows 用 I/O 缓冲区来暂存串 行口输入和输出的数据,如果通信的速率较高,则应该设置较大的缓冲区.调用 SetupComm 函数可以设 置串行口的输入和输出缓冲区的大小. 先介绍一个结构: 先介绍一个结构:COMMTIMEOUTS typedef struct _COMMTIMEOUTS { DWORD ReadIntervalT // 读间隔超时 DWORD ReadTotalTimeoutM // 读时间系数 DWORD ReadTotalTimeoutC // 读时间常量 DWORD WriteTotalTimeoutM // 写时间系数DWORD WriteTotalTimeoutC // 写时间常量 } COMMTIMEOUTS,*LPCOMMTIMEOUTS; 再介绍两个函数 GetCommTimeouts 功能:读取 TimeOut 的值 函数原型: BOOL GetCommTimeouts( HANDLE hFile, // handle of communications device LPCOMMTIMEOUTS lpCommTimeouts // address of comm. time-outs structure ); SetCommTimeouts 功能:设置 TimeOUt 的值 函数原型: BOOL SetCommTimeouts( HANDLE hFile, // handle of communications device LPCOMMTIMEOUTS lpCommTimeouts // address of communications time-out structure ); 这里顺便介绍一下 TimeOut 机制的两个性质: 超时函数 说明: 在用 ReadFile 和 WriteFile 读写串行口时,需要考虑超时问题.如果在指定的时间内没有读出或写入指定 数量的字符,那么 ReadFile 或 WriteFile 的操作就会结束.要查询当前的超时设置应调用 GetCommTime outs 函数,该函数会填充一个 COMMTIMEOUTS 结构.调用 SetCommTimeouts 可以用某一个 COMMTI MEOUTS 结构的内容来设置超时. 有两种超时:间隔超时和总超时.间隔超时是指在接收时两个字符之间的最大时延,总超时是指读写 操作总共花费的最大时间.写操作只支持总超时,而读操作两种超时均支持.用 COMMTIMEOUTS 结构 可以规定读/写操作的超时,该结构的定义为: COMMTIMEOUTS 结构的成员都以毫秒为单位.总超时的 计算公式是: 总超时=时间系数×要求读/写的字符数 + 时间常量 例如,如果要读入 10 个字符,那么读操作的总超时的计算公式为: 读总超时=ReadTotalTimeoutMultiplier×10 + ReadTotalTimeoutConstant 可以看出,间隔超时和总超时的设置是不相关的,这可以方便通信程序灵活地设置各种超时. 如果所有写超时参数均为 0,那么就不使用写超时.如果 ReadIntervalTimeout 为 0,那么就不使用读 间隔超时,如果 ReadTotalTimeoutMultiplier 和 ReadTotalTimeoutConstant 都为 0,则不使用读总超时. 如果读间隔超时被设置成 MAXDWORD 并且两个读总超时为 0, 那么在读一次输入缓冲区中的内容后读操 作就立即完成,而不管是否读入了要求的字符.在用重叠方式读写串行口时,虽然 ReadFile 和 WriteFile 在完成操作以前就可能返回,但超时仍然是 起作用的.在这种情况下,超时规定的是操作的完成时间,而不是 ReadFile 和 WriteFile 的返回时间. Forexample: COMMTIMEOUTS timeO memset(&&timeOver.0.sizeof(timeOver)); DWORDtimeMultiplier,timeC timeOver.ReadTotalTimeoutMultiplier=timeM timeOver.ReadTotalTimeoutConstant=timeC SetCommTimeouts(hComport,&&timeOver); ReadFile() () 功能:读取数据 函数原型: BOOL ReadFile( HANDLE hFile, // 串口名称字符串(文件句柄 ) LPVOID lpBuffer, // 读缓冲区 DWORD nNumberOfBytesToRead, // 要求读入的字节数 LPDWORD lpNumberOfBytesRead, // 实际读入的字节数 LPOVERLAPPED lpOverlapped // 指向一个 OVERLAPPED 结构 ); //若返回 TRUE 则表明操作成功 Forexample: char *pReciveB DWORD nWantRead = 100, nReadR LPOVERLAPPED m_OverlappedR BOOL bReadStatus = ReadFile( m_hComm, preciveBuf,nWantRead, &&nReadRead, &&m_Overlap pedRead ); WriteFile() () 功能:来将资料写入 Serial port. 函数原型: BOOL WriteFile( HANDLE hFile, // handle to file to write to LPCVOID lpBuffer, // pointer to data to write to file DWORD nNumberOfBytesToWrite, // number of bytes to write LPDWORD lpNumberOfBytesWritten, // pointer to number of bytes written LPOVERLAPPED lpOverlapped // pointer to structure needed for overlapped I/O ); 说明: ReadFile 函数只要在串行口输入缓冲区中读入指定数量的字符,就算完成操作. 而 WriteFile 函数不但要把指定数量的字符拷入到输出缓冲中, 而且要等这些字符从串行口送出去后才算完 成操作.当 ReadFile 和 WriteFile 返回 FALSE 时,不一定就是操作失败,线程应该调用 GetLastError 函数分析返 回的结果.例如,在重叠操作时如果操作还未完成函数就返回,那么函数就返回 FALSE,而且 GetLastEr ror 函数返回 ERROR_IO_PENDING. 如果 GetLastError 函数返回 ERROR_IO_PENDING,则说明重叠操作还为完成,线程可以等待操作完成. 有两种等待办法:一种办法是用象 WaitForSingleObject 这样的等待函数来等待 OVERLAPPED 结构的 h Event 成员, 可以规定等待的时间,在等待函数返回后,调用 GetOverlappedResult. 另一种办法是调用 GetOverlappedResult 函数等待,如果指定该函数的 bWait 参数为 TRUE, 那么该函数将等待 OVERLAPPED 结构的 hEvent 事件. GetOverlappedResult 可以返回一个 OVERLAPPED 结构来报告包括实际传输字节在内的重叠操作结果. 如果规定了读/写操作的超时, 那么当超过规定时间后, hEvent 成员会变成有信号的. 因此, 在超时发生后, WaitForSingleObject 和 GetOverlappedResult 都会结束等待. WaitForSingleObject 的 dwMilliseconds 参 数会规定一个等待超时,该函数实际等待的时间是两个超时的最小值.注意 GetOverlappedResult 不能设 置等待的时限,因此如果 hEvent 成员无信号,则该函数将一直等待下去 ClearCommError() () 功能: 从字面上的意思看来, 它是用来清除错误情况用的, 但是实际上它还可以拿来取得目前通讯设备的 一些信息. 函数原型: BOOL ClearCommError( HANDLE hFile, // handle to communications device LPDWORD lpErrors, // pointer to variable to receive error codes LPCOMSTAT lpStat // pointer to buffer for communications status ); 说明: 在调用 ReadFile 和 WriteFile 之前,线程应该调用 ClearCommError 函数清除错误标志. 该函数负责报告指定的错误和设备的当前状态.PurgeComm() () 功能:终止目前正在进行的读或写的动作 函数原型: BOOL PurgeComm( HANDLE hFile, // handle of communications resource DWORD dwFlags // action to perform ); 参数说明: HANDLE hFile,//串口名称字符串 dwFlags 共有四种 flags: PURGE_TXABORT: 终止目前正在进行的(背景)写入动作 PURGE_RXABORT: 终正目前正在进行的(背景)读取动作 PURGE_TXCLEAR: flush 写入的 bufferPURGE_TXCLEAR: flush 读取的 buffer 调用 PurgeComm 函数可以终止正在进行的读写操作,该函数还会清除输入或输出缓冲区中的内容. GetCommMask() () 功能:得到设置的通信事件的掩码 函数原型: BOOL GetCommMask( HANDLE hFile, // handle of communications device LPDWORD lpEvtMask // address of variable to get event mask ); SetCommMask() () 功能:设置想要得到的通信事件的掩码 函数原型: BOOL SetCommMask( HANDLE hFile, // handle of communications device DWORD dwEvtMask // mask that identifies enabled events ); 说明: 可设置的通信事件标志(即 SetCommMask()函数所设置的掩码) 可以有 EV_BREAK,EV_CTS,EV_DSR, EV_ERR,EV_RING,EV_RLSD,EV_RXCHAR,EV_RX FLAG,EV_TXEMPTY. 注:若对端口数据的响应时间要求较严格,可采用事件驱动 I/O 读写, Windows 定义了 9 种串口通信事件, 较常用的有: EV_RXCHAR: 接收到一个字节,并放入输入缓冲区. EV_TXEMPTY: 输出缓冲区中的最后一个字符发送出去. EV_RXFLAG: 接收到事件字符(DCB 结构中 EvtChar 成员),放入输入缓冲区. 下面是 MSDN 上的解释: EV_BREAK A break was detected on input. EV_CTS The CTS (clear-to-send) signal changed state. EV_DSR The DSR (data-set-ready) signal changed state. EV_ERR A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE _RXPARITY. EV_RING A ring indicator was detected. EV_RLSD The RLSD (receive-line-signal-detect) signal changed state. EV_RXCHAR A character was received and placed in the input buffer. EV_RXFLAG The event character was received and placed in the input buffer. The event charact er is specified in the device's DCB structure, which is applied to a serial port by using the SetC ommState function. EV_TXEMPTY The last character in the output buffer was sent.WaitCommEvent() 功能:等待设定的通讯事件的发生 函数原型: BOOL WaitCommEvent( HANDLE hFile, // handle of communications device LPDWORD lpEvtMask, // address of variable for event that occurred LPOVERLAPPED lpOverlapped, // address of overlapped structure ); 说明: WaitCommEvent() 会一直 block(阻塞) 到你所设定的通讯事件发生为止. 所以当 WaitCommEvent() 返回时, 你可以由 lpEvtMask 取得究竟是那一事件发生, 再来决定要如何 处理. WaitForSingleObject() 功能:保证线程同步的等待函数 函数原型: DWORD WaitForSingleObject(HANDLE hHandle,//同步对象的句柄 DWORD dwMilliseconds//以毫秒为单位的超时间隔,如果设为 INFINITE,则超时间隔是无限的 ); 说明: 返回值 含义 函数失败WAIT_FAILEDWAIT_OBJECT_0 指定的同步对象处于有信号的状态 WAIT_ABANDONED 拥有一个 mutex 的线程已经中断了,但未释放该 MUTEX WAIT_TIMEOUT 超时返回,并且同步对象无信号WaitForMultipleObjects() () 功能:可以同时监测多个同步对象 函数原型: DWORD WaitForMultipleObjects(DWORD nCount,//句柄数组中句柄的数目 CONST HANDLE *lpHandles,//代表一个句柄数组 BOOL bWaitAll, //说明了等待类型(),如果为 TRUE,那么函数在所有对象都有信号后才返回, //如果为 FALSE,则只要有一个对象变成有信号的,函数就返回 DWORD dwMilliseconds//以毫秒为单位的超时间隔 ); 说明: 返回值 含义 若 bWaitAll 为 TRUE, 则返回值表明所有对象 象的最小索引. WAIT_OBJECT_0 到 WAIT_ OBJECT_0+nCount-1 都是有信号的. 如果 bWaitAll 为 FALSE, 则返回值减去 WAIT_OBJECT_0 就是数组中有信号对 WAIT_ABANDONED_0 到 WAIT_ ABANDONED_ 0+nCount-1 所有对象都有信号,但有一个 mutex 被 ONED_0 就是被放弃 WAIT_TIMEOUT 超时返回 mutex 在对象数组中的索引.若 bWaitAll 为 TRUE,则返回值表明放弃了.若 bWaitAll 为 FALSE,则返回值减去 WAIT_ABANDCreateEventCreateEvent 的用法 HANDLE CreateEvent( lpEventAttributes, // SD // // reset type LPSECURITY_ATTRIBUTES BOOL BOOL bManualReset, bInitialState, lpNameinitial //state object nameLPCTSTR );该函数创建一个 Event 同步对象,并返回该对象的 Handle 一般为 NULL 创建的 Event 是自动复位还是人工复位 为无信号. ,如果 true,人工复位,lpEventAttributes bManualReset一旦该 Event 被设置为有信号,则它一直会等到 ResetEvent()API 被调用时才会恢复 如果为 false,Event 被设置为有信号,则当有一个 wait 到它的 Thread 时, 该 Event 就会自动复位,变成无信号. bInitialState lpName 初始状态,true,有信号,false 无信号 Event 对象名一个 Event 被创建以后,可以用 OpenEvent()API 来获得它的 Handle,用 CloseHandle() 来关闭它,用 SetEvent()或 PulseEvent()来设置它使其有信号,用 ResetEvent() 来使其无信号,用 WaitForSingleObject()或 WaitForMultipleObjects()来等待 其变为有信号. PulseEvent()是一个比较有意思的使用方法,正如这个 API 的名字,它使一个 Event 对象的状态发生一次脉冲变化,从无信号变成有信号再变成无信号,而整个操作是原子的. 对自动复位的 Event 对象,它仅释放第一个等到该事件的 thread(如果有),而对于 人工复位的 Event 对象,它释放所有等待的 thread.下异步串口通讯( 使用 Win32API 实现 Windows 下异步串口通讯(下)- -只一个框架性流程而矣............ 实现重叠模型的步骤 下面就结合俺写的一个 Console 程序简单示例进行说明: 【第一步】打开串口 第一步】 HANDLE m_hCom = CreateFile(&com1&,GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (m_hCom == INVALID_HANDLE_VALUE) { cout&&&CreateFile fail!&&return -1; } cout&&&CreateFile OK!&&& p=&& /& 【第二步】设置缓冲区大小 第二步】 if(!SetupComm(m_hCom,)) { cout&&&SetupComm fail! Close Comm!&& CloseHandle(m_hCom); return -1; } cout&&&SetupComm OK!&&& p=&& /& 【第三步】设置超时 第三步】 COMMTIMEOUTS TimeO memset(&TimeOuts,0,sizeof(TimeOuts)); TimeOuts.ReadIntervalTimeout = MAXDWORD; TimeOuts.ReadTotalTimeoutConstant = 0; TimeOuts.ReadTotalTimeoutMultiplier = 0; TimeOuts.WriteTotalTimeoutConstant = 2000; TimeOuts.WriteTotalTimeoutMultiplier = 50; SetCommTimeouts(m_hCom,&TimeOuts); 【第四步】设置串口参数 第四步】 DCB if (!GetCommState(m_hCom,&dcb)) { cout&&&GetCommState fail! Comm close&& CloseHandle(m_hCom); return -1; } cout&&&GetCommState OK!&&dcb.DCBlength = sizeof(dcb); if (!BuildCommDCB(&9600,n,8,1&,&dcb))//填充DCB的数据传输率,奇偶校验类型,数据位,停止位 { //参数修改错误,进行错误处理 cout&&&BuileCOmmDCB fail,Comm close!&& CloseHandle(m_hCom); return -1; } if(SetCommState(m_hCom,&dcb)) { cout&&&SetCommState OK!&& }【第五步】建立并初始化重叠结构 第五步】 OVERLAPPED wrO ZeroMemory(&wrOverlapped,sizeof(wrOverlapped)); if (wrOverlapped.hEvent != NULL) { ResetEvent(wrOverlapped.hEvent); wrOverlapped.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); } 【第六步】封装数据(按照自己的格式封装需要发送的数据,此步可以省略) 第六步】封装数据 typedef enum { HEAT_BEAT, //心跳数据 NET_STATE,//网络状态数据 PACKET //正常数据包 //支持可扩展性...... }ProtocolT typedef enum { Train_No,//无线车次信息 Attemper_Command,//调度命令信息 Revert_Command,//调度命令回执信息 Replay_Command,//重发的调度命令信息 KGL_SING //开关量数据//支持可扩展性...... }PacketDataT //串口数据结构 typedef struct SerialNetProto { unsigned long PacketS //包总长度,不包括本身字段 ProtocolType NetS //协议包类型 PacketDataType DataT //数据类型 unsigned long SourcedA //数据包源地址 unsigned long DestinationA //数据包目的地址 unsigned long DataL unsigned long O }PacketH int DataLen = 100; char *pBuf = new char[DataLen]; strcpy(pBuf,&Hello World!&); DataLen = strlen(pBuf); //包的数据段长度 // 数据在整个包中的偏移地址PacketHead MMyhead.DestinationAddr = 11; Myhead.SourcedAddr = 10; Myhead.DataType = Attemper_C Myhead.DataLength = DataL Myhead.NetState = PACKET; Myhead.PacketSize = sizeof(PacketHead) - sizeof(unsigned long); Myhead.Offset = sizeof(Myhead.DestinationAddr) +sizeof(Myhead.SourcedAddr) + sizeof(Myhead.D ataType) +sizeof(Myhead.DataLength) + sizeof(Myhead.NetState) + sizeof(Myhead.PacketSize);char *pSendBuffer = new char[sizeof(Myhead)+DataLen+ 4];//发送的数据 memcpy(pSendBuffer,#&,2);//包头标志 memcpy(pSendBuffer+2,(char*)&Myhead,sizeof(Myhead));//包头 memcpy(pSendBuffer+2+sizeof(Myhead),pBuf,DataLen);//数据 memcpy(pSendBuffer+2+sizeof(Myhead)+DataLen,&@@&,2);//包尾标志 【第七步】发送数据 第七步】 DWORD dwE //DWORD dwWantSend = 100; DWORD dwRealSend = 0; char* pReadBuf = NULL; if (ClearCommError(m_hCom,&dwError,NULL)) { PurgeComm(m_hCom,PURGE_TXABORT | PURGE_TXCLEAR); cout&&&PurgeComm OK!&& } if (!WriteFile(m_hCom,pSendBuffer,sizeof(Myhead)+DataLen+ 4,&dwRealSend,&wrOverlapped)) { if (GetLastError() == ERROR_IO_PENDING) { while (!GetOverlappedResult(m_hCom,&wrOverlapped,&dwRealSend,FALSE)) { if (GetLastError() == ERROR_IO_INCOMPLETE) { //cout&&&写未完成,继续!&& } else { cout&&&发生错误,尝试恢复!&& ClearCommError(m_hCom,&dwError,NULL); } } } }【第八步】数据接收 第八步】 DWORD dwE DWORD dwWantRead = 100; DWORD dwRealRead = 0; char* pReadBuf = new char[100]; if (ClearCommError(m_hCom,&dwError,NULL)) { PurgeComm(m_hCom,PURGE_TXABORT | PURGE_TXCLEAR); cout&&&PurgeComm OK!&& } if(!ReadFile(m_hComm,pReadBuf,dwWantRead,&RealRead,&wrOverlapped)) { if(dwError = GetLastError()==ERROR_IO_PENDING) { While(GetOverlappedResult(m_hComm,&wrOverlapped,&dwRealRead,FALSE)) { //对接收到的数据进行数据解析,处理 //【第九步】............ cout&&&dwRealRead = &&& } } } 【第九步】数据解析(数据解包处理) 第九步】数据解析 #define MAX_SERIAL_BUFFER 4096 BOOL CanGetFullFrame(char* pReadBuf,int& dwRealRead) { static char Buf[MAX_SERIAL_BUFFER*2];//自定义一个数据缓冲区 static unsigned long nFrameStart = 0;//数据祯的开始位置 static unsigned long nFrameEnd = 0;//数据祯的结束位置 static unsigned long nCurrectPos = 0;//指针当前位置 char *pdest = NULL;if (pReadBuf && (dwRealRead!= 0)) { memcpy(&Buf[nCurrectPos],pReadBuf,dwRealRead); nCurrectPos = nCurrectPos + dwRealR//更新当前位置 } //查找数据祯的开始标志 pdest = (char*)Find(Buf,#&,MAX_SERIAL_BUFFER*2,2); if (pdest) { nFrameStart = unsigned long(pdest - Buf);//找到数据祯的开始位置 }else//没有找到开始祯标志#& { Buf[0] = Buf[nCurrectPos];//丢弃数据 nFrameStart = 0; return FALSE; }//查找数据祯的结尾标志 pdest = (char*)Find(Buf,&@@&,MAX_SERIAL_BUFFER*2,2); if (pdest) { nFrameEnd = unsigned long (pdest - Buf+2); dwRealRead= nFrameEnd - nFrameS memcpy(pReadBuf,&Buf[nFrameStart],dwRealRead); nFrameStart = nFrameE//指向下一帧的开始位置 nCurrectPos = nCurrectPos - dwRealR//修正 nCurrentPos 值 memcpy(Buf,&Buf[nFrameEnd],nCurrectPos);//向前移动数据 return TRUE; } else { return FALSE; } } //一个在内存块中查找指定字符串的函数 一个在内存块中查找指定字符串的函数 void* Find(const char *pSour,const char *pDest,int SourLen,int DestLen) { int i = 0, j = 0; while(i & SourLen && j & DestLen) { if(*(pSour + i) == *(pDest + j)) { i++; j++; } else { i =i - j + 1; j = 0; } } if(j == DestLen) {return (void*)(pSour + (i - DestLen)); } else { return NULL; } } 【第十步】重新投递 Overlapped,略...... 第十步】 注:////////////////////////////////////////////////////////////////////////////////////////////////////////////////VC 实现串口通信例程 实现串口通信例程作者:阮帮秋(2001.4)摘要: 摘要:WIN95 界面下的 VC++串口通讯程序在 WIN32 下是不建议对端口进行操作的,在 WIN32 中所有的设备都被看成是文件,串行口也不例外也是作为文件来进行处理的. 关键词 串行口,DWORD,缓冲区WIN95 界面下的 VC++串口通讯程序在 WIN32 下是不建议对端口进行操作的,在 WIN32 中所有的设备都被看成是文件,串行口也不例外也是作为文件来进行处理的.这是 我的一份关于串口编程的读书笔记,对于使 用 VC 进行编程的同行应该有一定的帮助. 1.打开串口: 打开串口: 打开串口 在 Window 95 下串行口作为文件处理,使用文件操作对串行口进行处理.使用 CreateFile()打开串口,CreateFile()将返回串口的句柄. HANDLE CreateFile( LPCTSTR lpFileName, // pointer to name of the file DWORD dwDesiredAccess, // access (read-write) mode DWORD dwShareMode, // share mode LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security attributes DWORD dwCreationDistribution, // how to create DWORD dwFlagsAndAttributes, // file attributes HANDLE hTemplateFile // handle to file with attributes to copy ); lpFileName: 3.设置缓冲区长度: 设置缓冲区长度: 设置缓冲区长度BOOL SetupComm( HANDLE hFile, // handle of communications device DWORD dwInQueue, // size of input buffer DWORD dwOutQueue // size of output buffer ); 4.COMMPROP 结构: 结构: 可使用 GetCommProperties()取得 COMMPROP 结构,COMMPROP 结构中记载了 系统支持的各项设置. typedef struct _COMMPROP { // cmmp WORD wPacketL // packet size, in bytes WORD wPacketV // packet version DWORD dwServiceM // services implemented DWORD dwReserved1; // reserved DWORD dwMaxTxQ // max Tx bufsize, in bytes DWORD dwMaxRxQ // max Rx bufsize, in bytes DWORD dwMaxB // max baud rate, in bps DWORD dwProvSubT // specific provider type DWORD dwProvC // capabilities supported DWORD dwSettableP // changeable parameters DWORD dwSettableB // allowable baud rates WORD wSettableD // allowable byte sizes WORD wSettableStopP // stop bits/parity allowed DWORD dwCurrentTxQ // Tx buffer size, in bytes DWORD dwCurrentRxQ // Rx buffer size, in bytes DWORD dwProvSpec1; // provider-specific data DWORD dwProvSpec2; // provider-specific data WCHAR wcProvChar[1]; // provider-specific data } COMMPROP; dwMaxBaud: BAUD_075 75 bps BAUD_110 110 bps BAUD_134_5 134.5 bps BAUD_150 150 bps BAUD_300 300 bps BAUD_600 600 bps BAUD_ bps BAUD_ bps BAUD_ bps BAUD_ bps BAUD_ bps BAUD_ bpsBAUD_ bps BAUD_ bps BAUD_ bps BAUD_56K 56K bps BAUD_ bps BAUD_200 bps BAUD_128K 128K bps BAUD_USER Programmable baud rates available dwProvSubType: PST_FAX 传真设备 PST_LAT LAT 协议 PST_MODEM 调制解调器设备 PST_NETWORK_BRIDGE 未指定的网桥 PST_PARALLELPORT 并口 PST_RS232 RS-232 口 PST_RS422 RS-422 口 PST_RS423 RS-432 口 PST_RS449 RS-449 口 PST_SCANNER 扫描仪设备 PST_TCPIP_TELNET TCP/IP Telnet 协议 PST_UNSPECIFIED 未指定 PST_X25 X.25 标准 dwProvCapabilities PCF_16BITMODE 支持特殊的 16 位模式 PCF_DTRDSR 支持 DTR(数据终端就绪)/DSR(数据设备就绪) PCF_INTTIMEOUTS 支持区间超时 PCF_PARITY_CHECK 支持奇偶校验 PCF_RLSD 支持 RLSD(接收线信号检测) PCF_RTSCTS 支持 RTS(请求发送)/CTS(清除发送) PCF_SETXCHAR 支持可设置的 XON/XOFF PCF_SPECIALCHARS 支持特殊字符 PCF_TOTALTIMEOUTS 支持总(占用时间)超时 PCF_XONXOFF 支持 XON/XOFF 流控制 标准 RS-232 和 WINDOW 支持除 PCF_16BITMODE 和 PCF_SPECIALCHAR 外的所 有功能 dwSettableParams SP_BAUD 可配置波特率 SP_DATABITS 可配置数据位个数 SP_HANDSHAKING 可配置握手(流控制) SP_PARITY 可配置奇偶校验模式 SP_PARITY_CHECK 可配置奇偶校验允许/禁止 SP_RLSD 可配置 RLSD(接收信号检测) SP_STOPBITS 可配置停止位个数 标准 RS-232 和 WINDOW 支持以上所有功能wSettableData DATABITS_5 5 个数据位 DATABITS_6 6 个数据位 DATABITS_7 7 个数据位 DATABITS_8 8 个数据位 DATABITS_16 16 个数据位 DATABITS_16X 通过串行硬件线路的特殊宽度路径 WINDOWS 95 支持 16 的所有设置 5.DCB 结构: 结构: typedef struct _DCB {// dcb DWORD DCB // sizeof(DCB) DWORD BaudR // DTR flow control type DTR_CO***OL_DISABLE 值将 DTR 置为 OFF, DTR_CO***OL_ENABLE 值将 DTR 置为 ON, DTR_CO***OL_HANDSHAKE 允许 DTR&握手&,DWORD // reserved 未使用 WORD wR // not currently used 未使用,必须为 0 WORD XonL // transmit XON threshold 指定在 XON 字符发送这前接收缓冲区中可允许的最小字节数 WORD XoffL // transmit XOFF threshold 指定在 XOFF 字符发送这前接收缓冲区中可允许的最小字节数 BYTE ByteS // number of bits/byte, 4-8 指定端口当前使用的数据位 BYTE P // 0-4=no,odd,even,mark,space 指定端口当前使用的奇偶校验方法,可能为: EVENPARITY,MARKPARITY,NOPARITY,ODDPARITY BYTE StopB // 0,1,2 = // Tx and Rx XON character 指定用于发送和接收字符 XON 的值 char XoffC // Tx and Rx XOFF character 指定用于发送和接收字符 XOFF 值 char ErrorC // error replacement character 本字符用来代替接收到的奇偶校验发生错误时的值 char EofC // end of input character 当没有使用二进制模式时,本字符可用来指示数据的结束 char EvtC // received event character 当接收到此字符时,会产生一个事件 WORD wReserved1; // do not use 未使用 } DCB; 6.改变端口设置 改变端口设置 使用如下的两个方法BOOL GetCommState(hComm,&dcb); BOOL SetCommState(hComm,&dcb); 7.改变普通设置 改变普通设置 BuildCommDCB(szSettings,&DCB); szSettings 的格式:baud parity data stop 例: &baud=96 parity=n data=8 stop=1& 简写:&96,N,8,1& szSettings 的有效值 baud: 11 or 110 = 110 bps 15 or 150 = 150 bps 30 or 300 = 300 bps 60 or 600 = 600 bps 12 or 1200 = 1200 bps 24 or 2400 = 2400 bps 48 or 4800 = 4800 bps 96 or 9600 = 9600 bps 19 or 1bps parity: n=none e=even o=odd m=mark s=space data: 5,6,7,8 StopBit 1,1.5,MCONFIG 结构: 结构: typedef struct _COMM_CONFIG { DWORD dwS WORD wV WORD wR DCB DWORD dwProviderSubT DWORD dwProviderO DWORD dwProviderS WCHAR wcProviderData[1]; } COMMCONFIG, *LPCOMMCONFIG; 可方便的使用 BOOL CommConfigDialog(LPTSTR lpszName, HWND hWnd, LPCOMMCONFIG lpCC); 来设置串行口. 9.超时设置 超时设置: 超时设置 可通过 COMMTIMEOUTS 结构设置超时, typedef struct _COMMTIMEOUTS { DWORD ReadIntervalT DWORD ReadTotalTimeoutM DWORD ReadTotalTimeoutC DWORD WriteTotalTimeoutM DWORD WriteTotalTimeoutC } COMMTIMEOUTS,*LPCOMMTIMEOUTS; 区间超时:(仅对从端口中读取数据有用)它指定在读取两个字符之间要经历的时间 总超时: 当读或写特定的字节数需要的总时间超过某一阈值时,超时触发. 超时公式: ReadTotalTimeout = (ReadTotalTimeoutMultiplier * bytes_to_read) + ReadToTaltimeoutConstant WriteTotalTimeout =timeouts); SetCommTimeouts(hComm,&timeouts); 10.查询方式读写数据 查询方式读写数据 例程: 例程: COMMTIMEOUTS DWORD ReadThread(LPDWORD lpdwParam) { BYTE inbuff[100]; DWORD nBytesR if(!(cp.dwProvCapabilities&PCF_INTTIMEOUTS)) return 1L; memset(&to,0,sizeof(to)); to.ReadIntervalTimeout = MAXDWORD; SetCommTimeouts(hComm,&to); while(bReading) { if(!ReadFile(hComm,inbuff,100,&nBytesRead,NULL)) locProcessCommError(GetLastError()); else if(nBytesRead) locProcessBytes(inbuff,nBytesRead); }PurgeComm(hComm,PURGE_RXCLEAR); return 0L; } NOTE: PurgeComm()是一个清除函数, 它可以中止任何未决的后台读或写, 并且可以冲掉 I/O 缓冲区. BOOL PurgeComm(HANDLE hFile,DWORD dwFlags); dwFlages 的有效值: PURGE_TXABORT: ClearCommError()将返回一个 COMSTAT 结构: typedef // reserved DWORD cbInQ // bytes in input buffer DWORD cbOutQ // bytes in output buffer } COMSTAT, *LPCOMSTAT; 其中的 cbInQue 和 cbOutQue 中即为缓冲区字节. 11.同步 I/O 读写数据 同步 COMMTIOMOUTS DWORD ReadThread(LPDWORD lpdwParam) { BYTE inbuff[100]; DWORD nByteRead,dwErrorMask,nToR COMSTAT if(!cp.dwProvCapabilities&PCF_TOTALTIMEOUTS) return 1L; memset(&to,0,sizeof(to)); to.ReadTotalTimeoutMultiplier = 5; to.ReadTotalTimeoutConstant = 50; SetCommTimeouts(hComm,&to); while(bReading) { ClearCommError(hComm,&dwErrorMask,&comstat); if(dwErrorMask) locProcessCommError(dwErrorMask); if(comstat.cbInQue &100) nToRead = 100; else nToRead = comstat.cbInQ if(nToRead == 0) if(!ReadFile(hComm,inbuff,nToRead,&nBytesRead,NULL)) locProcessCommError(GetLastError()); elseif(nBytesRead) locProcessBytes(inbuff,nBytesRead); } return 0L; } 12.异步 I/O 读写数据 异步 当 CreateFile()中的 fdwAttrsAndFlags 参数为 FILE_FLAG_OVERLAPPEN 时, 端口 是为异步 I/O 打开的,此时可以在 ReadFile 的最后一个参数中指定一个 OVERLAPPED 结 构,使数据的读操作在后台进行.WINDOWS 95 包括了异步 I/O 的许多变种. typedef struct _OVERLAPPED { DWORD I DWORD InternalH DWORD O DWORD OffsetH HANDLE hE } OVERLAPPED; 对于串行口仅 hEvent 成员有效,其于成员必须为 0. 例程: COMMTIMEOUTS ... DWORD ReadThread((LPDWORD lpdwParam) { BYTE inbuff[100]; DWORD nRytesRead,endtime, static OVERLAPPED if(!cp.dwProvCapabilities & PCF_TOTALTIMEOUTS) return 1L; memset(&to,0,sizeof(to)); to.ReadTotalTimeoutMultiplier = 5; to.ReadTotalTimeoutConstant = 1000; SetCommTimeouts(hComm,&to); o.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); while(bReading) { if(!ReadFile(hComm,inbuff,10,&nBytesRead,&o)) { nBytesRead = 0; if(lrc=GetLastError() == ERROR_IO_PENDING) { endtime = GetTickCount() + 1000; while(!GetOverlappedResult(hComm,&o,&nBytesRead,FALSE)) if(GetTickCount() & endtime)} if(nBytesRead) locProcessBytes(inbuff,nBytesRead); } else { if(nBytesRead) locProcessBytes(inbuff,nBytesRead); ResetEvent(o.hEvent); } } PurgeComm(hComm,PURGE_RXCLEAR); return 0L; } 这一例程是对一开始读缓冲区就读到所需的字节时的处理: while(bReading) { if(!ReadFile(hComm,inbuff,10,&nBytesRead,&o)) { if((lrc=GetLastError()) ==ERROR_IO_PENDING) { if(GetOverlappedResult(hComm,&o,&nBytesRead,TRUE)) { if(nBytesRead) locProcessBytesa(inbuff,nBytesRead); } else locProcessCommError(GetLastError()); } else locProcessCommError(GetLastError)); } else if(nBytesRead) locProcessBytes(inbuff,nBytesRead); ResetEvent(o.hEvent); } 13.事件驱 I/O 读写: 事件驱 读写: GetCommMask(hComm,&dwMask) Windows 95 报告给应用程序的事件由此方法返回. SetCommMasl(hComm,&dwMask) 添加或修改 Windows 此方法可以以同步或异步方式操作 例程: COMMTIMEOUTS... DWORD ReadTherad(LPDWORD lpdwParam) { BYTE binbuff[100]; DWORD nBytesRead,dwEvent,dwE COMSTAT SetCommMask(hComm,EV_RXHAR); while(bReading) { if(WaitCommEvent(hComm,&dwEvent,NULL)) { ClearCommError(hComm,&dwError,&cs); if((dwEvent&EV_RXCHAR)&&cs.cbInQue) { if(!ReadFile(hComm,inbuff,cs.cbInQue,&nBytesRead,NULL) locProcessCommError(GetLastError()); } else { if(nByteRead) locProcessBytes(inbuff,nBytesRead); } else locProcessCommError(GetLastError()); } PurgeComm(hComm,PURGE_RXCLEAR); return 0L; } NOTE:PCF_RTSCTS) { SetCommMask(hComm,EV_CTS); WaitCommEvent(hComm,&dwMask,NULL); if(dwMask&EV_CTS) { GetCommModemStatus(hComm,&dwStatus) if(dwStatus&MS_CTS_ON) /* CTS stransition OFF-ON */ else /* CTS stransition ON-OFF */ } } MS_CTS_ON CTS 为 ON MS_DSR_ON DSR 为 ON MS_RING_ON RING 为 ON MS_ELSD_ON RLSD 为 ON14.错误 错误 当发生错误时应用方法 ClearCommError(hComm,&dwErrorMask,&constat)得到错误 掩码. CE_BREAK 中止条件 CE_FRAME 帧错误 CW_IOE 一般 I/O 错误,常伴有更为详细的错误标志 CE_MODE 不支持请求的模式 CE_OVERRUN 缓冲区超限下一个字符将丢失 CE_RXOVER 接收缓冲区超限 CE_RXPARITY 奇偶校验错误 CE_TXFULL 发送缓冲区满 CE_DNS 没有选择并行设备 CE_PTO 并行设备发生超时 CE_OOP 并行设备缺纸 15.控制命令 控制命令 EscapeCommFunction()可将硬件信号置 ON 或 OFF,模拟 XON 或 XOFF BOOL EscapeCommFunction( HANDLE hFile, // handle to communications device DWORD dwFunc // extended function to perform ); dwFunc 的有效值(可用'|'同时使用多个值) CLRDTR DTR 置 OFF CLRRTS RTS 置 OFF SETDTR STR 置 ON SETRTS TRS 置 ON SETXOFF 模拟 XOFF 字符的接收 SETXON 模拟 XON 字符的接收 SETBREAK 在发送中产生一个中止 CLRBREAK 在发送中清除中止
更多搜索:
All rights reserved Powered by
文档资料库内容来自网络,如有侵犯请联系***。