1 串口通信的基本原理

串口通信中无论是写入串口还是读取串口,都是对缓冲区操作的。可以理解为写串口就是向输出缓冲区写入内容,读取串口就是从输入串口缓冲区读取内容。但是何时打开串口,何时发送数据,何时接受数据都是未知的。所以在串口通信时一般是一个主动一个被动。通信双方有一定的协议,就是事先协商好的数据格式。接收方接收到数据后,返回一个应答标志,告诉发送方已经接收到数据了。如果接收错误则返回接收错误标志,或者一直等待接收到正确的数据再返回。至于接收方怎么才知道有数据发送过来了,对于单片机之类的可以使用串口中断或者巡检的方式。对于工控机之类只能使用巡检的方式了。前段时间做一个检测系统,用到串口通信,顺手就写了一个类,这里分享出来。

2 串口通信的基本操作步骤

无论哪种操作方式,串口通信一般都通过四个步骤来完成:

1、打开串口;

2、配置串口;

3、读写串口;

4、关闭串口;

2.1 打开串口

在Windows中使用串口通信一般有两种方式,一种是使用Windows中的API,另一种方式使用MFC中的控件。这里采用API的方式。

HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDistribution,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);

lpFileName,要打开的串口号,如“COM1”;

dwDesiredAccess,串口访问的类型,可以是只读、只写、可读可写。其取值可以是GENERIC_READ、GENERIC_WRITE或者他们的组合;

dwShareMode,共享属性,由于串口不能共享,该参数必须置为0;

lpSecurityAttributes,引用的安全类型,一般设置为NULL;

dwCreationDistribution,创建文件的标志,对于串口操作该参数必须置为OPEN_EXISTING;

dwFlagsAndAttributes,串口通信是同步还是异步,0表示同步。FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED表示异步;

hTemplateFile:对串口而言该参数必须置为NULL。

异步方式打开串口示例代码:

CreateFile(
m_strCom,
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
NULL);
2.2 配置串口

串口打开需要配置一些参数,如DCB结构、输入输出缓冲区大小、设置超时结构。

配置DCB结构,该结构中可以配置波特率、数据位、奇偶校验和停止位之类的信息;

设置该结构的时候需要用到几个函数:

BOOL GetCommState(HANDLE hFile, LPDCB lpDCB);
BOOL SetCommState(HANDLE hFile, LPDCB lpDCB);

示例代码如下:

DCB dcb;
GetCommState(m_hCom, &dcb);
dcb.BaudRate = m_dwBaudRate;
dcb.ByteSize = m_byteSize;
dcb.Parity = m_byteCheck;
dcb.StopBits = m_byteStop;
SetCommState(m_hCom, &dcb);

设置串口缓冲区大小:

BOOL SetupComm( HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue);

示例代码如下:

DWORD dwInQueue = 1024;
DEWORD dwOutQueue = 1024;
SetupComm(hCom, dwInQueue, dwOutQueue);

设置超时:

BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts);

该函数第一个参数不用多说,第二个参数是个结构体。

