串口通信(Win32, C++)

简单封装Win32串口, 采用异步读写方式

CSerialPort.h

#pragma once

#include <string>
#include <windows.h>
#include <tchar.h>

#ifdef _UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endif

class CSerialPort
{
public:
    CSerialPort();
    CSerialPort(const CSerialPort& r) = delete;
    ~CSerialPort();

    // 打开串口
    bool Open(
        int nComm,                      //串口号
        DWORD dwBaudRate = CBR_115200,  //波特率
        BYTE bByteSize = 8,             //数据位
        BYTE bParity = NOPARITY,        //校验位
        BYTE bStopBits = ONESTOPBIT     //停止位
    );

    // 打开串口
    bool Open(
        const _tstring& strName = _T("COM1"),        //串口名
        DWORD dwBaudRate = CBR_115200,  //波特率
        BYTE bByteSize = 8,             //数据位
        BYTE bParity = NOPARITY,        //校验位
        BYTE bStopBits = ONESTOPBIT     //停止位
    );

    // 检测是否已经打开
    bool IsOpen() const;

    // 设置状态配置
    bool SetState(
        DWORD dwBaudRate = CBR_115200,  //波特率
        BYTE bByteSize = 8,             //数据位
        BYTE bParity = NOPARITY,        //校验位
        BYTE bStopBits = ONESTOPBIT     //停止位
    );

    // 设置状态配置
    bool SetState(const LPDCB lpDcb);

    // 设置缓冲区大小
    bool SetupComm(
        DWORD dwInQueue = 14,   // 输入缓冲区 1 - 14
        DWORD dwOutQueue = 16   // 输出缓冲区 1 - 16
    );

    // 设置超时配置
    bool SetTimeOut(
        DWORD ReadInterval = 50,          // 读间隔超时
        DWORD ReadTotalMultiplier = 5,   // 读时间系数
        DWORD ReadTotalConstant = 1000,            // 读时间常量
        DWORD WriteTotalMultiplier = 10,         // 写时间系数
        DWORD WriteTotalConstant = 200            // 写时间常量
    );

    // 设置超时配置
    bool SetTimeOut(const LPCOMMTIMEOUTS lpTimeOuts);

    // 清空缓冲区
    bool Purge(
        DWORD  dwFlags = PURGE_TXCLEAR | PURGE_RXCLEAR
    );

    // 清除错误
    bool ClearError();

    // 关闭串口
    void Close();

    // 发送数据
    bool Write(
        LPCVOID lpData, 
        DWORD cbSize,
        LPDWORD lpBytesWritten = nullptr,
        DWORD nTimeOut = 1000
    );

    // 接收数据
    bool Read(
        LPVOID lpData, 
        DWORD cbSize,
        LPDWORD lpBytesRead = nullptr,
        DWORD nTimeOut = 3000
    );

private:

    // 打开串口
    bool OpenComm(int nComm);

    // 打开串口
    bool OpenComm(const _tstring& strName);

    // 等待重叠操作完成
    bool WaitForOverlapped(
        HANDLE hFile, 
        LPOVERLAPPED lpOv, 
        LPDWORD lpBytesTransferred, 
        DWORD nTimeOut
    );

private:

    HANDLE m_hComm;             // 串口句柄
    HANDLE m_hReadEvent;        // 重叠读事件句柄
    HANDLE m_hWriteEvent;       // 重叠写事件句柄
    bool m_bExit;               // 退出标记
};

CSerialPort.cpp

#include "CSerialPort.h"
#include <strsafe.h>

CSerialPort::CSerialPort()
    :
    m_hComm(INVALID_HANDLE_VALUE),
    m_hReadEvent(nullptr),
    m_hWriteEvent(nullptr),
    m_bExit(false)
{

}

CSerialPort::~CSerialPort()
{
    Close();
}

// 打开串口
bool CSerialPort::Open(
    int nComm,
    DWORD dwBaudRate/* = CBR_115200*/,
    BYTE bByteSize/* = 8*/,
    BYTE bParity/* = NOPARITY*/,
    BYTE bStopBits/* = ONESTOPBIT*/
)
{
    if (!OpenComm(nComm))
    {
        return false;
    }

    if (!SetState(dwBaudRate, bByteSize, bParity, bStopBits))
    {
        this->Close();
        return false;
    }

    m_bExit = false;
    return true;
}

// 打开串口
bool CSerialPort::Open(
    const _tstring& strName/* = _T("COM1")*/,               //串口名
    DWORD dwBaudRate/* = CBR_115200*/,
    BYTE bByteSize/* = 8*/,
    BYTE bParity/* = NOPARITY*/,
    BYTE bStopBits/* = ONESTOPBIT*/
)
{
    if (!OpenComm(strName))
    {
        return false;
    }

    if (!SetState(dwBaudRate, bByteSize, bParity, bStopBits))
    {
        this->Close();
        return false;
    }

    m_bExit = false;
    return true;
}

