zoukankan      html  css  js  c++  java
  • c++串口通信实例

    头文件SerialPort.h。

      1 ///////////////////////
      2 ////这是头文件的代码,主要是定义了一个类
      3 ///////////////////////////////
      4 
      5 #ifndef SERIALPORT_H_  
      6 #define SERIALPORT_H_  
      7 #include <process.h>    
      8 #include "TChar.h"
      9 #include <string>
     10 #include <sstream>
     11 #include <iostream>
     12 #include <iomanip>
     13 #include <algorithm>
     14 #include <iterator>
     15 #include <cctype>
     16 #include <Windows.h>  
     17 using namespace std;
     18 /** 串口通信类
     19 *
     20 *  本类实现了对串口的基本操作
     21 *  例如监听发到指定串口的数据、发送指定数据到串口
     22 */
     23 
     24 typedef enum AxisType
     25 {
     26     AXIS_XX = 2,
     27     AXIS_YY = 3,
     28     AXIS_ZZ = 1,
     29     AXIS_OO = 4,
     30 }AXIS_TYPE;
     31 
     32 
     33 class CSerialPort
     34 {
     35 public:
     36     CSerialPort(void);
     37     ~CSerialPort(void);
     38 
     39 public:
     40 
     41     /** 初始化串口函数
     42     *
     43     *  @param:  UINT portNo 串口编号,默认值为1,即COM1,注意,尽量不要大于9
     44     *  @param:  UINT baud   波特率,默认为9600
     45     *  @param:  char parity 是否进行奇偶校验,'Y'表示需要奇偶校验,'N'表示不需要奇偶校验
     46     *  @param:  UINT databits 数据位的个数,默认值为8个数据位
     47     *  @param:  UINT stopsbits 停止位使用格式,默认值为1
     48     *  @param:  DWORD dwCommEvents 默认为EV_RXCHAR,即只要收发任意一个字符,则产生一个事件
     49     *  @return: bool  初始化是否成功
     50     *  @note:   在使用其他本类提供的函数前,请先调用本函数进行串口的初始化
     51     *        /n本函数提供了一些常用的串口参数设置,若需要自行设置详细的DCB参数,可使用重载函数
     52     *           /n本串口类析构时会自动关闭串口,无需额外执行关闭串口
     53     *  @see:
     54     */
     55     //bool InitPort(UINT  portNo = 3, UINT  baud = CBR_19200, char  parity = 'N', UINT  databits = 8, UINT  stopsbits = 1, DWORD dwCommEvents = EV_RXCHAR);
     56     bool InitPort(UINT  portNo, UINT  baud, char  parity, UINT  databits, UINT  stopsbits, DWORD dwCommEvents);
     57 
     58 
     59     /** 串口初始化函数
     60     *
     61     *  本函数提供直接根据DCB参数设置串口参数
     62     *  @param:  UINT portNo
     63     *  @param:  const LPDCB & plDCB
     64     *  @return: bool  初始化是否成功
     65     *  @note:   本函数提供用户自定义地串口初始化参数
     66     *  @see:
     67     */
     68     bool InitPort(UINT  portNo, const LPDCB& plDCB);
     69 
     70     /** 开启监听线程
     71     *
     72     *  本监听线程完成对串口数据的监听,并将接收到的数据打印到屏幕输出
     73     *  @return: bool  操作是否成功
     74     *  @note:   当线程已经处于开启状态时,返回flase
     75     *  @see:
     76     */
     77     bool OpenListenThread();
     78 
     79     /** 关闭监听线程
     80     *
     81     *
     82     *  @return: bool  操作是否成功
     83     *  @note:   调用本函数后,监听串口的线程将会被关闭
     84     *  @see:
     85     */
     86     bool CloseListenTread();
     87 
     88     /** 向串口写数据
     89     *
     90     *  将缓冲区中的数据写入到串口
     91     *  @param:  unsigned char * pData 指向需要写入串口的数据缓冲区
     92     *  @param:  unsigned int length 需要写入的数据长度
     93     *  @return: bool  操作是否成功
     94     *  @note:   length不要大于pData所指向缓冲区的大小
     95     *  @see:
     96     */
     97     bool WriteData(unsigned char *pData, int length);
     98 
     99     /** 获取串口缓冲区中的字节数
    100     *
    101     *
    102     *  @return: UINT  操作是否成功
    103     *  @note:   当串口缓冲区中无数据时,返回0
    104     *  @see:
    105     */
    106     UINT GetBytesInCOM();
    107     /*UINT WriteData1(unsigned long long int *pData1, unsigned int length);*/
    108 
    109     /** 读取串口接收缓冲区中一个字节的数据
    110     *
    111     *
    112     *  @param:  char & cRecved 存放读取数据的字符变量
    113     *  @return: bool  读取是否成功
    114     *  @note:
    115     *  @see:
    116     */
    117     bool ReadChar(unsigned char &cRecved);
    118 
    119     unsigned char *MotorMoveXY(unsigned char x, unsigned char y);//xy相对移动
    120     unsigned char *StopMotor(unsigned char sm1);
    121     unsigned char *SetSpeed(AXIS_TYPE enAxisType, int speed);
    122     unsigned char *SetRunSpeed(int TY, int speed);
    123 private:
    124 
    125     /** 打开串口
    126     *
    127     *
    128     *  @param:  UINT portNo 串口设备号
    129     *  @return: bool  打开是否成功
    130     *  @note:
    131     *  @see:
    132     */
    133     bool openPort(UINT  portNo);
    134 
    135     /** 关闭串口
    136     *
    137     *
    138     *  @return: void  操作是否成功
    139     *  @note:
    140     *  @see:
    141     */
    142     void ClosePort();
    143 
    144     /** 串口监听线程
    145     *
    146     *  监听来自串口的数据和信息
    147     *  @param:  void * pParam 线程参数
    148     *  @return: UINT WINAPI 线程返回值
    149     *  @note:
    150     *  @see:
    151     */
    152     static UINT WINAPI ListenThread(void* pParam);
    153 
    154 private:
    155 
    156     /** 串口句柄 */
    157     HANDLE  m_hComm;
    158 
    159     /** 线程退出标志变量 */
    160     static bool s_bExit;
    161 
    162     /** 线程句柄 */
    163     volatile HANDLE    m_hListenThread;
    164 
    165     /** 同步互斥,临界区保护 */
    166     CRITICAL_SECTION   m_csCommunicationSync;       //!< 互斥操作串口  
    167 
    168 };
    169 
    170 #endif //SERIALPORT_H_

    SerialPort.cpp

    // SerialPort.cpp : 定义控制台应用程序的入口点。
    //
     
    //#include "stdafx.h"
     
    //////////////////////////////////////////////////////////////////////////  
    /// COPYRIGHT NOTICE  
    /// Copyright (c) 2009, 华中科技大学tickTick Group  (版权声明)  
    /// All rights reserved.  
    ///   
    /// @file    SerialPort.cpp    
    /// @brief   串口通信类的实现文件  
    ///  
    /// 本文件为串口通信类的实现代码  
    ///  
    /// @version 1.0     
    /// @author  卢俊    
    /// @E-mail:lujun.hust@gmail.com  
    /// @date    2010/03/19  
    ///   
    ///  
    ///  修订说明:  
    //////////////////////////////////////////////////////////////////////////  
     
    //#include "StdAfx.h"  
    #include "SerialPort.h"  
    using namespace std;
    /** 线程退出标志 */
    bool CSerialPort::s_bExit = false;
    /** 当串口无数据时,sleep至下次查询间隔的时间,单位:秒 */
    const UINT SLEEP_TIME_INTERVAL = 5;
     
    CSerialPort::CSerialPort(void)
        : m_hListenThread(INVALID_HANDLE_VALUE)
    {
        m_hComm = INVALID_HANDLE_VALUE;
        m_hListenThread = INVALID_HANDLE_VALUE;
        InitializeCriticalSection(&m_csCommunicationSync);
    }
     
    CSerialPort::~CSerialPort(void)
    {
        CloseListenTread();
        ClosePort();
        DeleteCriticalSection(&m_csCommunicationSync);
    }
     
    //初始化串口函数
    bool CSerialPort::InitPort(UINT portNo /*= 1*/, UINT baud /*= CBR_9600*/, char parity /*= 'N'*/,
        UINT databits /*= 8*/, UINT stopsbits /*= 1*/, DWORD dwCommEvents /*= EV_RXCHAR*/)
    {
     
        /** 临时变量,将制定参数转化为字符串形式,以构造DCB结构 */
        char szDCBparam[50];
        sprintf_s(szDCBparam, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopsbits);
     
        /** 打开指定串口,该函数内部已经有临界区保护,上面请不要加保护 */
        if (!openPort(portNo))
        {
            return false;
        }
     
        /** 进入临界段 */
        EnterCriticalSection(&m_csCommunicationSync);
     
        /** 是否有错误发生 */
        BOOL bIsSuccess = TRUE;
     
        /** 在此可以设置输入输出的缓冲区大小,如果不设置,则系统会设置默认值.
        *  自己设置缓冲区大小时,要注意设置稍大一些,避免缓冲区溢出
        */
        /*if (bIsSuccess )
        {
        bIsSuccess = SetupComm(m_hComm,10,10);
        }*/
     
        /** 设置串口的超时时间,均设为0,表示不使用超时限制 */
        COMMTIMEOUTS  CommTimeouts;
        CommTimeouts.ReadIntervalTimeout = 0;
        CommTimeouts.ReadTotalTimeoutMultiplier = 0;
        CommTimeouts.ReadTotalTimeoutConstant = 0;
        CommTimeouts.WriteTotalTimeoutMultiplier = 0;
        CommTimeouts.WriteTotalTimeoutConstant = 0;
        if (bIsSuccess)
        {
            bIsSuccess = SetCommTimeouts(m_hComm, &CommTimeouts);
        }
     
        DCB  dcb;
        if (bIsSuccess)
        {
            //// 将ANSI字符串转换为UNICODE字符串  
            //DWORD dwNum = MultiByteToWideChar(CP_ACP, 0, szDCBparam, -1, NULL, 0);
            //wchar_t *pwText = new wchar_t[dwNum];
            //if (!MultiByteToWideChar(CP_ACP, 0, szDCBparam, -1, pwText, dwNum))
            //{
            //    bIsSuccess = TRUE;
            //}
     
            /** 获取当前串口配置参数,并且构造串口DCB参数 */
            bIsSuccess = GetCommState(m_hComm, &dcb) && BuildCommDCB(szDCBparam, &dcb);
            /** 开启RTS flow控制 */
            dcb.fRtsControl = RTS_CONTROL_ENABLE;
     
            /** 释放内存空间 */
            /*delete[] pwText;*/
        }
     
        if (bIsSuccess)
        {
            /** 使用DCB参数配置串口状态 */
            bIsSuccess = SetCommState(m_hComm, &dcb);
        }
     
        /**  清空串口缓冲区 */
        PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
     
        /** 离开临界段 */
        LeaveCriticalSection(&m_csCommunicationSync);
     
        return bIsSuccess == TRUE;
    }
     
    //初始化串口函数
    bool CSerialPort::InitPort(UINT portNo, const LPDCB& plDCB)
    {
        /** 打开指定串口,该函数内部已经有临界区保护,上面请不要加保护 */
        if (!openPort(portNo))
        {
            return false;
        }
     
        /** 进入临界段 */
        EnterCriticalSection(&m_csCommunicationSync);
     
        /** 配置串口参数 */
        if (!SetCommState(m_hComm, plDCB))
        {
            return false;
        }
     
        /**  清空串口缓冲区 */
        PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
     
        /** 离开临界段 */
        LeaveCriticalSection(&m_csCommunicationSync);
     
        return true;
    }
     
    //关闭关闭串口
    void CSerialPort::ClosePort()
    {
        /** 如果有串口被打开,关闭它 */
        if (m_hComm != INVALID_HANDLE_VALUE)
        {
            CloseHandle(m_hComm);
            m_hComm = INVALID_HANDLE_VALUE;
        }
    }
     
    //打开出串口
    bool CSerialPort::openPort(UINT portNo)
    {
        /** 进入临界段 */
        EnterCriticalSection(&m_csCommunicationSync);
     
        /** 把串口的编号转换为设备名 */
        char szPort[50];
        sprintf_s(szPort, "COM%d", portNo);
     
        /** 打开指定的串口 */
        m_hComm = CreateFileA(szPort,  /** 设备名,COM1,COM2等 */
            GENERIC_READ | GENERIC_WRITE, /** 访问模式,可同时读写 */
            0,                            /** 共享模式,0表示不共享 */
            NULL,                         /** 安全性设置,一般使用NULL */
            OPEN_EXISTING,                /** 该参数表示设备必须存在,否则创建失败 */
            0,
            0);
     
        /** 如果打开失败,释放资源并返回 */
        if (m_hComm == INVALID_HANDLE_VALUE)
        {
            LeaveCriticalSection(&m_csCommunicationSync);
            return false;
        }
     
        /** 退出临界区 */
        LeaveCriticalSection(&m_csCommunicationSync);
     
        return true;
    }
     
    //打开监听线程
    bool CSerialPort::OpenListenThread()
    {
        /** 检测线程是否已经开启了 */
        if (m_hListenThread != INVALID_HANDLE_VALUE)
        {
            /** 线程已经开启 */
            return false;
        }
        s_bExit = false;
        /** 线程ID */
        UINT threadId;
        /** 开启串口数据监听线程 */
        m_hListenThread = (HANDLE)_beginthreadex(NULL, 0, ListenThread, this, 0, &threadId);
        if (!m_hListenThread)
        {
            return false;
        }
        /** 设置线程的优先级,高于普通线程 */
        if (!SetThreadPriority(m_hListenThread, THREAD_PRIORITY_ABOVE_NORMAL))
        {
            return false;
        }
     
        return true;
    }
    //关闭监听线程
    bool CSerialPort::CloseListenTread()
    {
        if (m_hListenThread != INVALID_HANDLE_VALUE)
        {
            /** 通知线程退出 */
            s_bExit = true;
     
            /** 等待线程退出 */
            Sleep(10);
     
            /** 置线程句柄无效 */
            CloseHandle(m_hListenThread);
            m_hListenThread = INVALID_HANDLE_VALUE;
        }
        return true;
    }
    //获取串口缓冲区的字节数
    UINT CSerialPort::GetBytesInCOM()
    {
        DWORD dwError = 0;  /** 错误码 */
        COMSTAT  comstat;   /** COMSTAT结构体,记录通信设备的状态信息 */
        memset(&comstat, 0, sizeof(COMSTAT));
     
        UINT BytesInQue = 0;
        /** 在调用ReadFile和WriteFile之前,通过本函数清除以前遗留的错误标志 */
        if (ClearCommError(m_hComm, &dwError, &comstat))
        {
            BytesInQue = comstat.cbInQue; /** 获取在输入缓冲区中的字节数 */
        }
     
        return BytesInQue;
    }
    //串口监听线程
    UINT WINAPI CSerialPort::ListenThread(void* pParam)
    {
        /** 得到本类的指针 */
        CSerialPort *pSerialPort = reinterpret_cast<CSerialPort*>(pParam);
     
        // 线程循环,轮询方式读取串口数据  
        while (!pSerialPort->s_bExit)
        {
            UINT BytesInQue = pSerialPort->GetBytesInCOM();
            /** 如果串口输入缓冲区中无数据,则休息一会再查询 */
            if (BytesInQue == 0)
            {
                Sleep(SLEEP_TIME_INTERVAL);
                continue;
            }
     
            /** 读取输入缓冲区中的数据并输出显示 */
            unsigned char cRecved = 0x00;
            do
            {
                cRecved =0x00;
                if (pSerialPort->ReadChar(cRecved) == true)
                {
     
                    std::stringstream  ss;
                    int tm = cRecved;
                    ss << std::hex << std::setw(2) << std::setfill('0') << tm;
                    ss << " ";
                    string a = ss.str();
                    string b;
                    transform(a.begin(), a.end(), back_inserter(b), ::toupper);
                    cout <<  b ;
                    continue;
                }
            } while (--BytesInQue);
        }
     
        return 0;
    }
    //读取串口接收缓冲区中一个字节的数据
    bool CSerialPort::ReadChar(unsigned char &cRecved)
    {
        BOOL  bResult = TRUE;
        DWORD BytesRead = 0;
        if (m_hComm == INVALID_HANDLE_VALUE)
        {
            return false;
        }
     
        /** 临界区保护 */
        EnterCriticalSection(&m_csCommunicationSync);
     
        /** 从缓冲区读取一个字节的数据 */
        bResult = ReadFile(m_hComm, &cRecved, 1, &BytesRead, NULL);
        if ((!bResult))
        {
            /** 获取错误码,可以根据该错误码查出错误原因 */
            DWORD dwError = GetLastError();
     
            /** 清空串口缓冲区 */
            PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);
            LeaveCriticalSection(&m_csCommunicationSync);
     
            return false;
        }
     
        /** 离开临界区 */
        LeaveCriticalSection(&m_csCommunicationSync);
     
        return (BytesRead == 1);
     
    }
     
    // 向串口写数据, 将缓冲区中的数据写入到串口
    bool CSerialPort::WriteData(unsigned char *pData,  int length)
    {
        int *pData1=new int;
        BOOL   bResult = TRUE;
        DWORD  BytesToSend = 0;
        if (m_hComm == INVALID_HANDLE_VALUE)
        {
            return false;
        }
     
        /** 临界区保护 */
        EnterCriticalSection(&m_csCommunicationSync);
     
        /** 向缓冲区写入指定量的数据 */
        bResult = WriteFile(m_hComm,/*文件句柄*/pData,/*用于保存读入数据的一个缓冲区*/ 8,/*要读入的字符数*/ &BytesToSend,/*指向实际读取字节数的指针*/ NULL);
        if (!bResult)
        {
            DWORD dwError = GetLastError();
            /** 清空串口缓冲区 */
            PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);
            LeaveCriticalSection(&m_csCommunicationSync);
     
            return false;
        }
     
        /** 离开临界区 */
        LeaveCriticalSection(&m_csCommunicationSync);
        
        return true;
    }

    main.cpp

    //#include "StdAfx.h"  
    #include "SerialPort.h"  
    using namespace std;
    int _tmain(int argc, _TCHAR* argv[])
    {
        CSerialPort mySerialPort;//首先将之前定义的类实例化
        int length = 8;//定义传输的长度
    
    
        unsigned char *temp = new unsigned char[8];//动态创建一个数组
    
    
    
    
        if (!mySerialPort.InitPort(3, CBR_19200, 'N', 8, 1, EV_RXCHAR))//是否打开串口,3就是你外设连接电脑的com口,可以在设备管理器查看,然后更改这个参数
        {
            std::cout << "initPort fail !" << std::endl;
        }
        else
        {
            std::cout << "initPort success !" << std::endl;
        }
        if (!mySerialPort.OpenListenThread())//是否打开监听线程,开启线程用来传输返回值
        {
            std::cout << "OpenListenThread fail !" << std::endl;
        }
        else
        {
            std::cout << "OpenListenThread success !" << std::endl;
        }
    
    
        temp[0] = 03;
        temp[1] = 85;
        temp[2] = 04;
        temp[3] = 00;
        temp[4] = 00;
        temp[5] = 00;
        temp[6] = 11;
        temp[7] = 03;
        cout << mySerialPort.WriteData(temp, 8) << endl;//这个函数就是给串口发送数据的函数,temp就是要发送的数组。
        cout << mySerialPort.GetBytesInCOM() << endl;//这个函数就是显示返回值函数
        //delete temp[];
        system("pause");
        return 0;
    }

    (mySerialPort.InitPort(3, CBR_19200, 'N', 8, 1, EV_RXCHAR)调用这个函数来打开串口,3就是你的外设连接电脑的com口(我的外设连接的com口为3),可以在设备管理器查看,然后更改这个参数,CBR_19200为波特率设置,8即为要传输的长度。 mySerialPort.WriteData(temp, 8)有的新手之前没有接触过程序不知道主程序中怎么写发送函数,这个函数就是发送函数第一个参数就是你要发送的数据,第二个就是你要发送的长度(在头文件中都定义好的)。返回数据,是通过开启一个监听线程,来监听串口处是否有数据返回,不作详细解释。

  • 相关阅读:
    【Azure 应用服务】在Azure App Service多实例的情况下,如何在应用中通过代码获取到实例名(Instance ID)呢?
    【Azure 应用服务】App Service For Windows 中如何设置代理实现前端静态文件和后端Java Spring Boot Jar包
    【Azure Developer】使用Azure Key Vault 的Key签名后,离线验证的一些参考资料
    【Azure Function】调试 VS Code Javascript Function本地不能运行,报错 Value cannot be null. (Parameter 'provider')问题
    【Azure 应用服务】App Service 使用Tomcat运行Java应用,如何设置前端网页缓存的相应参数呢(Xms512m Xmx1204m)?
    【Azure API 管理】APIM添加Logtoeventhub的策略后,一些相关APIM与Event Hub的问题
    【Azure API 管理】为调用APIM的请求启用Trace 调试APIM Policy的利器
    【Azure 事件中心】China Azure上是否有Kafka服务简答
    【Azure 应用服务】探索在Azure上设置禁止任何人访问App Service的默认域名(Default URL)
    【Azure 微服务】记一次错误的更新Service Fabric 证书而引发的集群崩溃而只能重建
  • 原文地址:https://www.cnblogs.com/zxl971213/p/13323267.html
Copyright © 2011-2022 走看看