typedef struct _COMMTIMEOUTS {
DWORD ReadIntervalTimeout;
DWORD ReadTotalTimeoutMultiplier;
DWORD ReadTotalTimeoutConstant;
DWORD WriteTotalTimeoutMultiplier;
DWORD WriteTotalTimeoutConstant;
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

ReadIntervalTimeout,读取操作过程中两个字符之间的延时,当读取串口的时候如果两个字符传输的时间差如果超过这个时间的话,读取串口函数就会返回;

ReadTotalTimeoutMultiplier,读取操作计算总超时时每个字符读取的时间,其实就是估算的每个字符传输需要的时间;

ReadTotalTimeoutConstant,一次串口读取超时时间的固定值,其实就是担心估计的两个字符之间传输时间不准确,然后又加上的一个超时时间;

读取总超时时间的计算方法如下:

读取操作总超时 = ReadTotalTimeoutMultiplier*读取字符数 + ReadTotalTimeoutConstant;

读取串口的时候有两种超时,一种是两个传输字符之间的时间间隔;如果读取两个字符之间的时间超过ReadIntervalTimeout的话,读取串口的操作就会返回。另一种是读取总时间超时,如果读取操作时间超过刚计算的总超时的话,读取操作也会返回;这里说的返回与串口的同步操作和异步操作中说的返回不同。同步和异步那种返回是指函数的返回,这里的返回是指串口读取操作的返回;

WriteTotalTimeoutMultiplier,同读操作相关的参数;

WriteTotalTimeoutConstant,同读操作相关参数;

写操作总超时时间计算方法如下:

写操作总超时 = WriteTotalTimeoutMultiplier*写入字符数 + WriteTotalTimeoutConstant;

写入操作只有一种超时,只有总超时;

一般做以下设置:

TimeOuts.ReadIntervalTimeout = MAXDWORD;     // 把间隔超时设为最大,
//把总超时设为0将导致ReadFile立即返回并完成操作
TimeOuts.ReadTotalTimeoutMultiplier = 0; //读时间系数
TimeOuts.ReadTotalTimeoutConstant = 0; //读时间常量
TimeOuts.WriteTotalTimeoutMultiplier = 50; //总超时=时间系数*要求读/写的字符数+时间常量
TimeOuts.WriteTotalTimeoutConstant = 2000; //设置写超时以指定WriteComm成员函数中的

这样设置后读取完所有字符后就会返回,写完操作后也会返回;

2.3 读写串口

使用两个函数ReadFile和WriteFile。

ReadFile:

BOOL ReadFile(
HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped);

hFile,要读取串口的句柄;

lpBuffer,要接收数据的缓冲区;

nNumberOfBytesToRead,要读取数据的字节数;

lpNumberOfBytesRead,DWORD指针,保存实际读入的数据的个数;

lpOverlapped,OVERLAPPED结构体,如果是同步串口通信串口设置为NULL。异步串口通信操作需要一个OVERLAPPED结构体指针;

异步读取数据示例代码如下:

DWORD dwRead;//这个值需要根据实际要读取的数据
DWORD dwReadTrue = 0;
BOOL bRead;
//清除错误标志
COMSTAT ComStat;
DWORD dwErrorFlags;
ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
dwRead = (dwLength <= ComStat.cbInQue)?dwLength:ComStat.cbInQue;
if ( !dwRead ) return MAXDWORD;//输入缓存区里面没有内容
OVERLAPPED osRead;
memset(&osRead, 0, sizeof(OVERLAPPED));
HANDLE hEventRecv = CreateEvent(NULL, TRUE, FALSE, NULL);//这个事件必须为手动复位
osRead.hEvent = hEventRecv;
bRead = ReadFile(m_hCom, pBuffer, dwRead, &dwReadTrue, &osRead);
if ( !bRead && (ERROR_IO_PENDING == GetLastError()) )//读操作未完成
{
GetOverlappedResult(m_hCom, &osRead, &dwReadTrue, TRUE);//等待读操作完成,暂时这样操作
PurgeComm(m_hCom, PURGE_RXABORT|PURGE_RXCLEAR);
ResetEvent(m_hEventRecv);
return dwReadTrue;
}
else if ( !bRead )
{
ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
PurgeComm(m_hCom, PURGE_RXABORT|PURGE_RXCLEAR);
return MAXDWORD;
}
return dwReadTrue;

WriteFile:

BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped);

hFile,要写入的串口句柄;

lpBuffer,要写入的数据;

nNumberOfBytesToWrite,要写入数据的字节数;

lpNumberOfBytesWritten,一个DWORD指针,实际写入数据字节数;

lpOverlapped,OVERLAPPED结构体,如果是同步串口通信串口设置为NULL。异步串口通信操作需要一个OVERLAPPED结构体指针;

写入数据示例代码如下:

DWORD dwToWrite = dwLength;
DWORD dwWritten = 0;
BOOL bWrite;
COMSTAT ComStat;
DWORD dwErrorFlags;
ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
OVERLAPPED osWrite;
memset(&osWrite, 0, sizeof(OVERLAPPED));
HANDLE hEventSend = CreateEvent(NULL, TRUE, FALSE, NULL);//这个事件必须为手动复位
osWrite.hEvent = hEventSend;
bWrite = WriteFile(m_hCom, pBuffer, dwToWrite, &dwWritten, &osWrite);
if ( !bWrite && (ERROR_IO_PENDING == GetLastError()) )//串口写操作未完成
{
GetOverlappedResult(
m_hCom,
&osWrite,
&dwWritten,
TRUE);//等待写操作完成,这里暂时使用这种操作方式
PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
ResetEvent(m_hEventSend);
return dwWritten;
}
else if ( !bWrite )//串口写入错误
{
ClearCommError( m_hCom, &dwErrorFlags, &ComStat );
PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
return MAXDWORD;
}
return dwWritten;
2.4 关闭串口