bool CSerialPort::IsOpen() const
{
    return INVALID_HANDLE_VALUE != m_hComm;
}

bool CSerialPort::SetState(
    DWORD dwBaudRate/* = CBR_115200*/,
    BYTE bByteSize/* = 8*/,
    BYTE bParity/* = NOPARITY*/,
    BYTE bStopBits/* = ONESTOPBIT*/
)
{
    if (INVALID_HANDLE_VALUE == m_hComm)
    {
        return false;
    }

    DCB dcb = { 0 };
    dcb.DCBlength = sizeof(DCB);

    if (!::GetCommState(m_hComm, &dcb))
    {
        return false;
    }

    dcb.DCBlength = sizeof(DCB);
    dcb.BaudRate = dwBaudRate;
    dcb.ByteSize = bByteSize;
    dcb.Parity = bParity;
    dcb.StopBits = bStopBits;

    return ::SetCommState(m_hComm, &dcb);
}

bool CSerialPort::SetState(const LPDCB lpDcb)
{
    if (nullptr == lpDcb)
    {
        return false;
    }

    return ::SetCommState(m_hComm, lpDcb);
}

// 设置缓冲区大小
bool CSerialPort::SetupComm(
    DWORD dwInQueue/* = 14*/,
    DWORD dwOutQueue/* = 16*/
)
{
    if (INVALID_HANDLE_VALUE == m_hComm)
    {
        return false;
    }

    return ::SetupComm(m_hComm, dwInQueue, dwOutQueue);
}

bool CSerialPort::SetTimeOut(
    DWORD ReadInterval/* = 50*/,
    DWORD ReadTotalMultiplier/* = 50*/,
    DWORD ReadTotalConstant/* = 100*/,
    DWORD WriteTotalMultiplier/* = 50*/,
    DWORD WriteTotalConstant/* = 200*/
)
{
    if (INVALID_HANDLE_VALUE == m_hComm)
    {
        return false;
    }

    COMMTIMEOUTS comTimeOuts = { 0 };

    if (!::GetCommTimeouts(m_hComm, &comTimeOuts))
    {
        return false;
    }

    comTimeOuts.ReadIntervalTimeout = ReadInterval;
    comTimeOuts.ReadTotalTimeoutMultiplier = ReadTotalMultiplier;
    comTimeOuts.ReadTotalTimeoutConstant = ReadTotalConstant;
    comTimeOuts.WriteTotalTimeoutMultiplier = WriteTotalMultiplier;
    comTimeOuts.WriteTotalTimeoutConstant = WriteTotalConstant;
    return ::SetCommTimeouts(m_hComm, &comTimeOuts);
}

bool CSerialPort::SetTimeOut(const LPCOMMTIMEOUTS lpTimeOuts)
{
    if (nullptr == lpTimeOuts)
    {
        return false;
    }

    return ::SetCommTimeouts(m_hComm, lpTimeOuts);
}

bool CSerialPort::Purge(
    DWORD  dwFlags/* = PURGE_TXCLEAR | PURGE_RXCLEAR*/
)
{
    if (INVALID_HANDLE_VALUE == m_hComm)
    {
        return false;
    }

    return ::PurgeComm(m_hComm, dwFlags);
}

bool CSerialPort::ClearError()
{
    if (INVALID_HANDLE_VALUE == m_hComm)
    {
        return false;
    }

    DWORD dwError = 0;
    COMSTAT comStat = { 0 };

    return ::ClearCommError(m_hComm, &dwError, &comStat);
}

void CSerialPort::Close()
{
    m_bExit = true;
    if (nullptr != m_hReadEvent)
    {
        ::SetEvent(m_hReadEvent);
        ::CloseHandle(m_hReadEvent);
        m_hReadEvent = nullptr;
    }

    if (nullptr != m_hWriteEvent)
    {
        ::SetEvent(m_hWriteEvent);
        ::CloseHandle(m_hWriteEvent);
        m_hWriteEvent = nullptr;
    }

    if (INVALID_HANDLE_VALUE != m_hComm)
    {
        ::CancelIoEx(m_hComm, nullptr);
        ::CloseHandle(m_hComm);
        m_hComm = INVALID_HANDLE_VALUE;
    }
}

