串口使用和CSerial類


1 串口通信的基本原理

串口通信中無論是寫入串口還是讀取串口,都是對緩沖區操作的。可以理解為寫串口就是向輸出緩沖區寫入內容,讀取串口就是從輸入串口緩沖區讀取內容。但是何時打開串口,何時發送數據,何時接受數據都是未知的。所以在串口通信時一般是一個主動一個被動。通信雙方有一定的協議,就是事先協商好的數據格式。接收方接收到數據后,返回一個應答標志,告訴發送方已經接收到數據了。如果接收錯誤則返回接收錯誤標志,或者一直等待接收到正確的數據再返回。至於接收方怎么才知道有數據發送過來了,對於單片機之類的可以使用串口中斷或者巡檢的方式。對於工控機之類只能使用巡檢的方式了。前段時間做一個檢測系統,用到串口通信,順手就寫了一個類,這里分享出來。

2 串口通信的基本操作步驟

無論哪種操作方式,串口通信一般都通過四個步驟來完成:
1、打開串口;
2、配置串口;
3、讀寫串口;
4、關閉串口;

2.1 打開串口

在Windows中使用串口通信一般有兩種方式,一種是使用Windows中的API,另一種方式使用MFC中的控件。這里采用API的方式。

HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDistribution,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);

lpFileName,要打開的串口號,如“COM1”;

dwDesiredAccess,串口訪問的類型,可以是只讀、只寫、可讀可寫。其取值可以是GENERIC_READ、GENERIC_WRITE或者他們的組合;

dwShareMode,共享屬性,由於串口不能共享,該參數必須置為0;

lpSecurityAttributes,引用的安全類型,一般設置為NULL;

dwCreationDistribution,創建文件的標志,對於串口操作該參數必須置為OPEN_EXISTING;

dwFlagsAndAttributes,串口通信是同步還是異步,0表示同步。FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED表示異步;

hTemplateFile:對串口而言該參數必須置為NULL。

異步方式打開串口示例代碼:

CreateFile(
m_strCom,
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
NULL);
2.2 配置串口

串口打開需要配置一些參數,如DCB結構、輸入輸出緩沖區大小、設置超時結構。
配置DCB結構,該結構中可以配置波特率、數據位、奇偶校驗和停止位之類的信息;
設置該結構的時候需要用到幾個函數:

BOOL GetCommState(HANDLE hFile, LPDCB lpDCB);
BOOL SetCommState(HANDLE hFile, LPDCB lpDCB);

示例代碼如下:

DCB dcb;
GetCommState(m_hCom, &dcb);
dcb.BaudRate = m_dwBaudRate;
dcb.ByteSize = m_byteSize;
dcb.Parity = m_byteCheck;
dcb.StopBits = m_byteStop;
SetCommState(m_hCom, &dcb);

設置串口緩沖區大小:

BOOL SetupComm( HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue);

示例代碼如下:

DWORD dwInQueue = 1024;
DEWORD dwOutQueue = 1024;
SetupComm(hCom, dwInQueue, dwOutQueue);

設置超時:

BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts);

該函數第一個參數不用多說,第二個參數是個結構體。

