源码出处也不知!
H文件:
#pragma once
#include <string>
#include <vector>
using namespace std;
class CSerial485
{
public:
CSerial485(void);
~CSerial485(void);
public:
/** 初始化串口函数
* @param: UINT portNo
* @param: UINT baud 波特率,
* @param: char parity 是否进行奇偶校验,
* @param: UINT databits 数据位的个数,默认值为8个数据位
* @param: UINT stopsbits 停止位使用格式,默认值为1
* @param: DWORD dwCommEvents 默认为EV_RXCHAR,即只要收发任意一个字符,则产生一个事件
* @return: BOOL 初始化是否成功
* @note: 在使用其他本类提供的函数前,请先调用本函数进行串口的初始化
* 本函数提供了一些常用的串口参数设置,若需要自行设置详细的DCB参数,可使用重载函数
*本串口类析构时会自动关闭串口,无需额外执行关闭串口
*/ //EVENPARITY
BOOL InitPort(UINT portNo = 0, UINT baud = CBR_115200, char parity = NOPARITY, UINT databits = 8, UINT stopsbits = ONESTOPBIT);
/** 向串口写数据
* 将缓冲区中的数据写入到串口
* @param: unsigned char * pData 指向需要写入串口的数据缓冲区
* @param: unsigned int iLen 需要写入的数据长度
* @return: BOOL 操作是否成功
* @note: length不要大于pData所指向缓冲区的大小
*/
BOOL WriteData(char* pData, int iLen);
BOOL ReadData(char* pData, int iLen);
/** 关闭串口
* @return: void 操作是否成功
*/
void ClosePort();
//获取全部串口
UINT EnumSerial(vector<string>& listCom);
//是否已打开
BOOL IsConnect();
//清空读取数据
void ClearRead();
wchar_t* Multi2WideChar(const string& pKey);
private:
/** 打开串口
* @param: UINT portNo 串口设备号
* @return: BOOL 打开是否成功
*/
BOOL openPort(UINT portNo);
/** 获取串口缓冲区中的字节数
* @return: UINT 操作是否成功
* @note: 当串口缓冲区中无数据时,返回0
*/
UINT GetBytesInCOM();
HANDLE m_hComm;
};
CPP文件:
#include "stdafx.h"
#include <process.h>
#include "Serial_485.h"
// 当串口无数据时,sleep至下次查询间隔的时间,单位:秒
const UINT SLEEP_TIME_INTERVAL = 1;
CSerial485::CSerial485(void)
{
m_hComm = INVALID_HANDLE_VALUE;
}
CSerial485::~CSerial485(void)
{
ClosePort();
}
BOOL CSerial485::InitPort(UINT portNo, UINT baud, char parity, UINT databits, UINT stopsbits)
{
if (!openPort(portNo))
{
return false;
}
DCB dcb;
BOOL bRet = FALSE;
// 在此可以设置输入输出的缓冲区大小,如果不设置,则系统会设置默认值.
// 自己设置缓冲区大小时,要注意设置稍大一些,避免缓冲区溢出
// 设置串口的超时时间,均设为0,表示不使用超时限制
COMMTIMEOUTS CommTimeouts;
CommTimeouts.ReadIntervalTimeout = 100;
CommTimeouts.ReadTotalTimeoutMultiplier = 10;
CommTimeouts.ReadTotalTimeoutConstant = 100;
CommTimeouts.WriteTotalTimeoutMultiplier = 10;
CommTimeouts.WriteTotalTimeoutConstant = 100;
if (SetCommTimeouts(m_hComm, &CommTimeouts))
{
// 获取当前串口配置参数,并且构造串口DCB参数
bRet = GetCommState(m_hComm, &dcb);
if (bRet)// 使用DCB参数配置串口状态
{
// 开启RTS flow控制
dcb.fRtsControl = RTS_CONTROL_ENABLE;
dcb.BaudRate = baud;
dcb.fParity = 1;
dcb.Parity = parity;
dcb.ByteSize = databits;
if (dcb.StopBits != stopsbits)
{
dcb.StopBits = stopsbits;
}
bRet = SetCommState(m_hComm, &dcb);
if (bRet) // 清空串口缓冲区
{
bRet = PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
}
}
}
if (!bRet)
{
ClosePort();
}
return bRet;
}
void CSerial485::ClosePort()
{
if (m_hComm != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hComm);
m_hComm = INVALID_HANDLE_VALUE;
}
}
BOOL CSerial485::openPort(UINT portNo)
{
// 把串口的编号转换为设备名
wchar_t szPort[50];
if (portNo >= 10)
{
wsprintf(szPort, _T("\\\\.\\COM%d"), portNo);
}
else
{
wsprintf(szPort, _T("COM%d"), portNo);
}
// 打开指定的串口
m_hComm = CreateFile(szPort, // 设备名,COM1,COM2等
GENERIC_READ | GENERIC_WRITE, // 访问模式,可同时读写
0, // 共享模式,0表示不共享
NULL, // 安全性设置,一般使用NULL
OPEN_EXISTING, // 该参数表示设备必须存在,否则创建失败
0, 0);
return m_hComm != INVALID_HANDLE_VALUE;
}
BOOL CSerial485::WriteData(char* pData, int length)
{
DWORD BytesToSend = 0;
if (m_hComm == INVALID_HANDLE_VALUE)
{
return false;
}
ClearRead();
BOOL bRet = WriteFile(m_hComm, pData, length, &BytesToSend, NULL);
if (!bRet)
{
PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);
}
return bRet;
}
BOOL CSerial485::ReadData(char* pData, int iLen)
{
if (INVALID_HANDLE_VALUE == m_hComm)
{
return FALSE;
}
//读取头信息 RS485CMDHEAD_ST
int iTimes = 0;
while (TRUE)
{
if (iLen <= GetBytesInCOM())
{
break;
}
if (10 <= iTimes++)
{
return FALSE;
}
Sleep(100);
}
DWORD BytesRead = 0;
BOOL bResult = ReadFile(m_hComm, pData, iLen, &BytesRead, NULL);
if (!bResult)// 清空串口缓冲区
{
PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);
ClosePort();
}
return bResult;
}
UINT CSerial485::GetBytesInCOM()
{
DWORD dwError = 0; // 错误码
UINT BytesInQue = 0;
COMSTAT comstat; // COMSTAT结构体,记录通信设备的状态信息
memset(&comstat, 0, sizeof(COMSTAT));
// 在调用ReadFile和WriteFile之前,通过本函数清除以前遗留的错误标志
if (ClearCommError(m_hComm, &dwError, &comstat))
{
BytesInQue = comstat.cbInQue; // 获取在输入缓冲区中的字节数
}
return BytesInQue;
}
BOOL CSerial485::IsConnect()
{
return m_hComm != INVALID_HANDLE_VALUE;
}
void CSerial485::ClearRead()
{
if (INVALID_HANDLE_VALUE == m_hComm)
{
return;
}
char pBuf[128];
int iBufLen = 128;
while (0 < GetBytesInCOM())
{
DWORD BytesRead = 0;
BOOL bResult = ReadFile(m_hComm, pBuf, iBufLen, &BytesRead, NULL);
Sleep(20);
}
}
wchar_t* CSerial485::Multi2WideChar(const string& pKey)
{
const char* pCStrKey = pKey.c_str();
//第一次调用返回转换后的字符串长度,用于确认为wchar_t*开辟多大的内存空间
int pSize = MultiByteToWideChar(CP_OEMCP, 0, pCStrKey, strlen(pCStrKey) + 1, NULL, 0);
wchar_t *pWCStrKey = new wchar_t[pSize];
//第二次调用将单字节字符串转换成双字节字符串
MultiByteToWideChar(CP_OEMCP, 0, pCStrKey, strlen(pCStrKey) + 1, pWCStrKey, pSize);
return pWCStrKey;
}
void Wide2MultiByte(std::string& szDst, wchar_t* wchar)
{
wchar_t * wText = wchar;
DWORD dwNum = WideCharToMultiByte(CP_OEMCP, NULL, wText, -1, NULL, 0, NULL, FALSE);//WideCharToMultiByte的运用
char* pDest = new char[dwNum];
WideCharToMultiByte(CP_OEMCP, NULL, wText, -1, pDest, dwNum, NULL, FALSE);//WideCharToMultiByte的再次运用
szDst = pDest;// std::string赋值
delete []pDest;// psText的清除
}
UINT CSerial485::EnumSerial(vector<string>& listCom)
{
HKEY hKey;
LPCTSTR lpSubKey = _T("HARDWARE\\DEVICEMAP\\SERIALCOMM\\");
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpSubKey, 0, KEY_READ, &hKey))
{
return 0;
}
int iSerialComNum = 0;
const int NAME_LEN = 100;
wchar_t szValueName[NAME_LEN];
BYTE szPortName[NAME_LEN];
LONG status;
DWORD dwIndex = 0;
DWORD dwSizeValueName = NAME_LEN;
DWORD dwSizeofPortName = NAME_LEN;
DWORD Type;
dwSizeValueName = NAME_LEN;
dwSizeofPortName = NAME_LEN;
do
{
//每读取一次dwSizeValueName和dwSizeofPortName都会被修改
//注意一定要重置,否则会出现很离奇的错误,本人就试过因没有重置,出现读不了COM大于10以上的串口
dwSizeValueName = NAME_LEN;
dwSizeofPortName = NAME_LEN;
status = RegEnumValue(hKey, dwIndex++, szValueName, &dwSizeValueName, NULL, &Type, szPortName, &dwSizeofPortName);
if ((status == ERROR_SUCCESS))
{
string strName;
Wide2MultiByte(strName, (wchar_t*)szPortName);
listCom.push_back(strName);
iSerialComNum++;
}
} while ((status != ERROR_NO_MORE_ITEMS));
RegCloseKey(hKey);
return iSerialComNum;
}