串口属于系统资源,打开了使用后要关闭掉。调用以下函数就行了:

BOOL CloseHandle(HANDLE hObject);
2.5 还有些需要用到的函数
BOOL PurgeComm(HANDLE hFile, DWORD dwFlags);

参数dwFlags指定要完成的操作,可以如下值:

PURGE_TXABORT 无论串口处于什么状态,中断所有写操作并立即返回;

PURGE_RXABORT 无论串口处于什么状态,中断所有读操作并立即返回;

PURGE_TXCLEAR 清空输出缓冲区;

PURGE_RXCLEAR 清空输入缓冲区;

3 CSerial成员函数和成员变量

为了方便使用,对串口的一些API函数进行了封装:CSerial类。下表中有CSerial类的说明,具体的使用会有一个使用案例。CSerial类是对串口使用的一个封装,主要包括打开串口,串口读写功能。该类使用比较简单,不用复杂的配置,那俩可以马上使用。还可以共享串口,不同的CSerial对象可以共享同一个串口,类中有一个对串口的引用计数,当与该串口绑定的对象都析构后会自动关闭该串口。类成员函数如下表:

成员函数 说明
CSerial(); 构造函数;
CSerial(CString strCom, DWORD dwBaudRate = 9600, BYTE byteSize = 8, BYTE byteCheck = NOPARITY, BYTE byteStop = ONESTOPBIT, BOOL bSync = FALSE); 构造函数;
WriteData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout = 1000); 向串口写入数据
ReadData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout = 1000); 从串口读出数据;
ClearError(); 清除串口错误,该函数会清除串口错误标志位。
Purge(); 同API函数中的PurgeComm()
SetComString(CString strCom); 设置串口号,比如“COM1”。只有当实例化CSerial对象是没有传入串口号或者想要修改串口号的时候使用该函数;
SetBaudRate(DWORD dwBaudRate); 设置串口通信的波特率,默认为9600;
SetByteSize(BYTE byteSize); 设置串口通信的数据位,默认为8位;
SetCheck(BYTE byteCheck); 设置串口通信的奇偶校验,默认为不校验;
SetStopBit(BYTE byteStopBit); 设置串口通信的停止位,默认为一位停止位;
SetSync(BOOL bSync); 设置是串口通信是同步还是异步,默认是同步通信;
SetInQue(DWORD dwInQue); 设置输入缓冲区大小,默认为1024;
SetOutQue(DWORD dwOutQue); 设置输出缓冲区大小,默认为1024;
SetTimeouts(COMMTIMEOUTS timeouts); 设置超时设置,如果更改默认超时设置的时候,调用该函数;
GetComString(); 获取串口通信的串口号;
GetBaudRate(); 获取串口通信的波特率;
GetByteSize(); 获取串口通信的数据位数;
GetCheck(); 获取串口通信是否使用奇偶校验;
GetStopBit(); 获取串口通信使用了几位停止位;
GetSync(); 获取串口通信的通信方式是同步还是异步;
GetInQue(); 获取串口通信的输入缓冲区大小;
GetOutQue(); 获取串口通信的输出缓冲区大小;
GetComStatus(); 获取串口的状态,一般查看串口是否正确打开;

4 使用说明

-------------------------------
CSerial();

构造函数,当实例化对象的时候,不传入参数则调用该函数。如果不传入参数该对象不能用来通信,需要通过SetComString(CString strCom);函数来设置该对象的串口号来正常通信;

-------------------------------
CSerial(
CString strCom,
DWORD dwBaudRate = 9600,
BYTE byteSize = 8,
BYTE byteCheck = NOPARITY,
BYTE byteStop = ONESTOPBIT,
BOOL bSync = FALSE);

构造函数,需要传入串口号。其他参数都有默然值。如果需要更改的话,可以通过Setxx()函数来设置相应的参数;

-------------------------------
DWORD WriteData(
unsigned char *pBuffer,
DWORD dwLength,
DWORD dwTimeout = 1000);

pBuffer,要写入的数据;

dwLength,写入数据的长度;

dwTimeout,异步通信的时超时时间,暂时没有使用,可以不管;

