基于VC++6.0的串口通信开发方法
1 引言
在当今的工业控制领域,串口通信是计算机与其他设备进行数据通信时经常使用的方法,具有实现简单、使用灵活、数据传输可靠等几个优点,特别是在实时监控系统中得到广泛应用,在我们使用的计算机上使用的串口一般是RS232,使用RS232接口只能进行一对一的通信,然而在工业控制领域往往是一台工控机和多台智能设备进行通信,并且要求传输距离远,因为这些需求,在工控领域一般使用RS485。
在Win32下,可以使用两种编程方式实现串口通信,其一是使用MScomm控件,这种方法程序简单,但欠灵活。其二是调用Windows的API函数,这种方法可以清楚地掌握串口通信的机制,并且自由灵活。使用控件的方法在本质上也是使用API进行串口通信,控件只不过是对API的一个封装处理,本文只介绍使用API进行串口通信编程的方法。
2 串口通信的一般步骤
2.1 打开串口
在32位Windows中,串口和其他通信设备(如磁盘等)都被作为文件进行处理,在使用前必须先将其打开,为保证串口通信数据传输的可靠性,串口一般以非共享模式打开,也就是在被串口打开后,其他程序不能在去打开此设备。
2.2 配置串口
在使用串口进行数据通信前必须对其进行正确的配置,串口需要配置的主要参数有波特率、数据位、停止位、奇偶校验、收发数据缓冲区大小。除此之外还要对串口进行超时设置,以防止在串口通信时数据传输突然中断而导致读写操作进入无限期等待的状态,设置了超时,如果在指定时间内没有完成所进行的操作,则此操作被自动放弃。
2.3 读写串口
在串口被打开并设置好后,就可以使用串口进行读写数据了,读写数据可以采用同步、异步及事件驱动等多种方式。
2.4 关闭串口
在使用完串口后应该将其关闭,如果没有关闭,该串口会处于打开状态,其他的应用程序便无法打开使用该串口。
3 利用API函数实现串口通信
3.1 打开串口
Win32系统把文件的概念进行了扩展。无论是文件、通信设备、命名管道、邮件槽、磁盘、还是控制台,都是用API函数CreateFile来打开或创建的。该函数的原型为:
HANDLE CreateFile(LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDistribution,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);
各个参数说明如下:
lpFileName:将要打开的串口逻辑名,如“COM
dwDesiredAccess:指定串口访问的类型,可以是读取、写入或二者并列;
dwShareMode:指定共享属性,由于串口不能共享,该参数必须置为0;
lpSecurityAttributes:引用安全性属性结构,缺省值为NULL;
dwCreationDistribution:创建标志,对串口操作该参数必须置为OPEN_EXISTING;
dwFlagsAndAttributes:属性描述,用于指定该串口是否进行异步操作,该值为FILE_FLAG_OVERLAPPED,表示使用异步的I/O;该值为0,表示同步I/O操作;
hTemplateFile:对串口而言该参数必须置为NULL;
3.2 配置串口
在打开通讯设备句柄后,常常需要对串口进行一些初始化配置工作。这需要通过一个DCB结构来进行。DCB结构包含了诸如波特率、数据位数、奇偶校验和停止位数等信息。在查询或配置串口的属性时,都要用DCB结构来作为缓冲区。在打开串口后,可以调用GetCommState函数来获取串口的默认配置,该函数获取一个DCB结构体,只要在该结构内对应该先修改DCB结构,然后再调用SetCommState函数以修改后的DCB结构设置串口。DCB主要有以下几个重要的成员:
BYTE ByteSize; // 通信字节位数
BYTE Parity; //指定奇偶校验方法。此成员可以有下列值:
//EVENPARITY 偶校验 NOPARITY 无校验
//MARKPARITY 标记校验 ODDPARITY 奇校验
BYTE StopBits; //指定停止位的位数。此成员可以有下列值:
//ONESTOPBIT 1位停止位
//TWOSTOPBITS 2位停止位
//ONE5STOPBITS 1.5位停止位
除了使用BCD设置串口的一些基本参数外,一般还需要设置串口收发数据缓冲区的大小和超时,超时的作用是在指定的时间内没有读入或发送指定数量的字符,读写操作仍然会结束。Windows用I/O缓冲区来暂存串口输入和输出的数据,如果通信的速率较高,则应该设置较大的缓冲区。我们可以使用API函数SetupComm设置串口的输入和输出缓冲区的大小,其原型如下:
BOOL SetupComm(
HANDLE hFile, // 串口句柄
DWORD dwInQueue, // 输入缓冲区的大小(字节数)
DWORD dwOutQueue ); // 输出缓冲区的大小(字节数)
关于读写串口的超时设置,windows给我们提供一个专门的结构体COMMTIMEOUTS,其定义如下:
typedef struct _COMMTIMEOUTS {
DWORD ReadIntervalTimeout; //读间隔超时
DWORD ReadTotalTimeoutMultiplier; //读时间系数
DWORD ReadTotalTimeoutConstant; //读时间常量
DWORD WriteTotalTimeoutMultiplier; //写时间系数
DWORD WriteTotalTimeoutConstant; //写时间常量
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;
COMMTIMEOUTS结构的成员都以毫秒为单位。总超时的计算公式是:
总超时=时间系数×要求读/写的字符数+时间常量
例如,要读入10个字符,那么读操作的总超时的计算公式为:
读总超时=ReadTotalTimeoutMultiplier×10+ReadTotalTimeoutConstant
通过该结构体windowsAPI为我们提供两个函数:GetCommTimeouts和SetCommTimeouts,前者获取当前的超时设置,后者使用修改后的COMMTIMEOUTS设置超时,与设置串口阐述类似。
在读写串口之前,还要用PurgeComm(…)函数清空缓冲区,该函数原型:
BOOL PurgeComm(
HANDLE hFile, //串口句柄
DWORD dwFlags ); //需要完成的操作
参数dwFlags指定要完成的操作,可以是下列值的组合:
PURGE_TXABORT 中断所有写操作并立即返回,即使写操作还没有完成。
PURGE_RXABORT 中断所有读操作并立即返回,即使读操作还没有完成。
PURGE_TXCLEAR 清除输出缓冲区
PURGE_RXCLEAR 清除输入缓冲区
3.3 读写串口
读写串口使用ReadFile和WriteFile两个函数,其原型如下:
BOOL ReadFile(
HANDLE hFile, //串口的句柄
LPVOID lpBuffer, //保存读入数据的指针
DWORD nNumberOfBytesToRead, //要读入的数据的字节数
LPDWORD lpNumberOfBytesRead, //实际读入的字节数
LPOVERLAPPED lpOverlapped ); //OVERLAPPED,同步为NULL
BOOL WriteFile(
HANDLE hFile, //串口的句柄
LPCVOID lpBuffer, //要写入数据的地址
DWORD nNumberOfBytesToWrite, //要写入数据的字节数
LPDWORD lpNumberOfBytesWritten, //实际写入的字节数
LPOVERLAPPED lpOverlapped); //OVERLAPPED,同步为NULL
在进行同步操作时,读写函数要等到执行完才返回,而在异步操作时函数立即返回,但不保证读写操作完成,这时候就需要使用OVERLAPPED结构进行异步控制,该结构体有一个重要的成员hEvent,该成员是windows事件对象的句柄在控制线程同步及异步操作时常用到,如果是异步操作,我们可以使用CreateEvent(…)创建事件对象并将返回值赋给hEvent,然后使用WaitForSingleObject或GetOverlappedResult等待读写操作完成,进而达到控制异步操作的目的。
3.4 关闭串口
在不使用串口的时候应该将其关闭,以释放windows的资源供其他程序使用,关闭串口只需调用CloseHandle(hComm/*串口句柄*/)即可。
4 串行通信在世纪星组态软件中的应用
作为通用的组态软件,世纪星要与其他PLC、智能仪表等设备进行通信,串行通信是主要的方式之一,基于前面所述使用API进行串行通信开发的优点,并考虑程序开发的便捷和可重用等,在世纪星中,我们将串行通信API进行封装,以类的方式对串口进行操作,其中打开串口及配置串口参数的操作我们通过可视化窗口进行设定,然后在封装类中实现,相关的操作处理读写数据外基本都已实现,因为不同的设备有不同的协议,因而读写串口的操作在驱动程序中完成,这样我们的开发人员就不必关注太多其他的相关操作,只需根据实际设备重写读写串口的成员函数即可。
5 结论
文章版权归西部工控xbgk所有,未经许可不得转载。