typedef struct _COMMTIMEOUTS {
    DWORD ReadIntervalTimeout;
    DWORD ReadTotalTimeoutMultiplier;
    DWORD ReadTotalTimeoutConstant;
    DWORD WriteTotalTimeoutMultiplier;
    DWORD WriteTotalTimeoutConstant;
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

ReadIntervalTimeout,讀取操作過程中兩個字符之間的延時,當讀取串口的時候如果兩個字符傳輸的時間差如果超過這個時間的話,讀取串口函數就會返回;

ReadTotalTimeoutMultiplier,讀取操作計算總超時時每個字符讀取的時間,其實就是估算的每個字符傳輸需要的時間;

ReadTotalTimeoutConstant,一次串口讀取超時時間的固定值,其實就是擔心估計的兩個字符之間傳輸時間不准確,然后又加上的一個超時時間;

讀取總超時時間的計算方法如下:

讀取操作總超時 = ReadTotalTimeoutMultiplier*讀取字符數 + ReadTotalTimeoutConstant;

讀取串口的時候有兩種超時,一種是兩個傳輸字符之間的時間間隔;如果讀取兩個字符之間的時間超過ReadIntervalTimeout的話,讀取串口的操作就會返回。另一種是讀取總時間超時,如果讀取操作時間超過剛計算的總超時的話,讀取操作也會返回;這里說的返回與串口的同步操作和異步操作中說的返回不同。同步和異步那種返回是指函數的返回,這里的返回是指串口讀取操作的返回;

WriteTotalTimeoutMultiplier,同讀操作相關的參數;

WriteTotalTimeoutConstant,同讀操作相關參數;

寫操作總超時時間計算方法如下:

寫操作總超時 = WriteTotalTimeoutMultiplier*寫入字符數 + WriteTotalTimeoutConstant;

寫入操作只有一種超時,只有總超時;
一般做以下設置:

TimeOuts.ReadIntervalTimeout = MAXDWORD;     // 把間隔超時設為最大,
                                             //把總超時設為0將導致ReadFile立即返回並完成操作
TimeOuts.ReadTotalTimeoutMultiplier = 0;     //讀時間系數
TimeOuts.ReadTotalTimeoutConstant = 0;       //讀時間常量
TimeOuts.WriteTotalTimeoutMultiplier = 50;   //總超時=時間系數*要求讀/寫的字符數+時間常量
TimeOuts.WriteTotalTimeoutConstant = 2000;   //設置寫超時以指定WriteComm成員函數中的

這樣設置后讀取完所有字符后就會返回,寫完操作后也會返回;

2.3 讀寫串口

使用兩個函數ReadFile和WriteFile。

ReadFile:

BOOL ReadFile(
HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped);

hFile,要讀取串口的句柄;

lpBuffer,要接收數據的緩沖區;

nNumberOfBytesToRead,要讀取數據的字節數;

lpNumberOfBytesRead,DWORD指針,保存實際讀入的數據的個數;

lpOverlapped,OVERLAPPED結構體,如果是同步串口通信串口設置為NULL。異步串口通信操作需要一個OVERLAPPED結構體指針;

異步讀取數據示例代碼如下:

DWORD dwRead;//這個值需要根據實際要讀取的數據
DWORD dwReadTrue = 0;
BOOL bRead;
//清除錯誤標志
COMSTAT ComStat;
DWORD dwErrorFlags;
ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
dwRead = (dwLength <= ComStat.cbInQue)?dwLength:ComStat.cbInQue;
if ( !dwRead ) return MAXDWORD;//輸入緩存區里面沒有內容
OVERLAPPED osRead;
memset(&osRead, 0, sizeof(OVERLAPPED));
HANDLE hEventRecv = CreateEvent(NULL, TRUE, FALSE, NULL);//這個事件必須為手動復位
osRead.hEvent = hEventRecv;
bRead = ReadFile(m_hCom, pBuffer, dwRead, &dwReadTrue, &osRead);
if ( !bRead && (ERROR_IO_PENDING == GetLastError()) )//讀操作未完成
{
	GetOverlappedResult(m_hCom, &osRead, &dwReadTrue, TRUE);//等待讀操作完成,暫時這樣操作
	PurgeComm(m_hCom, PURGE_RXABORT|PURGE_RXCLEAR);
	ResetEvent(m_hEventRecv);
	return dwReadTrue;
}
else if ( !bRead )
{
	ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
	PurgeComm(m_hCom, PURGE_RXABORT|PURGE_RXCLEAR);
	return MAXDWORD;
}
return dwReadTrue;

WriteFile:

BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped);

hFile,要寫入的串口句柄;

