• VS2015 +Qt5 串口工具


    简单的小工具是VS2015 + Qt5.6.1实现的,界面部分是Qt实现,串口是封装的WinAPI,把串口收发模块封装成了个Serialport.dllQt界面调用。

    由于VS2015需要Universal CRT运行环境,因此把Qt编译成了静态的版本。

    一、串口收发是封装的Win32,单独封装成了一个Serialport.dll.

    包括串口通信类:

    class CSerialport
    {
    public:
        CSerialport();
        ~CSerialport();
        BOOL openComm(const string & name);
        BOOL closeComm();
        BOOL setCommState(const DCB & dcb)const;
        BOOL getCommState(DCB & dcb)const;
        BOOL setCommTimeouts(const COMMTIMEOUTS & commtimeOuts)const;
        BOOL purgeComm(DWORD flags = PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT)const;
        BOOL setupComm(DWORD dwInQueue, DWORD dwOutQueue)const;
        int  readFile(vector<char> & buffer, DWORD nNumberOfBytesToRead,DWORD & lpNumberOfBytesRead, LPOVERLAPPED pLoverlapped = NULL);
        int  writeFile(vector<char> & buffer, DWORD nNumberOfBytesToRead, DWORD & lpNumberOfBytesRead, LPOVERLAPPED pLoverlapped = NULL);
        string getPortName()const;
        HANDLE getHandle()const;
        //
    private:
        HANDLE m_hspCom;
        string m_commName;
    };

    此类负责基本的串口通信。

    线程类:

     1 class CBaseThread
     2 {
     3 public:
     4     CBaseThread(void);
     5     virtual ~CBaseThread(void);
     6 public:
     7     virtual void start();                                        //创建线程
     8     virtual void end();                                          //结束线程
     9     virtual void resume();                                       //重启线程
    10     virtual void suspend();                                      //暂停线程
    11     //
    12     virtual int  getThreadID() const;                            //获得线程ID
    13     virtual BOOL isRun() const;                                  //判断线程是否运行
    14     virtual void runTask() = 0;                                  //子类实现此函数完成业务逻辑
    15     //
    16     static unsigned _stdcall threadFunc(void* pParam);           //线程函数,调RunTask逻辑
    17 
    18 protected:
    19     HANDLE m_hEndEvent;                                         //设置退出线程处理
    20     HANDLE m_hExitEvent;                                        //线程RunTask结束时设置,确保线程正常退出                                     
    21     HANDLE m_hThreadHandle;
    22     unsigned int m_uThreadID;
    23     //
    24 };

    线程基类,封装了_beginthreadex()

    串口线程调度线程:

    typedef int (*pGET_DATA_CAAL_BACK)(list<char> &);
    
    class CSerialportThread :
        public CBaseThread
    {
    public:
        CSerialportThread(void);
        virtual ~CSerialportThread(void);
        void setCommConfig(const char* com, int baudRate, char byteSize, char parity, char stopBits);
        static CSerialportThread * getInstance();
        int  writeFile(const char * writeBuffer,int size);
        void initCallBack(void *);
    protected:
        virtual void runTask();
        void initComm();
    protected:
        CSerialport m_serialport;
        //
        string m_com;
        int m_baudRate;
        char m_byteSize;
        char m_parity;
        char m_stopBits;
        bool m_bInit;
        pGET_DATA_CAAL_BACK m_addDataCallBack;
    };

    继承CBaseThread实现runTask()线程函数,由CSerialport 类成员变量进行串口的通信的管理,并提供一个回调接口,将接收到的数据回调给接收数据维护的类。

    串口数据接收维护类:

    class CCommDataHolder
    {
    public:
        CCommDataHolder();
        ~CCommDataHolder();
        //
        static std::shared_ptr<CCommDataHolder> getInstance();
        static int getDataCallBackS(list<char> & buffer);
        
        int getCommData(char * buffer,int len);
    protected:
        int getDataCallBack(list<char> & buffer);
        //
    private:
        static std::shared_ptr<CCommDataHolder>  s_pInstance;
        static std::mutex s_mt;
        //
        list<char> m_listData;
        std::mutex m_mt;
    };

    数据维护类,上层应用来这里取数据即可。

    Serialport.dll导出接口:

     1 #pragma once
     2 
     3 #define SERIALPORT_DLL_EXPORT __declspec(dllexport)
     4 
     5 #ifdef __cplusplus
     6 extern "C"
     7 {
     8 #endif // 
     9     
    10 SERIALPORT_DLL_EXPORT int commReadData(char* buffer,int size);
    11 SERIALPORT_DLL_EXPORT int commWriteData(const char* buffer, int size);
    12 SERIALPORT_DLL_EXPORT void setCommConfig(const char* com,int baudRate,char byteSize,char parity,char stopBits);
    13 SERIALPORT_DLL_EXPORT void start();
    14 SERIALPORT_DLL_EXPORT void end();
    15 
    16 #ifdef __cplusplus
    17 }
    18 #endif // 

    二、Qt界面实现

    界面实现主要是设置串口的通信的参数,然后设置串口通信的收发区域,这里使用textEdit控件,然后设置了一下数据的展现方式,分ASCIIHEX(16进制发送),

    16进制发送的形式应该为: 61 25 AA 7A 5B的这种形式,然后选择Hex选项,发送。

    串口列表:自动枚举系统的所有已存在串口enumPort(QStringList & strList)

     1 void SerialPortTools::enumPort(QStringList & strList)
     2 {
     3     HKEY hKey;
     4     LPCTSTR lpSubKey = _T("HARDWARE\DEVICEMAP\SERIALCOMM\");
     5 
     6     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpSubKey, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
     7     {
     8         return;
     9     }
    10     WCHAR szValueName[100] = { 0 };
    11     WCHAR szPortName[100] = { 0 };
    12     LONG status;
    13     DWORD dwIndex = 0;
    14     DWORD dwSizeValueName = 100;
    15     DWORD dwSizeofPortName = 100;
    16     DWORD Type;
    17     dwSizeValueName = 100;
    18     dwSizeofPortName = 100;
    19     do
    20     {
    21         status = RegEnumValue(hKey, dwIndex++, szValueName, &dwSizeValueName, NULL, &Type, (PUCHAR)szPortName, &dwSizeofPortName);
    22         if ((status == ERROR_SUCCESS))
    23         {
    24             QString tmp = CCodecUtils::str2qstr(CEcoder::wstringToString(szPortName));
    25             strList << tmp;
    26         }
    27         dwSizeValueName = 100;
    28         dwSizeofPortName = 100;
    29     } while ((status != ERROR_NO_MORE_ITEMS));
    30     RegCloseKey(hKey);
    31 }

    设置好串口参数,串口号,波特率,数据位,校验位,停止位等参数后setCommConfig(com.c_str(), baudRate, byteSize, parity, stopBit);

    ,打开串口start();开启调度串口通信线程

    打开串口需要引用Serialport.dll的接口函数

     1 void SerialPortTools::openPort()
     2 {
     3     QString strCom = ui.comboBox->currentText();
     4     QString strBaudRate = ui.comboBox_2->currentText();
     5     QString strByteSize = ui.comboBox_3->currentText();
     6     //
     7     string com   = CCodecUtils::qstr2str(strCom);
     8     int baudRate = strBaudRate.toInt();
     9     int byteSize = strByteSize.toInt();
    10     int parity     = ui.comboBox_4->currentIndex();
    11     int stopBit  = ui.comboBox_5->currentIndex();
    12     setCommConfig(com.c_str(), baudRate, byteSize, parity, stopBit);
    13     //
    14     start();    
    15     m_timer.start();
    16     MessageBoxInfo(tr("提示"), tr("    串口打开成功    "));
    17     ui.openaction->setEnabled(false);
    18     ui.closeaction->setEnabled(true);
    19 }

    数据接收的实现是设置了一个定时器,不断去调用commReadData(char* buffer,int size) 去读取dll数据缓存区的数据,读到之后更新到数据接收区的界面。

    发送则很简单,获取数据发送区的内容,点击发送,直接调用commWriteData(const char *buffer,int size)发送串口数据。

    定时器:

    1     m_timer.setInterval(2000);
    2     connect(&m_timer, SIGNAL(timeout()), this, SLOT(reciveData()));

    数据收:

     1 void SerialPortTools::reciveData()
     2 {
     3     char szRead[1024] = { 0 };
     4     memset(&szRead, 0, 1024);
     5     int nRet = commReadData(szRead, 1024);
     6     m_strRec = CCodecUtils::qstr2str(ui.textEdit->toPlainText());
     7     if (nRet != 0)
     8     {
     9         string str = szRead;
    10         m_strRec += str;
    11         if (ui.radioButton->isChecked())
    12         {
    13             ui.textEdit->setText(CCodecUtils::str2qstr(m_strRec));
    14         }
    15         else if (ui.radioButton_2->isChecked())
    16         {
    17             char sz[2048];
    18             memset(&sz, 0, 2048);
    19             ui.textEdit->setText(CCodecUtils::str2qstr(CCodecUtils::byte2HexCpp(m_strRec)));
    20         }
    21     }    
    22 }

    数据发:

     1 void SerialPortTools::sendData()
     2 {
     3     QString str = ui.textEdit_2->toPlainText();
     4     string strSend = CCodecUtils::qstr2str(str);
     5     if (!strSend.empty())
     6     {
     7         //
     8         if (ui.radioButton_3->isChecked())
     9         {
    10             commWriteData(strSend.c_str(), strSend.length() + 1);
    11         }
    12         else if (ui.radioButton_4->isChecked())
    13         {
    14             string strtmp = CCodecUtils::hexStr2Str(CCodecUtils::eraseSpace(strSend));
    15             commWriteData(strtmp.c_str(),strtmp.length()+1);
    16             //
    17         }
    18     }
    19 }

    串口关闭end();

    1 void SerialPortTools::closePort()
    2 {
    3     end();
    4     ui.openaction->setEnabled(true);
    5     ui.closeaction->setEnabled(false);
    6 }

    软件运行:

    用虚拟串口工具打开COM1COM2两个串口对,打开两次SerialPortTools.exe ,分别打开COM1COM2,进行简单的数据通信测试。

     

    源码地址:https://github.com/karllen/SerialPortTools

     

  • 相关阅读:
    WPF 复制和粘贴
    WPF控件模拟双击事件
    WPF获取鼠标当前位置
    user32.dll
    C# 根据IP地址获取城市
    WPF 动态布局Grid
    手机自动化测试:Appium源码分析之跟踪代码分析五
    手机自动化测试:appium源码分析之bootstrap三
    手机自动化测试:appium源码分析之bootstrap二
    手机自动化测试:appium源码分析之bootstrap一
  • 原文地址:https://www.cnblogs.com/Forever-Kenlen-Ja/p/7115377.html
Copyright © 2020-2023  润新知