《vc++串口编程 (2).pdf》由会员分享,可在线阅读,更多相关《vc++串口编程 (2).pdf(37页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、深入浅出深入浅出 VC+VC+VC+VC+串口编程之基本概念串口编程之基本概念2006-02-172006-02-172006-02-172006-02-17 09:4309:4309:4309:43 作者作者:宋宝华出处宋宝华出处:天极开发责任天极开发责任编辑:方舟编辑:方舟引言引言在 PC 机的主板上,有一种类型的接口可能为我们所忽视,那就是 RS-232C串行接口,在微软的Windows 系统中称其为 COM。我们可以通过设备管理器来查看 COM 的硬件参数设置,如图 1。图 1 在 Windows 上查看 PC串口设置迄今为止,几乎每一台 PC 都包含 COM。本质而言,COM 是 P
2、C为和外界通信所提供的一种串行数据传输的接口。作为一种物理通信的途径和设备,它和目前风靡的另一种串行接口USB 所提供的功能是一致的。不过 RS-232C显然已经开始被后起之秀 USB 赶超,因为 USB 的传输速率已经远远超过了RS-232C。尽管如此,RS-232C仍然具有非常广泛的应用,在相对长的一段时间里,难以被 USB 等接口取代。RS-232C接口(又称 EIA RS-232C),1970年由美国电子工业协会(EIA)联合贝尔系统、调制解调器厂家及计算机终端生产厂家共同制定,全名是数据终端设备(DTE)和数据通讯设备(DCE)之间串行二进制数据交换接口技术标准。本文将对这一接口进行
3、硬件原理的介绍,随后我们将逐章学习 DOS 平台的串口编程,及 Windows 平台下基于 API、控件和第三方类的串口编程,最后本文将给出一个综合实例。在本文的连载过程中,您可以通过如下方式联系作者(热忱欢迎读者朋友对本文的内容提出质疑或给出修改意见):作者 email:(可以来信提问,笔者将力求予以回信解答,并摘取其中的典型问题,在本系列文章最后一次连载的读者反馈中予以阐述);硬件原理硬件原理众所周知,CPU与存储芯片和 I/O 芯片的通信是并行的(并行传输的最大位数依赖于 CPU 的字长、数据总线的宽度),一种叫做 UART(通用异步收发器,Universal AsynchronousR
4、eceiver/Transmitter)的芯片提供了并行数据传输和 RS-232C串行数据传输方式的转换。这样的设备通常有如图 2 所示的管脚分布,当其向外传输数据时,CPU 并行的将数据写入这类芯片的寄存器,UART 再将寄存器中的数据一位一位地移动并向外传输;当外界向其传输数据时,UART 一位一位地接收数据,并将其移位组合为并行数据,CPU 再并行地读取这些数据。实际上,由于 UART 芯片一般以 TTL/CMOS 电平工作,在 UART 连接接口之前,还要经过一个 TTL/CMOS 和 RS-232C电平的转换。RS-232C规定了其标准的电气特性,逻辑1 对应的电压必须在-5-15V
5、 之间;逻辑 0对应的的电压必须在+5+15V之间。图 2 UART 并/串转换一个常见的 TTL/CMOS 和 RS-232C电平转换芯片如图 3。图 3 常见的 TTL/CMOS 和 RS-232C电平转换芯片RS-232C通常以两类接插件与外界相连,分别称为 DB9 和 DB25,如图 4所示。图 4 DB9和 DB25而接插件中各个针的定义则如表 1:表 1 DB9和 DB25引脚定义DB9DB25针号功能说明缩写针号功能说明缩写1数据载波检测DCD8数据载波检测DCD2接收数据RXD3接收数据RXD3发送数据TXD2发送数据TXD4数据终端准备DTR20数据终端准备DTR5信号地GN
6、D7信号地GND6数据设备准备好DSR6数据准备好DSR7请求发送RTS4请求发送RTS8清除发送CTS5清除发送CTS9振铃指示DELL22振铃指示DELLRS-232C定义为数据通信设备(DCE)和数据终端设备(DTE)之间的互连,实现上,到现在为止,究竟一个设备属于 DCE还是属于 DTE 已经没有明显的界限,PC 即可作为 DCE,又可作为 DTE。两串口互连,连接方法主要有二:一种方法是,数据的发送和接收由软件控制,不进行硬件握手,其连接方法如图 5(最常用 DB9 连接示意)和表 2(DB9、DB25三线连接表),真正需要互相连接的是 RXD、TXD 和 GND;图 5 无硬件握手
7、时两串口连接表 2 DB9、DB25三线连接9 针9针5 针25针2 9 针25针233222322333557757软件握手又称为 XON/XOFF,通常以 CTRL-S(0 x13)和 CTRL-Q(0 x11)两个字符来实现流控制。前者用于请求对方暂停发送,后者用于清除暂停传送的请求,继续发送数据。另一种方法是,数据的发送和接收由硬件控制,进行硬件握手,其连接方法如图 6(最常用 DB9 连接示意),需要连接的信号除 RXD、TXD 和 GND 外,还包括 DTR、DSR、RTS 和 CTS。硬件握手依赖于 RTS 和 CTS 信号,当发送设备欲发送数据时,将 RTS 信号置为有效表示请
8、求发送,接收设备准备好后,置 CTS 信号有效,接着发送设备通过信号线 TXD 开始发送串行数据。这里我们联想开来,RTS/CTS 模式在许多领域里都出现过。回忆一下 IEEE 802.11无线局域网协议标准,在其 MAC 协议中就使用了 RTS/CTS,RTS/CTS 抽象开来就是一种请求/应答。笔者曾经在拙作中多次以实例论证计算机领域里许多知识的相通性,这又是一个明证。图 6 有硬件握手时两串口连接实际上,目前我们经常使用的是方法一,即只连接 RXD、TXD 和 GND,简单灵活。另外,串口之间互连还有诸多途径,如图 7 所示。图 7 其它互连方式Win32Win32Win32Win32
9、串口编程串口编程作者:韩耀旭下载源代码在工业控制中,工控机(一般都基于 Windows 平台)经常需要与智能仪表通过串口进行通信。串口通信方便易行,应用广泛。一般情况下,工控机和各智能仪表通过 RS485 总线进行通信。RS485 的通信方式是半双工的,只能由作为主节点的工控 PC 机依次轮询网络上的各智能控制单元子节点。每次通信都是由 PC 机通过串口向智能控制单元发布命令,智能控制单元在接收到正确的命令后作出应答。在 Win32 下,可以使用两种编程方式实现串口通信,其一是使用 ActiveX 控件,这种方法程序简单,但欠灵活。其二是调用 Windows 的 API 函数,这种方法可以清楚
10、地掌握串口通信的机制,并且自由灵活。本文我们只介绍 API 串口通信部分。串口的操作可以有两种操作方式:同步操作方式和重叠操作方式(又称为异步操作方式)。同步操作时,API 函数会阻塞直到操作完成以后才能返回(在多线程方式中,虽然不会阻塞主线程,但是仍然会阻塞监听线程);而重叠操作方式,API 函数会立即返回,操作在后台进行,避免线程的阻塞。无论那种操作方式,一般都通过四个步骤来完成:(1)打开串口(2)配置串口(3)读写串口(4)关闭串口(1)打开串口Win32 系统把文件的概念进行了扩展。无论是文件、通信设备、命名管道、邮件槽、磁盘、还是控制台,都是用 API 函数 CreateFile
11、来打开或创建的。该函数的原型为:HANDLE CreateFile(LPCTSTR lpFileName,DWORD dwDesiredAccess,DWORD dwShareMode,LPSECURITY_ATTRIBUTES lpSecurityAttributes,DWORD dwCreationDistribution,DWORD dwFlagsAndAttributes,HANDLE hTemplateFile);lpFileName:将要打开的串口逻辑名,如“COM1”;dwDesiredAccess:指定串口访问的类型,可以是读取、写入或二者并列;dwShareMode:指定共享
12、属性,由于串口不能共享,该参数必须置为 0;lpSecurityAttributes:引用安全性属性结构,缺省值为 NULL;dwCreationDistribution:创建标志,对串口操作该参数必须置为OPEN_EXISTING;dwFlagsAndAttributes:属性描述,用于指定该串口是否进行异步操作,该值为 FILE_FLAG_OVERLAPPED,表示使用异步的 I/O;该值为 0,表示同步 I/O 操作;hTemplateFile:对串口而言该参数必须置为 NULL;同步 I/O 方式打开串口的示例代码:HANDLE hCom;/全局变量,串口句柄hCom=CreateFi
13、le(COM1,/COM1 口GENERIC_READ|GENERIC_WRITE,/允许读和写0,/独占方式NULL,OPEN_EXISTING,/打开而不是创建0,/同步方式NULL);if(hCom=(HANDLE)-1)AfxMessageBox(打开 COM 失败!);return FALSE;return TRUE;重叠 I/O 打开串口的示例代码:HANDLE hCom;/全局变量,串口句柄hCom=CreateFile(COM1,/COM1 口GENERIC_READ|GENERIC_WRITE,/允许读和写0,/独占方式NULL,OPEN_EXISTING,/打开而不是创建F
14、ILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,/重叠方式NULL);if(hCom=INVALID_HANDLE_VALUE)AfxMessageBox(打开 COM 失败!);return FALSE;return TRUE;(2)、配置串口在打开通讯设备句柄后,常常需要对串口进行一些初始化配置工作。这需要通过一个 DCB 结构来进行。DCB 结构包含了诸如波特率、数据位数、奇偶校验和停止位数等信息。在查询或配置串口的属性时,都要用 DCB 结构来作为缓冲区。一般用 CreateFile 打开串口后,可以调用 GetCommState 函数来获取串口的初
15、始配置。要修改串口的配置,应该先修改 DCB 结构,然后再调用 SetCommState函数设置串口。DCB 结构包含了串口的各项参数设置,下面仅介绍几个该结构常用的变量:typedef struct _DCB/波特率,指定通信设备的传输速率。这个成员可以是实际波特率值或者下面的常量值之一:DWORD BaudRate;CBR_110,CBR_300,CBR_600,CBR_1200,CBR_2400,CBR_4800,CBR_9600,CBR_19200,CBR_38400,CBR_56000,CBR_57600,CBR_115200,CBR_128000,CBR_256000,CBR_14
16、400DWORD fParity;/指定奇偶校验使能。若此成员为 1,允许奇偶校验检查BYTE ByteSize;/通信字节位数,48BYTE Parity;/指定奇偶校验方法。此成员可以有下列值:EVENPARITY 偶校验NOPARITY无校验MARKPARITY 标记校验ODDPARITY 奇校验BYTE StopBits;/指定停止位的位数。此成员可以有下列值:ONESTOPBIT 1 位停止位TWOSTOPBITS 2 位停止位ONE5STOPBITS1.5 位停止位 DCB;winbase.h 文件中定义了以上用到的常量。如下:#define NOPARITY0#define OD
17、DPARITY1#define EVENPARITY2#define ONESTOPBIT0#define ONE5STOPBITS1#define TWOSTOPBITS2#define CBR_110110#define CBR_300300#define CBR_600600#define CBR_12001200#define CBR_24002400#define CBR_48004800#define CBR_96009600#define CBR_1440014400#define CBR_1920019200#define CBR_3840038400#define CBR_5
18、600056000#define CBR_5760057600#define CBR_115200115200#define CBR_128000128000#define CBR_256000256000GetCommState 函数可以获得 COM 口的设备控制块,从而获得相关参数:BOOL GetCommState(HANDLE hFile,/标识通讯端口的句柄LPDCB lpDCB/指向一个设备控制块(DCB 结构)的指针);SetCommState 函数设置 COM 口的设备控制块:BOOL SetCommState(HANDLE hFile,LPDCB lpDCB);除了在 BCD
19、 中的设置外,程序一般还需要设置 I/O 缓冲区的大小和超时。Windows 用 I/O 缓冲区来暂存串口输入和输出的数据。如果通信的速率较高,则应该设置较大的缓冲区。调用 SetupComm 函数可以设置串行口的输入和输出缓冲区的大小。BOOL SetupComm(HANDLE hFile,/通信设备的句柄DWORD dwInQueue,/输入缓冲区的大小(字节数)DWORD dwOutQueue/输出缓冲区的大小(字节数));在用 ReadFile 和 WriteFile 读写串行口时,需要考虑超时问题。超时的作用是在指定的时间内没有读入或发送指定数量的字符,ReadFile 或 Writ
20、eFile 的操作仍然会结束。要查询当前的超时设置应调用 GetCommTimeouts 函数,该函数会填充一个COMMTIMEOUTS 结构。调用 SetCommTimeouts 可以用某一个 COMMTIMEOUT S结构的内容来设置超时。读写串口的超时有两种:间隔超时和总超时。间隔超时是指在接收时两个字符之间的最大时延。总超时是指读写操作总共花费的最大时间。写操作只支持总超时,而读操作两种超时均支持。用 COMMTIMEOUTS 结构可以规定读写操作的超时。COMMTIMEOUTS 结构的定义为:typedef struct _COMMTIMEOUTS DWORD ReadInterva
21、lTimeout;/读间隔超时DWORD ReadTotalTimeoutMultiplier;/读时间系数DWORD ReadTotalTimeoutConstant;/读时间常量DWORD WriteTotalTimeoutMultiplier;/写时间系数DWORD WriteTotalTimeoutConstant;/写时间常量 COMMTIMEOUTS,*LPCOMMTIMEOUTS;COMMTIMEOUTS 结构的成员都以毫秒为单位。总超时的计算公式是:总超时时间系数要求读/写的字符数时间常量例如,要读入 10 个字符,那么读操作的总超时的计算公式为:读总超时ReadTotalTi
22、meoutMultiplier 10ReadTotalTimeoutConstant可以看出:间隔超时和总超时的设置是不相关的,这可以方便通信程序灵活地设置各种超时。如果所有写超时参数均为 0,那么就不使用写超时。如果 ReadIntervalTimeout为 0,那么就不使用读间隔超时。如果 ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant 都为 0,则不使用读总超时。如果读间隔超时被设置成 MAXDWORD 并且读时间系数和读时间常量都为 0,那么在读一次输入缓冲区的内容后读操作就立即返回,而不管是否读入了要求的字符。在用重叠方式读写串
23、口时,虽然 ReadFile 和 WriteFile 在完成操作以前就可能返回,但超时仍然是起作用的。在这种情况下,超时规定的是操作的完成时间,而不是 ReadFile 和 WriteFile 的返回时间。配置串口的示例代码:SetupComm(hCom,1024,1024);/输入缓冲区和输出缓冲区的大小都是 1024COMMTIMEOUTS TimeOuts;/设定读超时TimeOuts.ReadIntervalTimeout=1000;TimeOuts.ReadTotalTimeoutMultiplier=500;TimeOuts.ReadTotalTimeoutConstant=500
24、0;/设定写超时TimeOuts.WriteTotalTimeoutMultiplier=500;TimeOuts.WriteTotalTimeoutConstant=2000;SetCommTimeouts(hCom,&TimeOuts);/设置超时DCB dcb;GetCommState(hCom,&dcb);dcb.BaudRate=9600;/波特率为 9600dcb.ByteSize=8;/每个字节有 8 位dcb.Parity=NOPARITY;/无奇偶校验位dcb.StopBits=TWOSTOPBITS;/两个停止位SetCommState(hCom,&dcb);PurgeCo
25、mm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);在读写串口之前,还要用 PurgeComm()函数清空缓冲区,该函数原型:BOOL PurgeComm(HANDLE hFile,/串口句柄DWORD dwFlags/需要完成的操作);参数 dwFlags 指定要完成的操作,可以是下列值的组合:PURGE_TXABORT中断所有写操作并立即返回,即使写操作还没有完成。PURGE_RXABORT中断所有读操作并立即返回,即使读操作还没有完成。PURGE_TXCLEAR清除输出缓冲区PURGE_RXCLEAR清除输入缓冲区(3)、读写串口我们使用 ReadFile 和 Wr
26、iteFile 读写串口,下面是两个函数的声明:BOOL ReadFile(HANDLE hFile,/串口的句柄/读入的数据存储的地址,/即读入的数据将存储在以该指针的值为首地址的一片内存区LPVOID lpBuffer,DWORD nNumberOfBytesToRead,/要读入的数据的字节数/指向一个 DWORD 数值,该数值返回读操作实际读入的字节数LPDWORD lpNumberOfBytesRead,/重叠操作时,该参数指向一个 OVERLAPPED 结构,同步操作时,该参数为 NULL。LPOVERLAPPED lpOverlapped);BOOL WriteFile(HAND
27、LE hFile,/串口的句柄/写入的数据存储的地址,/即以该指针的值为首地址的 nNumberOfBytesToWrite/个字节的数据将要写入串口的发送数据缓冲区。LPCVOID lpBuffer,DWORD nNumberOfBytesToWrite,/要写入的数据的字节数/指向指向一个 DWORD 数值,该数值返回实际写入的字节数LPDWORD lpNumberOfBytesWritten,/重叠操作时,该参数指向一个 OVERLAPPED 结构,/同步操作时,该参数为 NULL。LPOVERLAPPED lpOverlapped);在用 ReadFile 和 WriteFile 读写
28、串口时,既可以同步执行,也可以重叠执行。在同步执行时,函数直到操作完成后才返回。这意味着同步执行时线程会被阻塞,从而导致效率下降。在重叠执行时,即使操作还未完成,这两个函数也会立即返回,费时的 I/O 操作在后台进行。ReadFile 和 WriteFile 函数是同步还是异步由 CreateFile 函数决定,如果在调用 CreateFile 创建句柄时指定了 FILE_FLAG_OVERLAPPED 标志,那么调用ReadFile 和 WriteFile 对该句柄进行的操作就应该是重叠的;如果未指定重叠标志,则读写操作应该是同步的。ReadFile 和 WriteFile 函数的同步或者异
29、步应该和 CreateFile 函数相一致。ReadFile 函数只要在串口输入缓冲区中读入指定数量的字符,就算完成操作。而 WriteFile 函数不但要把指定数量的字符拷入到输出缓冲区,而且要等这些字符从串行口送出去后才算完成操作。如果操作成功,这两个函数都返回 TRUE。需要注意的是,当 ReadFile 和WriteFile 返回 FALSE 时,不一定就是操作失败,线程应该调用 GetLastError 函数分析返回的结果。例如,在重叠操作时如果操作还未完成函数就返回,那么函数就返回 FALSE,而且 GetLastError 函数返回 ERROR_IO_PENDING。这说明重叠操
30、作还未完成。同步方式读写串口比较简单,下面先例举同步方式读写串口的代码:/同步读串口char str100;DWORD wCount;/读取的字节数BOOL bReadStat;bReadStat=ReadFile(hCom,str,100,&wCount,NULL);if(!bReadStat)AfxMessageBox(读串口失败!);return FALSE;return TRUE;/同步写串口char lpOutBuffer100;DWORD dwBytesWrite=100;COMSTAT ComStat;DWORD dwErrorFlags;BOOL bWriteStat;Clea
31、rCommError(hCom,&dwErrorFlags,&ComStat);bWriteStat=WriteFile(hCom,lpOutBuffer,dwBytesWrite,&dwBytesWrite,NULL);if(!bWriteStat)AfxMessageBox(写串口失败!);PurgeComm(hCom,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);在重叠操作时,操作还未完成函数就返回。重叠 I/O 非常灵活,它也可以实现阻塞(例如我们可以设置一定要读取到一个数据才能进行到下一步操作)。有两种方法可以等待操
32、作完成:一种方法是用象 WaitForSingleObject 这样的等待函数来等待 OVERLAPPED 结构的 hEvent 成员;另一种方法是调用 GetOverlappedResult 函数等待,后面将演示说明。下面我们先简单说一下 OVERLAPPED 结构和 GetOverlappedResult 函数:OVERLAPPED 结构OVERLAPPED 结构包含了重叠 I/O 的一些信息,定义如下:typedef struct _OVERLAPPED /oDWORDInternal;DWORDInternalHigh;DWORDOffset;DWORDOffsetHigh;HANDL
33、E hEvent;OVERLAPPED;在使用 ReadFile 和 WriteFile 重叠操作时,线程需要创建 OVERLAPPED 结构以供这两个函数使用。线程通过 OVERLAPPED 结构获得当前的操作状态,该结构最重要的成员是 hEvent。hEvent 是读写事件。当串口使用异步通讯时,函数返回时操作可能还没有完成,程序可以通过检查该事件得知是否读写完毕。当调用 ReadFile,WriteFile函数的时候,该成员会自动被置为无信号状态;当重叠操作完成后,该成员变量会自动被置为有信号状态。GetOverlappedResult 函数BOOL GetOverlappedResul
34、t(HANDLE hFile,/串口的句柄/指向重叠操作开始时指定的 OVERLAPPED 结构LPOVERLAPPED lpOverlapped,/指向一个 32 位变量,该变量的值返回实际读写操作传输的字节数。LPDWORD lpNumberOfBytesTransferred,/该参数用于指定函数是否一直等到重叠操作结束。/如果该参数为 TRUE,函数直到操作结束才返回。/如果该参数为 FALSE,函数直接返回,这时如果操作没有完成,/通过调用 GetLastError()函数会返回 ERROR_IO_INCOMPLETE。BOOL bWait);该函数返回重叠操作的结果,用来判断异步操
35、作是否完成,它是通过判断OVERLAPPED 结构中的 hEvent 是否被置位来实现的。异步读串口的示例代码:char lpInBuffer1024;DWORD dwBytesRead=1024;COMSTAT ComStat;DWORD dwErrorFlags;OVERLAPPED m_osRead;memset(&m_osRead,0,sizeof(OVERLAPPED);m_osRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);ClearCommError(hCom,&dwErrorFlags,&ComStat);dwBytesRead=mi
36、n(dwBytesRead,(DWORD)ComStat.cbInQue);if(!dwBytesRead)return FALSE;BOOL bReadStatus;bReadStatus=ReadFile(hCom,lpInBuffer,dwBytesRead,&dwBytesRead,&m_osRead);if(!bReadStatus)/如果 ReadFile 函数返回 FALSEif(GetLastError()=ERROR_IO_PENDING)/GetLastError()函数返回 ERROR_IO_PENDING,表明串口正在进行读操作WaitForSingleObject(m
37、_osRead.hEvent,2000);/使用 WaitForSingleObject 函数等待,直到读操作完成或延时已达到 2 秒钟/当串口读操作进行完毕后,m_osRead 的 hEvent 事件会变为有信号PurgeComm(hCom,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);return dwBytesRead;return 0;PurgeComm(hCom,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);return dwBytesRead;对以
38、上代码再作简要说明:在使用 ReadFile函数进行读操作前,应先使用ClearCommError 函数清除错误。ClearCommError 函数的原型如下:BOOL ClearCommError(HANDLE hFile,/串口句柄LPDWORD lpErrors,/指向接收错误码的变量LPCOMSTAT lpStat/指向通讯状态缓冲区);该函数获得通信错误并报告串口的当前状态,同时,该函数清除串口的错误标志以便继续输入、输出操作。参数 lpStat 指向一个 COMSTAT 结构,该结构返回串口状态信息。COMSTAT 结构 COMSTAT 结构包含串口的信息,结构定义如下:typed
39、ef struct _COMSTAT /cstDWORD fCtsHold:1;/Tx waiting for CTS signalDWORD fDsrHold:1;/Tx waiting for DSR signalDWORD fRlsdHold:1;/Tx waiting for RLSD signalDWORD fXoffHold:1;/Tx waiting,XOFF char recdDWORD fXoffSent:1;/Tx waiting,XOFF char sentDWORD fEof:1;/EOF character sentDWORD fTxim:1;/character w
40、aiting for TxDWORD fReserved:25;/reservedDWORD cbInQue;/bytes in input bufferDWORD cbOutQue;/bytes in output buffer COMSTAT,*LPCOMSTAT;本文只用到了 cbInQue 成员变量,该成员变量的值代表输入缓冲区的字节数。最后用 PurgeComm 函数清空串口的输入输出缓冲区。这段代码用 WaitForSingleObject 函数来等待 OVERLAPPED 结构的 hEvent成员,下面我们再演示一段调用 GetOverlappedResult 函数等待的异步读串
41、口示例代码:char lpInBuffer1024;DWORD dwBytesRead=1024;BOOL bReadStatus;DWORD dwErrorFlags;COMSTAT ComStat;OVERLAPPED m_osRead;ClearCommError(hCom,&dwErrorFlags,&ComStat);if(!ComStat.cbInQue)return 0;dwBytesRead=min(dwBytesRead,(DWORD)ComStat.cbInQue);bReadStatus=ReadFile(hCom,lpInBuffer,dwBytesRead,&dwBy
42、tesRead,&m_osRead);if(!bReadStatus)/如果 ReadFile 函数返回 FALSEif(GetLastError()=ERROR_IO_PENDING)GetOverlappedResult(hCom,&m_osRead,&dwBytesRead,TRUE);/GetOverlappedResult 函数的最后一个参数设为 TRUE,/函数会一直等待,直到读操作完成或由于错误而返回。return dwBytesRead;return 0;return dwBytesRead;异步写串口的示例代码:char buffer1024;DWORD dwBytesWri
43、tten=1024;DWORD dwErrorFlags;COMSTAT ComStat;OVERLAPPED m_osWrite;BOOL bWriteStat;bWriteStat=WriteFile(hCom,buffer,dwBytesWritten,&dwBytesWritten,&m_OsWrite);if(!bWriteStat)if(GetLastError()=ERROR_IO_PENDING)WaitForSingleObject(m_osWrite.hEvent,1000);return dwBytesWritten;return 0;return dwBytesWrit
44、ten;(4)、关闭串口利用 API 函数关闭串口非常简单,只需使用 CreateFile 函数返回的句柄作为参数调用 CloseHandle 即可:BOOL CloseHandle(HANDLE hObject;/handle to object to close);串口编程的一个实例为了让您更好地理解串口编程,下面我们分别编写两个例程(见附带的源码部分),这两个例程都实现了工控机与百特显示仪表通过 RS485 接口进行的串口通信。其中第一个例程采用同步串口操作,第二个例程采用异步串口操作。我们只介绍软件部分,RS485 接口接线方法不作介绍,感兴趣的读者可以查阅相关资料。例程 1打开 VC
45、+6.0,新建基于对话框的工程 RS485Comm,在主对话框窗口IDD_RS485COMM_DIALOG 上添加两个按钮,ID 分别为 IDC_SEND 和IDC_RECEIVE,标题分别为“发送”和“接收”;添加一个静态文本框 IDC_DISP,用于显示串口接收到的内容。在 RS485CommDlg.cpp 文件中添加全局变量:HANDLE hCom;/全局变量,串口句柄在 RS485CommDlg.cpp 文件中的 OnInitDialog()函数添加如下代码:/TODO:Add extra initialization herehCom=CreateFile(COM1,/COM1 口G
46、ENERIC_READ|GENERIC_WRITE,/允许读和写0,/独占方式NULL,OPEN_EXISTING,/打开而不是创建0,/同步方式NULL);if(hCom=(HANDLE)-1)AfxMessageBox(打开 COM 失败!);return FALSE;SetupComm(hCom,100,100);/输入缓冲区和输出缓冲区的大小都是 1024COMMTIMEOUTS TimeOuts;/设定读超时TimeOuts.ReadIntervalTimeout=MAXDWORD;TimeOuts.ReadTotalTimeoutMultiplier=0;TimeOuts.Read
47、TotalTimeoutConstant=0;/在读一次输入缓冲区的内容后读操作就立即返回,/而不管是否读入了要求的字符。/设定写超时TimeOuts.WriteTotalTimeoutMultiplier=100;TimeOuts.WriteTotalTimeoutConstant=500;SetCommTimeouts(hCom,&TimeOuts);/设置超时DCB dcb;GetCommState(hCom,&dcb);dcb.BaudRate=9600;/波特率为 9600dcb.ByteSize=8;/每个字节有 8 位dcb.Parity=NOPARITY;/无奇偶校验位dcb.
48、StopBits=TWOSTOPBITS;/两个停止位SetCommState(hCom,&dcb);PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);分别双击 IDC_SEND 按钮和 IDC_RECEIVE 按钮,添加两个按钮的响应函数:void CRS485CommDlg:OnSend()/TODO:Add your control notification handler code here/在此需要简单介绍百特公司 XMA5000 的通讯协议:/该仪表 RS485 通讯采用主机广播方式通讯。/串行半双工,帧 11 位,1 个起始位(0),8 个数
49、据位,2 个停止位(1)/如:读仪表显示的瞬时值,主机发送:DC1 AAA BB ETX/其中:DC1 是标准 ASCII 码的一个控制符号,码值为 11H(十进制的 17)/在 XMA5000 的通讯协议中,DC1 表示读瞬时值/AAA 是从机地址码,也就是 XMA5000 显示仪表的通讯地址/BB 为通道号,读瞬时值时该值为 01/ETX 也是标准 ASCII 码的一个控制符号,码值为 03H/在 XMA5000 的通讯协议中,ETX 表示主机结束符char lpOutBuffer7;memset(lpOutBuffer,0,7);/前 7 个字节先清零lpOutBuffer0=x11;/
50、发送缓冲区的第 1 个字节为 DC1lpOutBuffer1=0;/第 2 个字节为字符 0(30H)lpOutBuffer2=0;/第 3 个字节为字符 0(30H)lpOutBuffer3=1;/第 4 个字节为字符 1(31H)lpOutBuffer4=0;/第 5 个字节为字符 0(30H)lpOutBuffer5=1;/第 6 个字节为字符 1(31H)lpOutBuffer6=x03;/第 7 个字节为字符 ETX/从该段代码可以看出,仪表的通讯地址为 001DWORD dwBytesWrite=7;COMSTAT ComStat;DWORD dwErrorFlags;BOOL b