-------------------------------
DWORD ReadData(
unsigned char *pBuffer,
DWORD dwLength,
DWORD dwTimeout = 1000);

pBuffer,读取数据缓冲区;

dwLength,读取数据长度;

dwTimeout,异步通信时的超时时间,暂时没有使用可以不管;

-------------------------------
void Purge(DWORD dwFlags);

与API中的PurgeComm函数功能一样,dwFlags的取值可以是PURGE_TXABORT、PUTGE_RXABORT、PURGE_TXCLEAR、PURGE_RXCLEAR或者他们的组合;

-------------------------------

使用CSerial进行串口通信的例子:

CSerial SerialMeter("COM1");
DWORD dwWritten,dwReadTrue;
unsigned char TxData[11];
unsigned char RxData[11] = {0};
dwWritten = SerialMeter.WriteData(TxData, 11);
if( MAXDWORD == dwWritten )
{
SerialMeter.ClearError();
return;
}
Sleep(100);
dwReadTrue = m_SerialMeter.ReadData(RxData,11);
if ( MAXDWORD == dwReadTrue )
{
m_SerialMeter.ClearError();
return;
}

串口共享的时候只需要把一个对象赋值给另一个对象,或者用另一个对象来初始化一个对象就可以了。其他的就像使用单独的一个对象一样。串口的打开和关闭由类自己管理。串口共享串口例子:

CSerial SerialMeter("COM1");
CSerial SeralVelocty = SerialMeter;
DWORD dwWritten,dwReadTrue;
unsigned char TxData[11];
unsigned char RxData[11] = {0};
// 第一个串口对象读写
dwWritten = SerialMeter.WriteData(TxData, 11);
if( MAXDWORD == dwWritten )
{
SerialMeter.ClearError();
return;
}
Sleep(100);
dwReadTrue = m_SerialMeter.ReadData(RxData,11); if ( MAXDWORD == dwReadTrue )
{
m_SerialMeter.ClearError();
return;
}
// 另一个串口对象读写
Sleep(100);
dwWritten = SeralVelocty.WriteData(TxData, 11);
if( MAXDWORD == dwWritten )
{
SeralVelocty.ClearError();
return;
}
Sleep(100);
dwReadTrue = SeralVelocty.ReadData(RxData,11);
if ( MAXDWORD == dwReadTrue )
{
SeralVelocty.ClearError();
return;
}

源代码如下:

头文件:

