您的位置:首页 > 编程语言 > Qt开发

VS2015 +Qt5 串口工具

2017-07-04 10:30 141 查看
简单的小工具是VS2015 + Qt5.6.1实现的,界面部分是Qt实现,串口是封装的WinAPI,把串口收发模块封装成了个Serialport.dll 供Qt界面调用。

由于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控件,然后设置了一下数据的展现方式,分ASCII和HEX(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 }


软件运行:

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





源码地址:https://github.com/karllen/SerialPortTools
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: