简单的小工具是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; //获得线程ID13 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 __cplusplus17 }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 do20 {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,进行简单的数据通信测试。


本文转自lzwxx 51CTO博客,原文链接:http://blog.51cto.com/13064681/1944329