//CSerial类头文件
#pragma once class CSerial
{
public:
CSerial();
CSerial(
CString strCom,
DWORD dwBaudRate = 9600,
BYTE byteSize = 8,
BYTE byteCheck = NOPARITY,
BYTE byteStop = ONESTOPBIT,
BOOL bSync = FALSE);
~CSerial();
inline CSerial(const CSerial &com)
{
m_hCom = com.m_hCom;
m_hEventSend = com.m_hEventSend;
m_hEventRecv = com.m_hEventRecv;
m_bSync = com.m_bSync;
m_bIsFirst = com.m_bIsFirst;
m_strCom = com.m_strCom;
m_dwBaudRate = com.m_dwBaudRate;
m_byteSize = com.m_byteSize;
m_byteCheck = com.m_byteCheck;
m_byteStop = com.m_byteStop;
m_dwInQueue = com.m_dwInQueue;
m_dwOutQueue = com.m_dwOutQueue;
m_Timeouts = com.m_Timeouts;
m_nInitResult = com.m_nInitResult;
m_pnReusecount = com.m_pnReusecount;
if ( m_pnReusecount )
{
(* m_pnReusecount)++;
}
}
inline CSerial & operator = (const CSerial &com)
{
m_hCom = com.m_hCom;
m_hEventSend = com.m_hEventSend;
m_hEventRecv = com.m_hEventRecv;
m_bSync = com.m_bSync;
m_bIsFirst = com.m_bIsFirst;
m_strCom = com.m_strCom;
m_dwBaudRate = com.m_dwBaudRate;
m_byteSize = com.m_byteSize;
m_byteCheck = com.m_byteCheck;
m_byteStop = com.m_byteStop;
m_dwInQueue = com.m_dwInQueue;
m_dwOutQueue = com.m_dwOutQueue;
m_Timeouts = com.m_Timeouts;
m_nInitResult = com.m_nInitResult;
m_pnReusecount = com.m_pnReusecount;
if ( m_pnReusecount )
{
(* m_pnReusecount)++;
}
return * this;
}
private:
int *m_pnReusecount;
BOOL m_bIsFirst; //是否是第一次成功打开串口
HANDLE m_hCom; //串口句柄
HANDLE m_hEventSend; //发送数据事件
HANDLE m_hEventRecv; //接收数据事件 BOOL m_bSync; //同步传输还是异步传输,TRUE则为同步,FALSE为异步,默认为异步 CString m_strCom; //串口端口
DWORD m_dwBaudRate; //波特率
BYTE m_byteSize; //数据位
BYTE m_byteCheck; //校验方式
BYTE m_byteStop; //停止位 DWORD m_dwInQueue; //串口输入缓冲区
DWORD m_dwOutQueue; //串口输出缓冲区
//超时相关变量
COMMTIMEOUTS m_Timeouts; int m_nInitResult;
public:
DWORD WriteData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout = 1000); //dwTimeout为占位符,暂时未用,返回,MAXDWORD表示写入错误
DWORD ReadData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout = 1000); //dwTimeout为占位符,暂时未用,返回,MAXDWORD表示读取错误
void CloseCom();
int InitCom();//返回1,表示没有错误,返回其他表示错误
void ClearError();
void Purge(DWORD dwFlags); int SetComString(CString strCom);
int SetBaudRate(DWORD dwBaudRate);
int SetByteSize(BYTE byteSize);
int SetCheck(BYTE byteCheck);
int SetStopBit(BYTE byteStopBit);
int SetSync(BOOL bSync);
int SetInQue(DWORD dwInQue);
int SetOutQue(DWORD dwOutQue);
int SetTimeouts(COMMTIMEOUTS timeouts); CString GetComString();
DWORD GetBaudRate();
BYTE GetByteSize();
BYTE GetCheck();
BYTE GetStopBit();
BOOL GetSync();
DWORD GetInQue();
DWORD GetOutQue();
int GetComStatus();//InitCom的返回值,观察串口是否打开成功,0表示没有串口名称,1表示打开成功,MAXINI32表示串口打开错误
};

源文件:

