很早以前就写好的代码,一时忘记要发了现在先补发下。
Windows串口通信差不多是对嵌入式系统开发的重要基础环节,也可以方便多机组网,所以在此请状况下,我就结合网上教程撸了一串代码供大家参考
C++真尼玛好玩!
一、代码核心函数
1.CreateFile()
HANDLE WINAPI CreateFile( _In_ LPCTSTR lpFileName, _In_ DWORD dwDesiredAccess, _In_ DWORD dwShareMode, _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, _In_ DWORD dwCreationDisposition, _In_ DWORD dwFlagsAndAttributes, _In_opt_ HANDLE hTemplateFile );
如执行成功,则返回文件句柄。INVALID_HANDLE_VALUE表示出错,会设置GetLastError。即使函数成功,但若文件存在,且指定了CREATE_ALWAYS 或 OPEN_ALWAYS,GetLastError也会设为ERROR_ALREADY_EXISTS
参数类型及说明
函数声明HANDLE CreateFile(LPCTSTR lpFileName, //普通文件名或者设备文件名
DWORD dwDesiredAccess, //访问模式(写/读) DWORD dwShareMode, //共享模式 LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全属性的指针 DWORD dwCreationDisposition, //如何创建 DWORD dwFlagsAndAttributes, //文件属性 HANDLE hTemplateFile //用于复制文件句柄 );
参数说明:
lpFileName String要打开的文件的名或设备名。这个字符串的最大长度在ANSI版本中为MAX_PATH,在unicode版本中为32767。
dwDesiredAccess指定类型的访问对象。如果为 GENERIC_READ 表示允许对设备进行读访问;如果为 GENERIC_WRITE 表示允许对设备进行写访问(可组合使用);如果为零,表示只允许获取与一个设备有关的信息 。
另外,还可以指定下面的控制标志:
标准控制权限(16-23位掩码):
DELETE 删除对象的权限。
READ_CONTROL 从对象的安全描述符中读取信息的权限,但不包括SACL(系统访问控制列表)中的信息。
WRITE_DAC 修改对象安全描述符中的DACL(随机访问控制列表)的权限
WRITE_OWNER 修改对象安全描述符中的属主的权限
SYNCHRONIZE 同步化使用对象的权限,即可以创建一个线程等待信号量释放(但有些对象不支持这个权限)。
STANDARD_RIGHTS_REQUIRED 等价于前面四种权限的总合(通常这四种是必须具有的权限)。
STANDARD_RIGHTS_READ 一般等价于READ_CONTROL
STANDARD_RIGHTS_WRITE 一般等价于READ_CONTROL
STANDARD_RIGHTS_EXECUTE 一般等价于READ_CONTROL
STANDARD_RIGHTS_ALL 等价于前面五种权限的总合。
特殊控制权限(0-15位掩码):
SPECIFIC_RIGHTS_ALL
ACCESS_SYSTEM_SECURITY
MAXIMUM_ALLOWED
GENERIC_READ
GENERIC_WRITE
GENERIC_EXECUTE
GENERIC_ALL
注:实质上是通过ACCESS_MASK结构体的一个双字值来设置标准权限、特殊权限和一般权限的。
dwShareModeLong, 如果是零表示不共享; 如果是FILE_SHARE_DELETE表示随后打开操作对象会成功只要删除访问请求;如果是FILE_SHARE_READ随后打开操作对象会成功只有请求读访问;如果是FILE_SHARE_WRITE 随后打开操作对象会成功只有请求写访问。
lpSecurityAttributesSECURITY_ATTRIBUTES, 指向一个SECURITY_ATTRIBUTES结构的指针,定义了文件的安全特性(如果操作系统支持的话)
dwCreationDispositionLong,下述常数之一:
CREATE_NEW 创建文件;如文件存在则会出错
CREATE_ALWAYS 创建文件,会改写前一个文件
OPEN_EXISTING 文件必须已经存在。由设备提出要求
OPEN_ALWAYS 如文件不存在则创建它
TRUNCATE_EXISTING 将现有文件缩短为零长度
dwFlagsAndAttributesLong, 一个或多个下述常数
FILE_ATTRIBUTE_ARCHIVE 标记归档属性
FILE_ATTRIBUTE_COMPRESSED 将文件标记为已压缩,或者标记为文件在目录中的默认压缩方式
FILE_ATTRIBUTE_NORMAL 默认属性
FILE_ATTRIBUTE_HIDDEN 隐藏文件或目录
FILE_ATTRIBUTE_READONLY 文件为只读
FILE_ATTRIBUTE_SYSTEM 文件为系统文件
FILE_FLAG_WRITE_THROUGH 操作系统不得推迟对文件的写操作
FILE_FLAG_OVERLAPPED 允许对文件进行重叠操作
FILE_FLAG_NO_BUFFERING 禁止对文件进行缓冲处理。文件只能写入磁盘卷的扇区块
FILE_FLAG_RANDOM_ACCESS 针对随机访问对文件缓冲进行优化
FILE_FLAG_SEQUENTIAL_SCAN 针对连续访问对文件缓冲进行优化
FILE_FLAG_DELETE_ON_CLOSE 关闭了上一次打开的句柄后,将文件删除。特别适合临时文件
也可在Windows NT下组合使用下述常数标记:
SECURITY_ANONYMOUS, SECURITY_IDENTIFICATION, SECURITY_IMPERSONATION, SECURITY_DELEGATION, SECURITY_CONTEXT_TRACKING, SECURITY_EFFECTIVE_ONLY
hTemplateFile,hTemplateFile为一个文件或设备句柄,表示按这个参数给出的句柄为模板创建文件(就是将该句柄文件拷贝到lpFileName指定的路径,然后再打开)。它将指定该文件的属性扩展到新创建的文件上面,这个参数可用于将某个新文件的属性设置成与现有文件一样,并且这样会忽略dwAttrsAndFlags。通常这个参数设置为NULL,为空表示不使用模板,一般为空。
返回值
如执行成功,则返回文件句柄。
INVALID_HANDLE_VALUE表示出错,会设置GetLastError。即使函数成功,但若文件存在,且指定了CREATE_ALWAYS 或 OPEN_ALWAYS,GetLastError也会设为ERROR_ALREADY_EXISTS。
2. SetupComm
该函数初始化一个指定的通信设备的通信参数。
BOOL SetupComm( HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue );
参数说明
hFile 通讯设备句柄。
CreateFile函数返回此句柄。
dwInQueue 指定推荐的大小,以字节为单位,对设备的内部输入缓冲区。
dwOutQueue 指定推荐的大小,以字节为单位,对设备的内部输出缓冲区。
返回值非零表示成功。零表示失败。 要获得更多错误信息,调用GetLastError函数
3.GetCommTimeouts
BOOL GetCommTimeouts( HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts );
typedef struct COMMTIMEOUTS { DWORD ReadIntervalTimeout; // 读间隔超时 DWORD ReadTotalTimeoutMultiplier; // 读时间系数 DWORD ReadTotalTimeoutConstant; // 读时间常量 DWORD WriteTotalTimeoutMultiplier; // 写时间系数 DWORD WriteTotalTimeoutConstant; // 写时间常量 } COMMTIMEOUTS,*LPCOMMTIMEOUTS;
COMMTIMEOUTS结构的成员都以毫秒为单位。
ReadIntervalTimeout:两字符之间最大的延时,当读取串口数据时,一旦两个字符传输的时间差超过该时间,读取函数将返回现有的数据。设置为0表示该参数不起作用。指定时间最大值(毫秒),允许接收的2个字节间有时间差。也就 是说,刚接收了一个字节后,等了ReadIntervalTimeout时间后还没有新的字节到达,就 认为本次读串口操作结束(后面的字节等下一次读取操作来处理)。即使你想读8个字节,但读第2个字节后,过了ReadIntervalTimeout时间后,第3个字节还没到。实际上就只读了2个字节。
ReadTotalTimeoutMultiplier:指定比例因子(毫秒),实际上是设置读取一个字节和等待下一个字节所需的时间,这样总的超时时间为读取的字节数乘以该值,同样一次读取操作到达这个时间后,也认为本次读操作己经结束。
ReadTotalTimeoutConstant:一次读取串口数据的固定超时。所以在一次读取串口的操作中,其超时为ReadTotalTimeoutMultiplier乘以读取的字节数再加上 ReadTotalTimeoutConstant。将ReadIntervalTimeout设置为MAXDWORD,并将ReadTotalTimeoutMultiplier 和ReadTotalTimeoutConstant设置为0,表示读取操作将立即返回存放在输入缓冲区的字符。可以理解为一个修正时间,实际上就是按ReadTotalTimeoutMultiplier计算出的超时时间再加上该时间才作为整个超时时间。
WriteTotalTimeoutMultiplier:写入每字符间的超时。
WriteTotalTimeoutConstant:一次写入串口数据的固定超时。所以在一次写入串口的操作中,其超时为WriteTotalTimeoutMultiplier乘以写入的字节数再加上 WriteTotalTimeoutConstant。
一般都会做以下设置:
TimeOuts.ReadIntervalTimeout=MAXDWORD;
// 把间隔超时设为最大,把总超时设为0将导致ReadFile立即返回并完成操作
TimeOuts.ReadTotalTimeoutMultiplier=0;
//读时间系数
TimeOuts.ReadTotalTimeoutConstant=0;
//读时间常量
TimeOuts.WriteTotalTimeoutMultiplier=50;
//总超时=时间系数*要求读/写的字符数+时间常量
TimeOuts.WriteTotalTimeoutConstant=2000;
//设置写超时以指定WriteComm成员函数中的
总超时的计算公式是:
总超时=时间系数×要求读/写的字符数 + 时间常量
例如,如果要读入10个字符,那么读操作的总超时的计算公式为:
读总超时=ReadTotalTimeoutMultiplier×10 + ReadTotalTimeoutConstant
可以看出,间隔超时和总超时的设置是不相关的,这可以方便通信程序灵活地设置各种超时。如果所有写超时参数均为0,那么就不使用写超时。如果ReadIntervalTimeout为0,那么就不使用读间隔超时,如果
ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant都为0,则不使用读总超时。如果读间隔超时被设置成MAXDWORD并且两个读总超时为0,那么在读一次输入缓冲区中的内容后读操作就立即完成,而不管是否读入了要求的字符。 在用重叠方式读写串行口时,虽然ReadFile和WriteFile在完成操作以前就可能返回,但超时仍然是起作用的。在这种情况下,超时规定的是操作的完成时间,而不是ReadFile和WriteFile的返回时间。
二、代码
#include "stdafx.h" //#include "测试串口.h" #include "stdio.h" #include "windows.h" HANDLE portfp; static char* speed; int _Open(char* com); LPCWSTR _Change(char* arvg); int _WriteDate(char* Date); int _ReadDate(); int _SetCom(char* _nBaud); int main(int argc, char* argv[]) { if (0==strcmp(argv[1], "help")) { printf("ZZF的串口工具帮助信息\n参数1.串口号\n参数2.波特率\n参数3.发送的字节\n"); return 0; } _Open(argv[1]); _SetCom(argv[2]); _WriteDate(argv[3]); //Sleep(100); _ReadDate(); CloseHandle(portfp); // speed =argv[3]; return 0; } int _Open(char* com) //打开文件OR串口 { portfp = CreateFile(_Change(com), // 普通文件名或者设备文件名 GENERIC_WRITE | GENERIC_READ, // 访问模式 读写 0, // 不共享 NULL, // 无安全指针 OPEN_ALWAYS, // 始终创建 FILE_ATTRIBUTE_NORMAL, // 文件属性 NULL); // 无复制文件句柄 if (portfp == INVALID_HANDLE_VALUE)//确认句柄是否无效 { printf("CreateFile() error:%d", GetLastError()); exit(0); } return 1; } int _SetCom(char* _nBaud) { //设置读写缓冲区大小 static const int g_nZhenMax = 32768; if (!SetupComm(portfp, g_nZhenMax, g_nZhenMax))//初始化通信端口 { printf("SetupComm() failed"); CloseHandle(portfp); return false; } //设置串口配置信息 DCB dcb;//定义串口通讯中的DCB结构 if (!GetCommState(portfp, &dcb))//读取串口设置(波特率,校验,停止位,数据位等),此处用于确认通信串口是否打开 { printf("GetCommState() failed"); CloseHandle(portfp); return false; }/*定义DCB*/ dcb.BaudRate = atol(_nBaud);//波特率 dcb.Parity = NOPARITY;//校验方式为无校验 dcb.ByteSize = 8;//数据位为8位 dcb.StopBits = ONESTOPBIT;//停止位为1位 if (!SetCommState(portfp, &dcb))//设置串口设置(波特率, 校验, 停止位, 数据位等).并确认是否成功设置 { printf("SetCommState() failed"); CloseHandle(portfp); return false; } COMMTIMEOUTS timeouts;//设置超时才 GetCommTimeouts(portfp, &timeouts); timeouts.ReadIntervalTimeout = 0;// 读间隔超时 timeouts.ReadTotalTimeoutMultiplier =10;// 读时间系数 timeouts.ReadTotalTimeoutConstant = 0; // 读时间常量 timeouts.WriteTotalTimeoutMultiplier = 0; // 写时间系数 timeouts.WriteTotalTimeoutConstant = 20; // 写时间常量 SetCommTimeouts(portfp, &timeouts); } LPCWSTR _Change(char* argv)// LPCWSTR 转换子程序 ATL要勾选 { CString str = CString(argv); USES_CONVERSION; LPCWSTR wszClassName = new WCHAR[str.GetLength() + 1]; wcscpy((LPTSTR)wszClassName, T2W((LPTSTR)str.GetBuffer(NULL))); str.ReleaseBuffer(); return wszClassName; } int _WriteDate(char* Date) //写入数据 { BOOL bErrorFlag = FALSE; DWORD dwBytesToWrite = (DWORD)strlen(Date); DWORD dwBytesWritten = 0; bErrorFlag = WriteFile( portfp, //串口 Date, //写入数据 dwBytesToWrite, //写的字节数 &dwBytesWritten,//用于保存实际写入字节数的存储区域的指针 NULL); if (FALSE == bErrorFlag) { printf("Terminal failure: Unable to write to file.\n"); } else { if (dwBytesWritten != dwBytesToWrite) { printf("Error: dwBytesWritten != dwBytesToWrite\n"); } else { printf("Wrote \" %s \" successfully.\n", Date); } } return 0; } int _ReadDate() { char str[51]; DWORD wCount;//读取的字节数 BOOL bReadStat; memset(str, 0, sizeof(str)); bReadStat = ReadFile(portfp, str, 50, &wCount, NULL); if (!bReadStat) { // AfxMessageBox("读串口失败!"); return FALSE; } printf("%s\n", str); }
三、结果分析
数据处理速度比不过一般串口软件,但是胜在有源码,可以改
最后生成的是命令行程序
串口工具帮助信息 参数1.串口号 2.波特率 3.发送的字节