VS2015 +Qt5 串口工具

时间:2022-01-21 13:13:36

简单的小工具是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选项,发送。

VS2015 +Qt5 串口工具

 

串口列表:自动枚举系统的所有已存在串口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,进行简单的数据通信测试。

 

VS2015 +Qt5 串口工具

 

 

VS2015 +Qt5 串口工具

 

 

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