#include "stdafx.h"
#include "Serial.h" CSerial::CSerial()
{
m_pnReusecount = NULL;
m_hCom = NULL;
m_hEventRecv = NULL;
m_hEventSend = NULL;
m_strCom = _T("");
m_dwBaudRate = 9600;
m_byteSize = 8;
m_byteCheck = NOPARITY;
m_byteStop = ONESTOPBIT;
m_bSync = TRUE;
m_bIsFirst = TRUE;
//缓冲区变量初始化
m_dwInQueue = 1024;
m_dwOutQueue = 1024;
//超时变量初始化
COMMTIMEOUTS timeout = {MAXDWORD, 0, 0, 50, 1000};
m_Timeouts = timeout;
} CSerial::CSerial(
CString strCom,
DWORD dwBaudRate,
BYTE byteSize,
BYTE byteCheck,
BYTE byteStop,
BOOL bSync)
{
m_pnReusecount = NULL;
m_hCom = NULL;
m_hEventRecv = NULL;
m_hEventSend = NULL;
m_strCom = strCom;
m_dwBaudRate = dwBaudRate;
m_byteSize = byteSize;
m_byteCheck = byteCheck;
m_byteStop = byteStop;
m_bSync = bSync;
m_bIsFirst = TRUE;
//缓冲区变量初始化
m_dwInQueue = 1024;
m_dwOutQueue = 1024;
//超时变量初始化
COMMTIMEOUTS timeout = {MAXDWORD, 0, 0, 50, 1000};
m_Timeouts = timeout;
m_nInitResult = InitCom();
} CSerial::~CSerial()
{
if (m_pnReusecount)
{
(* m_pnReusecount)--;
if( 0 >= *m_pnReusecount )
{
CloseCom();
delete m_pnReusecount;
}
}
} void CSerial::CloseCom()
{
if ( m_hEventRecv )
{
CloseHandle(m_hEventRecv);
m_hEventRecv = NULL;
}
if ( m_hEventSend )
{
CloseHandle(m_hEventSend);
m_hEventSend = NULL;
}
if ( m_hCom )
{
CloseHandle(m_hCom);
m_hCom = NULL;
}
} int CSerial::InitCom()
{
if ( m_strCom == _T("") ) return 0;//如果串口传入为空,则进行串口初始化
if ( m_hCom )
{
CloseHandle(m_hCom);
}
if ( m_bSync )
{
m_hCom = CreateFile(m_strCom, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
}
else
{
m_hCom = CreateFile(m_strCom, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, NULL);
}
if ( m_hCom == INVALID_HANDLE_VALUE )//串口打开失败
{
m_hCom = NULL;
return 2;
}
//设置缓冲区大小,默认为1024;
SetupComm(m_hCom, m_dwInQueue, m_dwOutQueue);
//超时设置
SetCommTimeouts(m_hCom, &m_Timeouts);
//配置串口
DCB dcb;
GetCommState(m_hCom, &dcb);
dcb.BaudRate = m_dwBaudRate;
dcb.ByteSize = m_byteSize;
dcb.Parity = m_byteCheck;
dcb.StopBits = m_byteStop;
SetCommState(m_hCom, &dcb);
PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_RXCLEAR);
if ( m_bIsFirst )
{
m_pnReusecount = new int;
* m_pnReusecount = 1;
m_bIsFirst = FALSE;
}
return 1;
} void CSerial::ClearError()
{
COMSTAT ComStat;
DWORD dwErrorFlags;
ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_RXCLEAR);
if ( m_hEventRecv ) ResetEvent(m_hEventRecv);
if ( m_hEventSend ) ResetEvent(m_hEventSend);
} DWORD CSerial::WriteData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout)
{
if ( !m_hCom ) return MAXDWORD;
DWORD dwToWrite = dwLength;
DWORD dwWritten = 0;
BOOL bWrite;
COMSTAT ComStat;
DWORD dwErrorFlags;
ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
if ( m_bSync )//同步
{
bWrite =WriteFile(m_hCom, pBuffer, dwToWrite, &dwWritten, NULL);
if ( bWrite )
{
return dwWritten;
}
else
{
return MAXDWORD;
}
}
else//异步
{
OVERLAPPED osWrite;
memset(&osWrite, 0, sizeof(OVERLAPPED));
if ( !m_hEventSend )
{
m_hEventSend = CreateEvent(NULL, TRUE, FALSE, NULL);//这个事件必须为手动复位
}
osWrite.hEvent = m_hEventSend;
bWrite = WriteFile(m_hCom, pBuffer, dwToWrite, &dwWritten, &osWrite);
if ( !bWrite && (ERROR_IO_PENDING == GetLastError()) )//串口写操作未完成
{
GetOverlappedResult(m_hCom, &osWrite, &dwWritten, TRUE);//等待写操作完成,这里暂时使用这种操作方式
PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
ResetEvent(m_hEventSend);
return dwWritten;
}
else if ( !bWrite )//串口写入错误
{
ClearCommError( m_hCom, &dwErrorFlags, &ComStat );
PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
return MAXDWORD;
}
return dwWritten;
}
} DWORD CSerial::ReadData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout)
{
if ( !m_hCom ) return MAXDWORD;
DWORD dwRead;
DWORD dwReadTrue = 0;
BOOL bRead;
//清除错误标志
COMSTAT ComStat;
DWORD dwErrorFlags;
ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
dwRead = (dwLength <= ComStat.cbInQue)?dwLength:ComStat.cbInQue;
if ( !dwRead ) return MAXDWORD;//输入缓存区里面没有内容
if ( m_bSync )//同步
{
bRead = ReadFile(m_hCom, pBuffer, dwRead, &dwReadTrue, NULL);
if ( bRead )
{
return dwReadTrue;
}
else
{
return MAXDWORD;
}
}
else//异步
{
OVERLAPPED osRead;
memset(&osRead, 0, sizeof(OVERLAPPED));
if ( !m_hEventRecv )
{
m_hEventRecv = CreateEvent(NULL, TRUE, FALSE, NULL);//这个事件必须为手动复位
}
osRead.hEvent = m_hEventRecv;
bRead = ReadFile(m_hCom, pBuffer, dwRead, &dwReadTrue, &osRead);
if ( !bRead && (ERROR_IO_PENDING == GetLastError()) )//读操作未完成
{
GetOverlappedResult(m_hCom, &osRead, &dwReadTrue, TRUE);//等待读操作完成,暂时这样操作
PurgeComm(m_hCom, PURGE_RXABORT|PURGE_RXCLEAR);
ResetEvent(m_hEventRecv);
return dwReadTrue;
}
else if ( !bRead )
{
ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
PurgeComm(m_hCom, PURGE_RXABORT|PURGE_RXCLEAR);
return MAXDWORD;
}
return dwReadTrue;
}
} void CSerial::Purge(DWORD dwFlags)
{
PurgeComm(m_hCom, dwFlags);
} int CSerial::SetComString(CString strCom)
{
CString strTemp = m_strCom;
m_strCom = strCom;
m_nInitResult = InitCom();
if ( 1 != m_nInitResult )
{
m_strCom = strTemp;
return -1;
}
return 0;
} int CSerial::SetBaudRate(DWORD dwBaudRate)
{
DWORD dwTemp = m_dwBaudRate;
m_dwBaudRate = dwBaudRate;
m_nInitResult = InitCom();
if ( 1 != m_nInitResult )
{
m_dwBaudRate = dwTemp;
return -1;
}
return 0;
} int CSerial::SetByteSize(BYTE byteSize)
{
BYTE byteTemp = m_byteSize;
m_byteSize = byteSize;
m_nInitResult = InitCom();
if ( 1 != m_nInitResult )
{
m_byteSize = byteTemp;
return -1;
}
return 0;
} int CSerial::SetCheck(BYTE byteCheck)
{
BYTE byteTemp = m_byteCheck;
m_byteCheck = byteCheck;
m_nInitResult = InitCom();
if ( 1 != m_nInitResult )
{
m_byteCheck = byteTemp;
return -1;
}
return 0;
} int CSerial::SetStopBit(BYTE byteStopBit)
{
BYTE byteTemp = m_byteStop;
m_byteStop = byteStopBit;
m_nInitResult = InitCom();
if ( 1 != m_nInitResult )
{
m_byteStop = byteTemp;
return -1;
}
return 0;
} int CSerial::SetSync(BOOL bSync)
{
BOOL bTemp = m_bSync;
m_bSync = bSync;
m_nInitResult = InitCom();
if ( 1 != m_nInitResult )
{
m_bSync = bTemp;
return -1;
}
return 0;
} int CSerial::SetInQue(DWORD dwInQue)
{
DWORD dwTemp = m_dwInQueue;
m_dwInQueue = dwInQue;
m_nInitResult = InitCom();
if ( 1 != m_nInitResult )
{
m_dwInQueue = dwTemp;
return -1;
}
return 0;
} int CSerial::SetOutQue(DWORD dwOutQue)
{
DWORD dwTemp = m_dwOutQueue;
m_dwOutQueue = dwOutQue;
m_nInitResult = InitCom();
if ( 1 != m_nInitResult )
{
m_dwOutQueue = dwTemp;
return -1;
}
return 0;
} int CSerial::SetTimeouts(COMMTIMEOUTS timeouts)
{
COMMTIMEOUTS timeoutTemp = m_Timeouts;
m_Timeouts = timeouts;
m_nInitResult = InitCom();
if ( 1 != m_nInitResult )
{
m_Timeouts = timeoutTemp;
return -1;
}
return 0;
} CString CSerial::GetComString()
{
return m_strCom;
} DWORD CSerial::GetBaudRate()
{
return m_dwBaudRate;
} BYTE CSerial::GetByteSize()
{
return m_byteSize;
} BYTE CSerial::GetCheck()
{
return m_byteCheck;
} BYTE CSerial::GetStopBit()
{
return m_byteStop;
} BOOL CSerial::GetSync()
{
return m_bSync;
} DWORD CSerial::GetInQue()
{
return m_dwInQueue;
} DWORD CSerial::GetOutQue()
{
return m_dwOutQueue;
} int CSerial::GetComStatus()
{
return m_nInitResult;
}