bool CSerialPort::OpenComm(int nComm)
{
    TCHAR szBuf[MAX_PATH];
    (void)::StringCchPrintf(szBuf, _countof(szBuf), _T(R"(\\.\COM%d)"), nComm);

    // 关闭前一个已打开的串口
    this->Close();

    m_hComm = ::CreateFile(
        szBuf,
        GENERIC_READ | GENERIC_WRITE,   //读写串口
        0,                              //独占打开
        nullptr,
        OPEN_EXISTING,                  //必须存在
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,           //重叠操作
        nullptr
    );

    if (INVALID_HANDLE_VALUE == m_hComm)
    {
        return false;
    }

    m_hReadEvent = CreateEvent(nullptr, false, false, nullptr);
    if (nullptr == m_hReadEvent)
    {
        this->Close();
        return false;
    }

    m_hWriteEvent = CreateEvent(nullptr, false, false, nullptr);
    if (nullptr == m_hWriteEvent)
    {
        this->Close();
        return false;
    }

    return true;
}

bool CSerialPort::OpenComm(const _tstring& strName)
{
    TCHAR szBuf[MAX_PATH];
    (void)::StringCchPrintf(szBuf, _countof(szBuf), _T(R"(\\.\%s)"), strName.c_str());

    // 关闭前一个已打开的串口
    this->Close();

    m_hComm = ::CreateFile(
        szBuf,
        GENERIC_READ | GENERIC_WRITE,   //读写串口
        0,                              //独占打开
        nullptr,
        OPEN_EXISTING,                  //必须存在
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,           //重叠操作
        nullptr
    );

    if (INVALID_HANDLE_VALUE == m_hComm)
    {
        return false;
    }

    m_hReadEvent = CreateEvent(nullptr, false, false, nullptr);
    if (nullptr == m_hReadEvent)
    {
        this->Close();
        return false;
    }

    m_hWriteEvent = CreateEvent(nullptr, false, false, nullptr);
    if (nullptr == m_hWriteEvent)
    {
        this->Close();
        return false;
    }

    return true;
}

bool CSerialPort::WaitForOverlapped(
    HANDLE hFile,
    LPOVERLAPPED lpOv,
    LPDWORD lpBytesTransferred,
    DWORD nTimeOut
)
{
    bool bResult = false;
    DWORD dwBytesTransferred = 0;
    DWORD dwWait = ::WaitForSingleObject(lpOv->hEvent, nTimeOut);

    switch (dwWait)
    {
    case WAIT_OBJECT_0:
    {
        if (::GetOverlappedResult(hFile,
            lpOv,
            &dwBytesTransferred,
            true
        ))
        {
            bResult = true;
        }
        else
        {
            bResult = false;
        }

        if (lpBytesTransferred)
        {
            *lpBytesTransferred = dwBytesTransferred;
        }
    }
    break;
    case WAIT_TIMEOUT:
        break;
    default:
        break;
    }

    if (!bResult && !m_bExit)
    {
        ::CancelIoEx(hFile, lpOv);
    }

    return bResult;
}

bool CSerialPort::Write(
    LPCVOID lpData,
    DWORD cbSize,
    LPDWORD lpBytesWritten,
    DWORD nTimeOut/* = 1000*/
)
{
    if (INVALID_HANDLE_VALUE == m_hComm ||
        nullptr == lpData ||
        0 == cbSize ||
        m_bExit)
    {
        return false;
    }

    OVERLAPPED ov = { 0 };
    BOOL bResult = FALSE;
    ov.hEvent = m_hWriteEvent;

    do
    {
        bResult = ::WriteFile(
            m_hComm,
            lpData,
            cbSize,
            lpBytesWritten,
            &ov
        );

        if (bResult)
        {
            break;
        }

        if (ERROR_IO_PENDING != ::GetLastError())
        {
            break;
        }

        bResult = WaitForOverlapped(m_hComm, &ov, lpBytesWritten, nTimeOut);

    } while (false);

    return bResult;
}

bool CSerialPort::Read(
    LPVOID lpData,
    DWORD cbSize,
    LPDWORD lpBytesRead/* = nullptr*/,
    DWORD nTimeOut/* = 3000*/
)
{
    if (INVALID_HANDLE_VALUE == m_hComm ||
        nullptr == lpData ||
        0 == cbSize || 
        m_bExit)
    {
        return false;
    }

    OVERLAPPED ov = { 0 };
    BOOL bResult = FALSE;
    ov.hEvent = m_hReadEvent;

    do
    {
        bResult = ::ReadFile(
            m_hComm,
            lpData,
            cbSize,
            lpBytesRead,
            &ov
        );

        if (bResult)
        {
            break;
        }

        if (ERROR_IO_PENDING != ::GetLastError())
        {
            break;
        }

        bResult = WaitForOverlapped(m_hComm, &ov, lpBytesRead, nTimeOut);

    } while (false);

    return bResult;
}

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值