lpBuffer,要寫入的數據;

nNumberOfBytesToWrite,要寫入數據的字節數;

lpNumberOfBytesWritten,一個DWORD指針,實際寫入數據字節數;

lpOverlapped,OVERLAPPED結構體,如果是同步串口通信串口設置為NULL。異步串口通信操作需要一個OVERLAPPED結構體指針;

寫入數據示例代碼如下:

DWORD dwToWrite = dwLength;
DWORD dwWritten = 0;
BOOL bWrite;
COMSTAT ComStat;
DWORD dwErrorFlags;
ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
OVERLAPPED osWrite;
memset(&osWrite, 0, sizeof(OVERLAPPED));
HANDLE hEventSend = CreateEvent(NULL, TRUE, FALSE, NULL);//這個事件必須為手動復位
osWrite.hEvent = hEventSend;
bWrite = WriteFile(m_hCom, pBuffer, dwToWrite, &dwWritten, &osWrite);
if ( !bWrite && (ERROR_IO_PENDING == GetLastError()) )//串口寫操作未完成
{
	GetOverlappedResult(
	m_hCom,
	&osWrite,
	&dwWritten,
	TRUE);//等待寫操作完成,這里暫時使用這種操作方式
	PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
	ResetEvent(m_hEventSend);
	return dwWritten;
}
else if ( !bWrite )//串口寫入錯誤
{
	ClearCommError( m_hCom, &dwErrorFlags, &ComStat );
	PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
	return MAXDWORD;
}
return dwWritten;
2.4 關閉串口

串口屬於系統資源,打開了使用后要關閉掉。調用以下函數就行了:

BOOL CloseHandle(HANDLE hObject);
2.5 還有些需要用到的函數
BOOL PurgeComm(HANDLE hFile, DWORD dwFlags);

參數dwFlags指定要完成的操作,可以如下值:

PURGE_TXABORT 無論串口處於什么狀態,中斷所有寫操作並立即返回;

PURGE_RXABORT 無論串口處於什么狀態,中斷所有讀操作並立即返回;

PURGE_TXCLEAR 清空輸出緩沖區;

PURGE_RXCLEAR 清空輸入緩沖區;

3 CSerial成員函數和成員變量

為了方便使用,對串口的一些API函數進行了封裝:CSerial類。下表中有CSerial類的說明,具體的使用會有一個使用案例。CSerial類是對串口使用的一個封裝,主要包括打開串口,串口讀寫功能。該類使用比較簡單,不用復雜的配置,那倆可以馬上使用。還可以共享串口,不同的CSerial對象可以共享同一個串口,類中有一個對串口的引用計數,當與該串口綁定的對象都析構后會自動關閉該串口。類成員函數如下表:

成員函數 說明
CSerial(); 構造函數;
CSerial(CString strCom, DWORD dwBaudRate = 9600, BYTE byteSize = 8, BYTE byteCheck = NOPARITY, BYTE byteStop = ONESTOPBIT, BOOL bSync = FALSE); 構造函數;
WriteData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout = 1000); 向串口寫入數據
ReadData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout = 1000); 從串口讀出數據;
ClearError(); 清除串口錯誤,該函數會清除串口錯誤標志位。
Purge(); 同API函數中的PurgeComm()
SetComString(CString strCom); 設置串口號,比如“COM1”。只有當實例化CSerial對象是沒有傳入串口號或者想要修改串口號的時候使用該函數;
SetBaudRate(DWORD dwBaudRate); 設置串口通信的波特率,默認為9600;
SetByteSize(BYTE byteSize); 設置串口通信的數據位,默認為8位;
SetCheck(BYTE byteCheck); 設置串口通信的奇偶校驗,默認為不校驗;
SetStopBit(BYTE byteStopBit); 設置串口通信的停止位,默認為一位停止位;
SetSync(BOOL bSync); 設置是串口通信是同步還是異步,默認是同步通信;
SetInQue(DWORD dwInQue); 設置輸入緩沖區大小,默認為1024;
SetOutQue(DWORD dwOutQue); 設置輸出緩沖區大小,默認為1024;
SetTimeouts(COMMTIMEOUTS timeouts); 設置超時設置,如果更改默認超時設置的時候,調用該函數;
GetComString(); 獲取串口通信的串口號;
GetBaudRate(); 獲取串口通信的波特率;
GetByteSize(); 獲取串口通信的數據位數;
GetCheck(); 獲取串口通信是否使用奇偶校驗;
GetStopBit(); 獲取串口通信使用了幾位停止位;
GetSync(); 獲取串口通信的通信方式是同步還是異步;
GetInQue(); 獲取串口通信的輸入緩沖區大小;
GetOutQue(); 獲取串口通信的輸出緩沖區大小;
GetComStatus(); 獲取串口的狀態,一般查看串口是否正確打開;

