windows純C++實現串列埠通訊
阿新 • • 發佈:2018-12-12
點h檔案
#ifndef _WZSERIALPORT_H #define _WZSERIALPORT_H #include <iostream> using namespace std; /* 作者:歐陽偉 日期:2017-12-14 類名:WZSerialPort 用途:串列埠讀寫 示例: WZSerialPort w; if(w.open("COM1",9600,0,8,1)) { w.send("helloworld",10); char buf[1024]; w.receive(buf,1024); } */ class WZSerialPort { public: WZSerialPort(); ~WZSerialPort(); // 開啟串列埠,成功返回true,失敗返回false // portname(串列埠名): 在Windows下是"COM1""COM2"等,在Linux下是"/dev/ttyS1"等 // baudrate(波特率): 9600、19200、38400、43000、56000、57600、115200 // parity(校驗位): 0為無校驗,1為奇校驗,2為偶校驗,3為標記校驗 // databit(資料位): 4-8,通常為8位 // stopbit(停止位): 1為1位停止位,2為2位停止位,3為1.5位停止位 // synchronizable(同步、非同步): 0為非同步,1為同步 bool open(const char* portname, int baudrate=115200, char parity=0, char databit=8, char stopbit=1, char synchronizeflag=0); //關閉串列埠,引數待定 void close(); //傳送資料或寫資料,成功返回傳送資料長度,失敗返回0 int send(string dat); //接受資料或讀資料,成功返回讀取實際資料的長度,失敗返回0 string receive(); private: int pHandle[16]; char synchronizeflag; }; #endif
點cpp檔案
#include "WZSerialPort.h" #include <stdio.h> #include <string.h> #include <WinSock2.h> #include <windows.h> #include<iostream> using namespace std; WZSerialPort::WZSerialPort() { } WZSerialPort::~WZSerialPort() { } bool WZSerialPort::open(const char* portname, int baudrate, char parity, char databit, char stopbit, char synchronizeflag) { this->synchronizeflag = synchronizeflag; HANDLE hCom = NULL; if (this->synchronizeflag) { //同步方式 hCom = CreateFileA(portname, //串列埠名 GENERIC_READ | GENERIC_WRITE, //支援讀寫 0, //獨佔方式,串列埠不支援共享 NULL,//安全屬性指標,預設值為NULL OPEN_EXISTING, //開啟現有的串列埠檔案 0, //0:同步方式,FILE_FLAG_OVERLAPPED:非同步方式 NULL);//用於複製檔案控制代碼,預設值為NULL,對串列埠而言該引數必須置為NULL } else { //非同步方式 hCom = CreateFileA(portname, //串列埠名 GENERIC_READ | GENERIC_WRITE, //支援讀寫 0, //獨佔方式,串列埠不支援共享 NULL,//安全屬性指標,預設值為NULL OPEN_EXISTING, //開啟現有的串列埠檔案 FILE_FLAG_OVERLAPPED, //0:同步方式,FILE_FLAG_OVERLAPPED:非同步方式 NULL);//用於複製檔案控制代碼,預設值為NULL,對串列埠而言該引數必須置為NULL } if(hCom == (HANDLE)-1) { return false; } //配置緩衝區大小 if(! SetupComm(hCom,1024, 1024)) { return false; } // 配置引數 DCB p; memset(&p, 0, sizeof(p)); p.DCBlength = sizeof(p); p.BaudRate = baudrate; // 波特率 p.ByteSize = databit; // 資料位 switch (parity) //校驗位 { case 0: p.Parity = NOPARITY; //無校驗 break; case 1: p.Parity = ODDPARITY; //奇校驗 break; case 2: p.Parity = EVENPARITY; //偶校驗 break; case 3: p.Parity = MARKPARITY; //標記校驗 break; } switch(stopbit) //停止位 { case 1: p.StopBits = ONESTOPBIT; //1位停止位 break; case 2: p.StopBits = TWOSTOPBITS; //2位停止位 break; case 3: p.StopBits = ONE5STOPBITS; //1.5位停止位 break; } if(! SetCommState(hCom, &p)) { // 設定引數失敗 return false; } //超時處理,單位:毫秒 //總超時=時間係數×讀或寫的字元數+時間常量 COMMTIMEOUTS TimeOuts; TimeOuts.ReadIntervalTimeout = 1000; //讀間隔超時 TimeOuts.ReadTotalTimeoutMultiplier = 500; //讀時間係數 TimeOuts.ReadTotalTimeoutConstant = 5000; //讀時間常量 TimeOuts.WriteTotalTimeoutMultiplier = 500; // 寫時間係數 TimeOuts.WriteTotalTimeoutConstant = 2000; //寫時間常量 SetCommTimeouts(hCom,&TimeOuts); PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);//清空串列埠緩衝區 memcpy(pHandle, &hCom, sizeof(hCom));// 儲存控制代碼 return true; } void WZSerialPort::close() { HANDLE hCom = *(HANDLE*)pHandle; CloseHandle(hCom); } int WZSerialPort::send(string dat) { HANDLE hCom = *(HANDLE*)pHandle; if (this->synchronizeflag) { // 同步方式 DWORD dwBytesWrite = dat.length(); //成功寫入的資料位元組數 BOOL bWriteStat = WriteFile(hCom, //串列埠控制代碼 (char*)dat.c_str(), //資料首地址 dwBytesWrite, //要傳送的資料位元組數 &dwBytesWrite, //DWORD*,用來接收返回成功傳送的資料位元組數 NULL); //NULL為同步傳送,OVERLAPPED*為非同步傳送 if (!bWriteStat) { return 0; } return dwBytesWrite; } else { //非同步方式 DWORD dwBytesWrite = dat.length(); //成功寫入的資料位元組數 DWORD dwErrorFlags; //錯誤標誌 COMSTAT comStat; //通訊狀態 OVERLAPPED m_osWrite; //非同步輸入輸出結構體 //建立一個用於OVERLAPPED的事件處理,不會真正用到,但系統要求這麼做 memset(&m_osWrite, 0, sizeof(m_osWrite)); m_osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, L"WriteEvent"); ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通訊錯誤,獲得裝置當前狀態 BOOL bWriteStat = WriteFile(hCom, //串列埠控制代碼 (char*)dat.c_str(), //資料首地址 dwBytesWrite, //要傳送的資料位元組數 &dwBytesWrite, //DWORD*,用來接收返回成功傳送的資料位元組數 &m_osWrite); //NULL為同步傳送,OVERLAPPED*為非同步傳送 if (!bWriteStat) { if (GetLastError() == ERROR_IO_PENDING) //如果串列埠正在寫入 { WaitForSingleObject(m_osWrite.hEvent, 1000); //等待寫入事件1秒鐘 } else { ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通訊錯誤 CloseHandle(m_osWrite.hEvent); //關閉並釋放hEvent記憶體 return 0; } } return dwBytesWrite; } } string WZSerialPort::receive() { HANDLE hCom = *(HANDLE*)pHandle; string rec_str=""; char buf[1024]; if (this->synchronizeflag) { //同步方式 DWORD wCount = 1024; //成功讀取的資料位元組數 BOOL bReadStat = ReadFile(hCom, //串列埠控制代碼 buf, //資料首地址 wCount, //要讀取的資料最大位元組數 &wCount, //DWORD*,用來接收返回成功讀取的資料位元組數 NULL); //NULL為同步傳送,OVERLAPPED*為非同步傳送 for (int i = 0; i < 1024; i++) { if (buf[i] != -52) rec_str += buf[i]; else break; } return rec_str; } else { //非同步方式 DWORD wCount = 1024; //成功讀取的資料位元組數 DWORD dwErrorFlags; //錯誤標誌 COMSTAT comStat; //通訊狀態 OVERLAPPED m_osRead; //非同步輸入輸出結構體 //建立一個用於OVERLAPPED的事件處理,不會真正用到,但系統要求這麼做 memset(&m_osRead, 0, sizeof(m_osRead)); m_osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, L"ReadEvent"); ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通訊錯誤,獲得裝置當前狀態 if (!comStat.cbInQue)return 0; //如果輸入緩衝區位元組數為0,則返回false //std::cout << comStat.cbInQue << std::endl; BOOL bReadStat = ReadFile(hCom, //串列埠控制代碼 buf, //資料首地址 wCount, //要讀取的資料最大位元組數 &wCount, //DWORD*,用來接收返回成功讀取的資料位元組數 &m_osRead); //NULL為同步傳送,OVERLAPPED*為非同步傳送 if (!bReadStat) { if (GetLastError() == ERROR_IO_PENDING) //如果串列埠正在讀取中 { //GetOverlappedResult函式的最後一個引數設為TRUE //函式會一直等待,直到讀操作完成或由於錯誤而返回 GetOverlappedResult(hCom, &m_osRead, &wCount, TRUE); } else { ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通訊錯誤 CloseHandle(m_osRead.hEvent); //關閉並釋放hEvent的記憶體 return 0; } } for (int i = 0;i<1024; i++) { if (buf[i]!=-52) rec_str += buf[i]; else break; } return rec_str; } }
測試用例,VS2013平臺,實驗是將串列埠模組的RX和TX直接連線,測試資料的傳送和接受,完成一個回傳測試
#include "WZSerialPort.h" #include<iostream> using namespace std; int main() { WZSerialPort w; if (w.open("com3")) { string str = "helloworsdasadfgsdjhjshdfgvhjhjzxvjzhhcsdugdczxhjczsdhdhxghdgysdhhhhjjhdjhfvvld"; w.send(str); cout << w.receive().c_str(); w.close(); } while (true) { } return 0; }
輸出結果