[Windows] Windows API 串口通信


Windows 中通過Windows API 進行串口通信主要有以下步驟:

  1. 打開串口
  2. 配置串口
  3. 讀寫串口
  4. 關閉串口

打開串口

關鍵API: CreateFile

Windows 中進行設備的操作,第一步都是需要通過CreateFile 函數進行打開設備。

HANDLE WINAPI CreateFile(
  _In_     LPCTSTR               lpFileName,
  _In_     DWORD                 dwDesiredAccess,
  _In_     DWORD                 dwShareMode,
  _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  _In_     DWORD                 dwCreationDisposition,
  _In_     DWORD                 dwFlagsAndAttributes,
  _In_opt_ HANDLE                hTemplateFile
);

具體函數說明可以參考MSDN。

此處針對串口設備,稍微解釋一下各個參數:

lpFileName:串口名,常見szPort.Format(_T("\\\\.\\COM%d"), nPort),nPort 是串口號;

dwDesiredAccess:設置訪問權限,一般設置為GENERIC_READ | GENERIC_WRITE,即可讀可寫;

dwShareMode:串口不可共享,所以這個值必須是0;

lpSecurityAttributes:文件安全模式,必須設置為NULL

dwCreationDisposition:創建方式,串口必須是OPEN_EXISTING

dwFlagsAndAttributes:涉及到同步操作和異步操作的概念,具體可參考MSDN。一般如果同步的話就是設置為0;如果異步設置為FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED

hTemplateFile:文件句柄,對於串口通信必須設置為NULL

通過判斷函數返回值是否是有效的handle,判斷是否有成功打開串口設備。

配置串口

關鍵數據結構:DCB structure; COMMTIMEOUTS structure

關鍵API:BuildCommDCB; GetCommState; SetCommState; SetupComm; SetCommTimeouts

DCB structure:

DCB結構體中包含了許多信息,對於串口而已主要有波特率、數據位數、奇偶校驗和停止位數等信息。在查詢或配置串口的屬性時,都需要使用到DCB 結構體。

在使用SetCommState對端口進行配置前,需要使用BuildCommDCB 先build 好DCB 結構體;或是使用GetCommState 拿到DCB 結構體,然后再相應修改對應數據。

一般在使用SetCommState 配置串口后,還需要使用SetupComm 設置串口的緩沖區大小。

COMMTIMEOUTS structure:

這個結構體和SetCommTimeouts 函數主要是用來設置讀寫超時的信息的,可以具體參考MSDN。

其中讀寫串口的超時有兩種:間隔超時和總超時。

  • 間隔超時:是指在接收時兩個字符之間的最大時延。
  • 總超時:是指讀寫操作總共花費的最大時間。寫操作只支持總超時,而讀操作兩種超時均支持。

參考代碼

BOOL OpenComDev(int nPort, LPCTSTR lpDef, int nControl)
{
	CloseComDev();	

	//
	DCB dcb;
	CString szPort;
	CString szDcb;

	szPort.Format(_T("\\\\.\\COM%d"), nPort);
	if (lpDef == NULL)
	{
		szDcb.Format(_T("baud=1200 parity=N data=8 stop=1"));
	}
	else
	{
		szDcb = lpDef;
	}

	m_hDev = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, 
		0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL );
	if (m_hDev == INVALID_HANDLE_VALUE) 
	{
		DWORD dwError = GetLastError();
		return FALSE;
	}

	COMMTIMEOUTS CommTimeOuts;
	CommTimeOuts.ReadIntervalTimeout = MAXDWORD;	//0xFFFFFFFF;
	CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
	CommTimeOuts.ReadTotalTimeoutConstant = 0;
	CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
	CommTimeOuts.WriteTotalTimeoutConstant = 5000;
	SetCommTimeouts( m_hDev, &CommTimeOuts );

	FillMemory(&dcb, sizeof(dcb), 0);
	dcb.DCBlength = sizeof(dcb);
	if (!BuildCommDCB(szDcb, &dcb)) 
	{  
		goto _Fail;
	}

	// DCB is ready for use.
	if (!SetCommState(m_hDev, &dcb ) ||
		!SetupComm(m_hDev, 1024*16, 1024*16))
	{
		DWORD dwError = GetLastError();
		goto _Fail;
	}

	return TRUE;

_Fail:
	CloseComDev();
	return FALSE;
}

讀寫串口

關鍵數據結構:OVERLAPPED structure(當采用異步讀寫時需要)

關鍵API:ReadFile, WriteFile

在讀寫串口時,要注意是同步操作還是異步操作,這個是由上文"打開串口"中的CreateFile 參數決定的。

同步讀寫操作簡單,當調用ReadFile 和 WriteFile 時會阻塞,直到處理結束這兩個函數才會完成;

異步操作時,調用ReadFile 和 WriteFile 時會立刻返回,費事的IO操作將在后台執行,此時需要自己去設置Event 等去進行同步等待。

下面主要是異步操作的code,其中異步操作需要使用到OVERLAPPED structure,且event 是定義的全局變量。

BOOL UART_ReadData(HANDLE hIDComDev, LPVOID lpBuffer, int num)
{
	if (hIDComDev == NULL) return FALSE;

	OVERLAPPED overlapped;
	memset(&overlapped, 0, sizeof(OVERLAPPED));
	overlapped.hEvent = g_hReadEvent;
	ResetEvent(overlapped.hEvent);

	BOOL bReadStatus;
	DWORD dwBytesRead, dwErrorFlags;
	COMSTAT ComStat;

	ClearCommError(hIDComDev, &dwErrorFlags, &ComStat);
	if (!ComStat.cbInQue) return FALSE;

	dwBytesRead = (DWORD)ComStat.cbInQue;
	if (num < (int) dwBytesRead) 	dwBytesRead = (DWORD)num;

	bReadStatus = ReadFile(hIDComDev, lpBuffer, dwBytesRead, &dwBytesRead, &overlapped);
	if (!bReadStatus)
        {
		if (GetLastError() == ERROR_IO_PENDING)
                {
			WaitForSingleObject(overlapped.hEvent, 2000);
			return (int)dwBytesRead;
		}
		return FALSE;
	}

	return dwBytesRead;
}

BOOL UART_WriteData(HANDLE hIDComDev, LPCVOID lpBuffer, int num )
{
	if (hIDComDev == NULL) return FALSE ;

	BOOL bWriteStat;
	DWORD dwBytesWritten;

	OVERLAPPED overlapped;
	memset(&overlapped, 0, sizeof(OVERLAPPED));
	overlapped.hEvent = g_hWriteEvent;
	ResetEvent(overlapped.hEvent);

	bWriteStat = WriteFile(hIDComDev, (LPVOID) lpBuffer, num, &dwBytesWritten, &overlapped);
	if (!bWriteStat && (GetLastError() == ERROR_IO_PENDING))
        {
		if (WaitForSingleObject(overlapped.hEvent, 2000)) 
                {
                        dwBytesWritten = 0;
                }
		else
                {
			GetOverlappedResult(hIDComDev, &overlapped, &dwBytesWritten, FALSE);
			overlapped.Offset += dwBytesWritten;
		}
	}
	
	return dwBytesWritten;
}         

  

關閉串口

關鍵API:CloseHandle

關閉串口很簡單,只是將上文中"打開串口" 中獲得的Handle 正確close 即可。


免責聲明!

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



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