4 使用說明

-------------------------------
CSerial();

構造函數,當實例化對象的時候,不傳入參數則調用該函數。如果不傳入參數該對象不能用來通信,需要通過SetComString(CString strCom);函數來設置該對象的串口號來正常通信;

-------------------------------
CSerial(
CString strCom,
DWORD dwBaudRate = 9600,
BYTE byteSize = 8,
BYTE byteCheck = NOPARITY,
BYTE byteStop = ONESTOPBIT,
BOOL bSync = FALSE);

構造函數,需要傳入串口號。其他參數都有默然值。如果需要更改的話,可以通過Setxx()函數來設置相應的參數;

-------------------------------
DWORD WriteData(
unsigned char *pBuffer,
DWORD dwLength,
DWORD dwTimeout = 1000);

pBuffer,要寫入的數據;

dwLength,寫入數據的長度;

dwTimeout,異步通信的時超時時間,暫時沒有使用,可以不管;

-------------------------------
DWORD ReadData(
unsigned char *pBuffer,
DWORD dwLength,
DWORD dwTimeout = 1000);

pBuffer,讀取數據緩沖區;

dwLength,讀取數據長度;

dwTimeout,異步通信時的超時時間,暫時沒有使用可以不管;

-------------------------------
void Purge(DWORD dwFlags);

與API中的PurgeComm函數功能一樣,dwFlags的取值可以是PURGE_TXABORT、PUTGE_RXABORT、PURGE_TXCLEAR、PURGE_RXCLEAR或者他們的組合;

-------------------------------

使用CSerial進行串口通信的例子:

CSerial SerialMeter("COM1");
DWORD dwWritten,dwReadTrue;
unsigned char TxData[11];
unsigned char RxData[11] = {0};
dwWritten = SerialMeter.WriteData(TxData, 11);
if( MAXDWORD == dwWritten )
{
	SerialMeter.ClearError();
	return;
}
Sleep(100);
dwReadTrue = m_SerialMeter.ReadData(RxData,11);
if ( MAXDWORD == dwReadTrue )
{
	m_SerialMeter.ClearError();
	return;
}

串口共享的時候只需要把一個對象賦值給另一個對象,或者用另一個對象來初始化一個對象就可以了。其他的就像使用單獨的一個對象一樣。串口的打開和關閉由類自己管理。串口共享串口例子:

CSerial SerialMeter("COM1");
CSerial SeralVelocty = SerialMeter;
DWORD dwWritten,dwReadTrue;
unsigned char TxData[11];
unsigned char RxData[11] = {0};
// 第一個串口對象讀寫
dwWritten = SerialMeter.WriteData(TxData, 11);
if( MAXDWORD == dwWritten )
{
	SerialMeter.ClearError();
	return;
}
Sleep(100);
dwReadTrue = m_SerialMeter.ReadData(RxData,11);