串口使用和CSerial类的更多相关文章

  1. 多线程串口编程工具CserialPort类(附VC基于MFC单文档协议通讯源程序及详细编程步骤)

    老有人觉得MSComm通讯控件很土,更有人大声疾呼:忘了它吧.确实当我们对串口编程有了一定的了解后,应该用API函数写一个属于自己的串口程序,由于编程者对程序了解,对程序修改自如.但我一直没有停止过用 ...

  2. C#串口操作类,包括串口读写操作

    串口进行操作的类,其中包括写和读操作,类可设置串口参数.设置接收函数.打开串口资源.关闭串口资源,操作完成后,一定要关闭串口.接收串口数据事件.接收数据出错事件.获取当前全部串口.把字节型转换成十六进 ...

  3. VS2015 +Qt5 串口工具

    简单的小工具是VS2015 + Qt5.6.1实现的,界面部分是Qt实现,串口是封装的WinAPI,把串口收发模块封装成了个Serialport.dll 供Qt界面调用. 由于VS2015需要CRT运 ...

  4. SerialPort类的用法与示例

    转:https://www.cnblogs.com/hwBeta/p/6926363.html Microsoft .Net框架SerialPort类的用法与示例 从Microsoft .Net 2. ...

  5. [C#] Microsoft .Net框架SerialPort类的用法与示例

    从Microsoft .Net 2.0版本以后,就默认提供了System.IO.Ports.SerialPort类,用户可以非常简单地编写少量代码就完成串口的信息收发程序.本文将介绍如何在PC端用C# ...

  6. Qt中的串口编程之一

    QtSerialPort 简介 功能介绍 SerialPort SerialPortInfo 源代码 编译和安装 配置编译环境 Perl只是在Qt5的时候才需要Qt4的情况下可以不配置 使用如下推荐步 ...

  7. 用C#实现通过串口对设备的数据采集--Server层

    今天中午没睡午觉,头昏眼花的,实在写不了代码,把这几天写的Server层数据采集的程序整理了一下. WatrLevelDataCollectServer.cs using System; using ...

  8. Visual studio之C# 串口通讯SerialPort

    背景 App需要串口进行通讯,在此做个记录和简要说明. 正文 添加命名空间 using System.IO.Ports; 实例化串口 SerialPort serialPortO = new Seri ...

  9. java读写串口数据

    本博文参考自https://www.cnblogs.com/Dreamer-1/p/5523046.html 最近接触到了串口及其读写,在此记录java进行串口读写的过程. 1.导入串口支持包 需要下 ...

随机推荐

  1. 安装tensorflow遇到:Your CPU supports instructions that this TensorFlow binary was not compiled to use

    为了提升CPU计算速度的.若你有支持cuda的GPU,则可以忽略这个问题,因为安装SSE4.1, SSE4.2, AVX, AVX2, FMA, 仅仅提升CPU的运算速度(大概有3倍). 解决方法: ...

  2. 持久化存储与HTTP缓存

    本文主要学习一下一些高级的HTTP知识,例如Session LocalStorage Cache-Control Expires ETag 其实主要就是涉及到了持久化存储与缓存的技术 在此之前已经学习 ...

  3. 原生 JS实现一个简单分页插件

    最近做的一个 PC端的需求,这个需求中有一个小点,页面底部有一块列表区域,这个列表的数据量比较大,需要进行分页控制,切换页码的时候,发送一个 ajax请求,在页面无刷新的情况下,实现列表数据的刷新,所 ...

  4. Acwing-201-可见的点(数学, 欧拉函数)

    链接: https://www.acwing.com/problem/content/description/203/ 题意: 在一个平面直角坐标系的第一象限内,如果一个点(x,y)与原点(0,0)的 ...

  5. vue学习时遇到的问题(二)

    1. this.$nextTick veu中进行数据改变后,并不会马上刷新视图:用nextTick可告诉执行下个函数后马上刷新视图: this.$nextTick(function(){     // ...

  6. C# 多线程任务分配辅助类

    1)首先实现一个多线程的辅助类,代码如下: public class ThreadMulti { public delegate void DelegateComplete(); public del ...

  7. 小程序开发之后台SSM环境搭建(一)

    1.新建web项目 打开eclipse,选择file-->New-->Dynamic web Project ,填写项目名字,一直点击next,勾选Generate web.xml dep ...

  8. SSH整合框架

    实现登录.新闻增删改查.树形菜单 引入pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi ...

  9. python socket.io 坑。

    python下star最高的是https://github.com/miguelgrinberg/python-socketio 是flask作者写的.client server都有了,而且还提供了a ...

  10. 7.Java Web的数据库操作

    一.环境配置(基于MySQL数据库) 1.下载MySQL数据库 2.下载安装 Navicat,破解方法去吾爱破解网站查询 第一次连接mysql时可能会出现错误,可能是因为二者对密码的编码方法不一致,可 ...