RS485 RS422串口读写 源码

源码出处也不知! 

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;
}
 

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值