if ( MAXDWORD == dwReadTrue )
{
	m_SerialMeter.ClearError();
	return;
}
// 另一個串口對象讀寫
Sleep(100);
dwWritten = SeralVelocty.WriteData(TxData, 11);
if( MAXDWORD == dwWritten )
{
	SeralVelocty.ClearError();
	return;
}
Sleep(100);
dwReadTrue = SeralVelocty.ReadData(RxData,11);
if ( MAXDWORD == dwReadTrue )
{
	SeralVelocty.ClearError();
	return;
}

源代碼如下:
頭文件:

//CSerial類頭文件
#pragma once

class CSerial
{
public:
	CSerial();
	CSerial(
		CString strCom,
		DWORD dwBaudRate = 9600,
		BYTE byteSize = 8,
		BYTE byteCheck = NOPARITY,
		BYTE byteStop = ONESTOPBIT,
		BOOL bSync = FALSE);
	~CSerial();
	inline CSerial(const CSerial &com)
	{
		m_hCom = com.m_hCom;
		m_hEventSend = com.m_hEventSend;
		m_hEventRecv = com.m_hEventRecv;
		m_bSync = com.m_bSync;
		m_bIsFirst = com.m_bIsFirst;
		m_strCom = com.m_strCom;
		m_dwBaudRate = com.m_dwBaudRate;
		m_byteSize = com.m_byteSize;
		m_byteCheck = com.m_byteCheck;
		m_byteStop = com.m_byteStop;
		m_dwInQueue = com.m_dwInQueue;
		m_dwOutQueue = com.m_dwOutQueue;
	    m_Timeouts = com.m_Timeouts;
		m_nInitResult = com.m_nInitResult;
		m_pnReusecount = com.m_pnReusecount;
		if ( m_pnReusecount )
		{
			(* m_pnReusecount)++;
		}
	}
	inline CSerial & operator = (const CSerial &com)
	{
		m_hCom = com.m_hCom;
		m_hEventSend = com.m_hEventSend;
		m_hEventRecv = com.m_hEventRecv;
		m_bSync = com.m_bSync;
		m_bIsFirst = com.m_bIsFirst;
		m_strCom = com.m_strCom;
		m_dwBaudRate = com.m_dwBaudRate;
		m_byteSize = com.m_byteSize;
		m_byteCheck = com.m_byteCheck;
		m_byteStop = com.m_byteStop;
		m_dwInQueue = com.m_dwInQueue;
		m_dwOutQueue = com.m_dwOutQueue;
		m_Timeouts = com.m_Timeouts;
		m_nInitResult = com.m_nInitResult;
		m_pnReusecount = com.m_pnReusecount;
		if ( m_pnReusecount )
		{
			(* m_pnReusecount)++;
		}
		return * this;
	}
private:
	int *m_pnReusecount;
	BOOL m_bIsFirst;         //是否是第一次成功打開串口
	HANDLE m_hCom;           //串口句柄
	HANDLE m_hEventSend;     //發送數據事件
	HANDLE m_hEventRecv;     //接收數據事件

	BOOL m_bSync;            //同步傳輸還是異步傳輸,TRUE則為同步,FALSE為異步,默認為異步

	CString m_strCom;        //串口端口
	DWORD m_dwBaudRate;      //波特率
	BYTE m_byteSize;         //數據位
	BYTE m_byteCheck;        //校驗方式
	BYTE m_byteStop;         //停止位

	DWORD m_dwInQueue;       //串口輸入緩沖區     
	DWORD m_dwOutQueue;     //串口輸出緩沖區
	//超時相關變量
	COMMTIMEOUTS m_Timeouts;

	int m_nInitResult;
public:
	DWORD WriteData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout = 1000);  //dwTimeout為占位符,暫時未用,返回,MAXDWORD表示寫入錯誤
	DWORD ReadData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout = 1000);   //dwTimeout為占位符,暫時未用,返回,MAXDWORD表示讀取錯誤
	void CloseCom();
	int InitCom();//返回1,表示沒有錯誤,返回其他表示錯誤
	void ClearError();
	void Purge(DWORD dwFlags);

	int SetComString(CString strCom);
	int SetBaudRate(DWORD dwBaudRate);
	int SetByteSize(BYTE byteSize);
	int SetCheck(BYTE byteCheck);
	int SetStopBit(BYTE byteStopBit);
	int SetSync(BOOL bSync);
	int SetInQue(DWORD dwInQue);
	int SetOutQue(DWORD dwOutQue);
	int SetTimeouts(COMMTIMEOUTS timeouts);


	CString GetComString();
	DWORD GetBaudRate();
	BYTE GetByteSize();
	BYTE GetCheck();
	BYTE GetStopBit();
	BOOL GetSync();
	DWORD GetInQue();
	DWORD GetOutQue();
	int GetComStatus();//InitCom的返回值,觀察串口是否打開成功,0表示沒有串口名稱,1表示打開成功,MAXINI32表示串口打開錯誤
};

源文件:

#include "stdafx.h"
#include "Serial.h"

CSerial::CSerial()
{
	m_pnReusecount = NULL;
	m_hCom = NULL;
	m_hEventRecv = NULL;
	m_hEventSend = NULL;
	m_strCom = _T("");
	m_dwBaudRate = 9600;
	m_byteSize = 8;
	m_byteCheck = NOPARITY;
	m_byteStop = ONESTOPBIT;
	m_bSync = TRUE;
	m_bIsFirst = TRUE;
	//緩沖區變量初始化
	m_dwInQueue = 1024;
	m_dwOutQueue = 1024;
	//超時變量初始化
	COMMTIMEOUTS timeout = {MAXDWORD, 0, 0, 50, 1000};
	m_Timeouts = timeout;
}

CSerial::CSerial(
	CString strCom,
	DWORD dwBaudRate,
	BYTE byteSize,
	BYTE byteCheck,
	BYTE byteStop,
	BOOL bSync)
{
	m_pnReusecount = NULL;
	m_hCom = NULL;
	m_hEventRecv = NULL;
	m_hEventSend = NULL;
	m_strCom = strCom;
	m_dwBaudRate = dwBaudRate;
	m_byteSize = byteSize;
	m_byteCheck = byteCheck;
	m_byteStop = byteStop;
	m_bSync = bSync;
	m_bIsFirst = TRUE;
	//緩沖區變量初始化
	m_dwInQueue = 1024;
	m_dwOutQueue = 1024;
	//超時變量初始化
	COMMTIMEOUTS timeout = {MAXDWORD, 0, 0, 50, 1000};
	m_Timeouts = timeout;
	m_nInitResult = InitCom();
}

CSerial::~CSerial()
{
	if (m_pnReusecount)
	{
		(* m_pnReusecount)--;
		if( 0 >= *m_pnReusecount )
		{
			CloseCom();
			delete m_pnReusecount;
		}
	}
}

void CSerial::CloseCom()
{
	if ( m_hEventRecv )
	{
		CloseHandle(m_hEventRecv);
		m_hEventRecv = NULL;
	}
	if ( m_hEventSend )
	{
		CloseHandle(m_hEventSend);
		m_hEventSend = NULL;
	}
	if ( m_hCom )
	{
		CloseHandle(m_hCom);
		m_hCom = NULL;
	}
}

int CSerial::InitCom()
{
	if ( m_strCom == _T("") ) return 0;//如果串口傳入為空,則進行串口初始化
	if ( m_hCom )
	{
		CloseHandle(m_hCom);
	}
	if ( m_bSync )
	{
		m_hCom = CreateFile(m_strCom, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
	}
	else
	{
		m_hCom = CreateFile(m_strCom, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, NULL);
	}
	if ( m_hCom == INVALID_HANDLE_VALUE )//串口打開失敗
	{
		m_hCom = NULL;
		return 2;
	}
	//設置緩沖區大小,默認為1024;
	SetupComm(m_hCom, m_dwInQueue, m_dwOutQueue);
	//超時設置
	SetCommTimeouts(m_hCom, &m_Timeouts);
	//配置串口
	DCB dcb;
	GetCommState(m_hCom, &dcb);
	dcb.BaudRate = m_dwBaudRate;
	dcb.ByteSize = m_byteSize;
	dcb.Parity = m_byteCheck;
	dcb.StopBits = m_byteStop;
	SetCommState(m_hCom, &dcb);
	PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_RXCLEAR);
	if ( m_bIsFirst )
	{
		m_pnReusecount = new int;
		* m_pnReusecount = 1;
		m_bIsFirst = FALSE;
	}
	return 1;
}

void CSerial::ClearError()
{
	COMSTAT ComStat;
	DWORD dwErrorFlags;
	ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
	PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_RXCLEAR);
	if ( m_hEventRecv ) ResetEvent(m_hEventRecv);
	if ( m_hEventSend ) ResetEvent(m_hEventSend);
}

DWORD CSerial::WriteData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout)
{
	if ( !m_hCom ) return MAXDWORD;
	DWORD dwToWrite = dwLength;
	DWORD dwWritten = 0;
	BOOL bWrite;
	COMSTAT ComStat;
	DWORD dwErrorFlags;
	ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
	PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
	if ( m_bSync )//同步
	{
		bWrite =WriteFile(m_hCom, pBuffer, dwToWrite, &dwWritten, NULL);
		if ( bWrite )
		{
			return dwWritten;
		}
		else
		{
			return MAXDWORD;
		}
	}
	else//異步
	{
		OVERLAPPED osWrite;
		memset(&osWrite, 0, sizeof(OVERLAPPED));
		if ( !m_hEventSend )
		{
			m_hEventSend = CreateEvent(NULL, TRUE, FALSE, NULL);//這個事件必須為手動復位
		}
		osWrite.hEvent = m_hEventSend;
		bWrite = WriteFile(m_hCom, pBuffer, dwToWrite, &dwWritten, &osWrite);
		if ( !bWrite && (ERROR_IO_PENDING == GetLastError()) )//串口寫操作未完成
		{
			GetOverlappedResult(m_hCom, &osWrite, &dwWritten, TRUE);//等待寫操作完成,這里暫時使用這種操作方式
			PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
			ResetEvent(m_hEventSend);
			return dwWritten;
		}
		else if ( !bWrite )//串口寫入錯誤
		{
			ClearCommError( m_hCom, &dwErrorFlags, &ComStat );
			PurgeComm(m_hCom, PURGE_TXABORT|PURGE_TXCLEAR);
			return MAXDWORD;
		}
		return dwWritten;
	}
}

DWORD CSerial::ReadData(unsigned char *pBuffer, DWORD dwLength, DWORD dwTimeout)
{
	if ( !m_hCom ) return MAXDWORD;
	DWORD dwRead;
	DWORD dwReadTrue = 0;
	BOOL bRead;
	//清除錯誤標志
	COMSTAT ComStat;
	DWORD dwErrorFlags;
	ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
	dwRead = (dwLength <= ComStat.cbInQue)?dwLength:ComStat.cbInQue;
	if ( !dwRead ) return MAXDWORD;//輸入緩存區里面沒有內容
	if ( m_bSync )//同步
	{
		bRead = ReadFile(m_hCom, pBuffer, dwRead, &dwReadTrue, NULL);
		if ( bRead )
		{
			return dwReadTrue;
		}
		else
		{
			return MAXDWORD;
		}
	}
	else//異步
	{
		OVERLAPPED osRead;
		memset(&osRead, 0, sizeof(OVERLAPPED));
		if ( !m_hEventRecv )
		{
			m_hEventRecv = CreateEvent(NULL, TRUE, FALSE, NULL);//這個事件必須為手動復位
		}
		osRead.hEvent = m_hEventRecv;
		bRead = ReadFile(m_hCom, pBuffer, dwRead, &dwReadTrue, &osRead);
		if ( !bRead && (ERROR_IO_PENDING == GetLastError()) )//讀操作未完成
		{
			GetOverlappedResult(m_hCom, &osRead, &dwReadTrue, TRUE);//等待讀操作完成,暫時這樣操作
			PurgeComm(m_hCom, PURGE_RXABORT|PURGE_RXCLEAR);
			ResetEvent(m_hEventRecv);
			return dwReadTrue;
		}
		else if ( !bRead )
		{
			ClearCommError(m_hCom, &dwErrorFlags, &ComStat);
			PurgeComm(m_hCom, PURGE_RXABORT|PURGE_RXCLEAR);
			return MAXDWORD;
		}
		return dwReadTrue;
	}
}

void CSerial::Purge(DWORD dwFlags)
{
	PurgeComm(m_hCom, dwFlags);
}

int CSerial::SetComString(CString strCom)
{
	CString strTemp = m_strCom;
	m_strCom = strCom;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_strCom = strTemp;
		return -1;
	}
	return 0;
}

int CSerial::SetBaudRate(DWORD dwBaudRate)
{
	DWORD dwTemp = m_dwBaudRate;
	m_dwBaudRate = dwBaudRate;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_dwBaudRate = dwTemp;
		return -1;
	}
	return 0;
}

int CSerial::SetByteSize(BYTE byteSize)
{
	BYTE byteTemp = m_byteSize;
	m_byteSize = byteSize;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_byteSize = byteTemp;
		return -1;
	}
	return 0;
}

int CSerial::SetCheck(BYTE byteCheck)
{
	BYTE byteTemp = m_byteCheck;
	m_byteCheck = byteCheck;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_byteCheck = byteTemp;
		return -1;
	}
	return 0;
}

int CSerial::SetStopBit(BYTE byteStopBit)
{
	BYTE byteTemp = m_byteStop;
	m_byteStop = byteStopBit;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_byteStop = byteTemp;
		return -1;
	}
	return 0;
}

int CSerial::SetSync(BOOL bSync)
{
	BOOL bTemp = m_bSync;
	m_bSync = bSync;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_bSync = bTemp;
		return -1;
	}
	return 0;
}

int CSerial::SetInQue(DWORD dwInQue)
{
	DWORD dwTemp = m_dwInQueue;
	m_dwInQueue = dwInQue;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_dwInQueue = dwTemp;
		return -1;
	}
	return 0;
}

int CSerial::SetOutQue(DWORD dwOutQue)
{
	DWORD dwTemp = m_dwOutQueue;
	m_dwOutQueue = dwOutQue;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_dwOutQueue = dwTemp;
		return -1;
	}
	return 0;
}

int CSerial::SetTimeouts(COMMTIMEOUTS timeouts)
{
	COMMTIMEOUTS timeoutTemp = m_Timeouts;
	m_Timeouts = timeouts;
	m_nInitResult = InitCom();
	if ( 1 != m_nInitResult )
	{
		m_Timeouts = timeoutTemp;
		return -1;
	}
	return 0;
}

CString CSerial::GetComString()
{
	return m_strCom;
}

DWORD CSerial::GetBaudRate()
{
	return m_dwBaudRate;
}

BYTE CSerial::GetByteSize()
{
	return m_byteSize;
}

BYTE CSerial::GetCheck()
{
	return m_byteCheck;
}

BYTE CSerial::GetStopBit()
{
	return m_byteStop;
}

BOOL CSerial::GetSync()
{
	return m_bSync;
}

DWORD CSerial::GetInQue()
{
	return m_dwInQueue;
}

DWORD CSerial::GetOutQue()
{
	return m_dwOutQueue;
}

int CSerial::GetComStatus()
{
	return m_nInitResult;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM