CH372 USB 칩을 사용하여 USB 데이터 통신을 구현할 때, CH372는 기본적으로 두 가지 동작 모드를 지원합니다. 내장 펌웨어 모드와 외장 펌웨어 모드가 그것입니다. 내장 펌웨어 모드로 설정하면 PC侧 애플리케이션에서 USB 칩 제조사가 제공하는 DLL을 호출해야 합니다. 기본적인 데이터收发 기능을 클래스 라이브러리로 캡슐화하면 이후 프로젝트에서 쉽게 재사용할 수 있습니다.
이 클래스 라이브러리는 두 개의 파일로 구성됩니다. USBConnector.h 헤더 파일과 USBConnector.cpp 구현 파일입니다.
USBConnector.h 헤더 파일
#ifndef __USBCONNECTOR_H__
#define __USBCONNECTOR_H__
#pragma once
#include "CH375DLL.H"
#pragma comment(lib,"CH375DLL")
#define EVENT_DEVICE_CONNECTED 1
#define EVENT_DEVICE_DISCONNECTED 2
#define ENDPOINT_1_RX 1
#define ENDPOINT_2_RX 2
class USBConnector
{
public:
USBConnector();
virtual ~USBConnector();
BOOL Disconnect(void);
BOOL Connect (CWnd *pWindowOwner,
ULONG ulDeviceIndex,
UINT unEndpoint1BufSize,
UINT unEndpoint2BufSize,
UINT unWriteTimeout,
UINT unReadTimeout,
UINT unEndpoint1Msg,
UINT unEndpoint2Msg,
UINT unConnectionMsg);
UINT Endpoint1Transmit(UCHAR *pTxBuffer, UINT unDataLength);
UINT Endpoint1Receive(UCHAR *pRxBuffer);
UINT Endpoint2Transmit(UCHAR *pTxBuffer, UINT unDataLength);
UINT Endpoint2Receive(UCHAR *pRxBuffer);
protected:
BOOL IsConnected(void) const;
BOOL ConfigureTimeouts(UINT unWriteTimeout, UINT unReadTimeout);
void SetDeviceIndex(ULONG ulIndex);
ULONG GetDeviceIndex(void) const;
BOOL InitializeThreads(void);
private:
static DWORD Endpoint1RxWorker(LPVOID lpParam);
static DWORD Endpoint2RxWorker(LPVOID lpParam);
CWnd * m_pWindow;
BOOL m_bInitialized;
ULONG m_ulDeviceIndex;
HANDLE m_hEndpoint1Down;
HANDLE m_hEndpoint1Up;
HANDLE m_hEndpoint2Down;
HANDLE m_hEndpoint2Up;
HANDLE m_hEndpoint1ExitEvent;
HANDLE m_hEndpoint2ExitEvent;
UCHAR *m_pEndpoint1Buffer;
UINT m_unEndpoint1Size;
UCHAR *m_pEndpoint2Buffer;
UINT m_unEndpoint2Size;
UINT m_unEndpoint1Message;
UINT m_unEndpoint2Message;
UINT m_unEndpoint1DataLen;
UINT m_unEndpoint2DataLen;
};
#endif
USBConnector.cpp 구현 파일
#include "StdAfx.h"
#include "USBConnector.h"
#include <assert.h>
UINT g_ulConnectionMessage = 0;
CWnd *g_pMainWindow = NULL;
VOID CALLBACK DeviceNotificationCallback(ULONG ulStatus)
{
if(ulStatus == CH375_DEVICE_ARRIVAL)
{
::SendMessage(g_pMainWindow->m_hWnd, g_ulConnectionMessage, (WPARAM)EVENT_DEVICE_CONNECTED, 0);
}
if(ulStatus == CH375_DEVICE_REMOVE)
{
::SendMessage(g_pMainWindow->m_hWnd, g_ulConnectionMessage, (WPARAM)EVENT_DEVICE_DISCONNECTED, 0);
}
}
USBConnector::USBConnector(void)
{
m_bInitialized = FALSE;
m_ulDeviceIndex = 0;
m_hEndpoint1Down = INVALID_HANDLE_VALUE;
m_hEndpoint1Up = INVALID_HANDLE_VALUE;
m_hEndpoint2Down = INVALID_HANDLE_VALUE;
m_hEndpoint2Up = INVALID_HANDLE_VALUE;
m_hEndpoint1ExitEvent = NULL;
m_hEndpoint2ExitEvent = NULL;
m_pEndpoint1Buffer = NULL;
m_pEndpoint2Buffer = NULL;
m_pWindow = NULL;
}
USBConnector::~USBConnector(void)
{
m_bInitialized = FALSE;
m_ulDeviceIndex = 0;
m_hEndpoint1Down = INVALID_HANDLE_VALUE;
m_hEndpoint1Up = INVALID_HANDLE_VALUE;
m_hEndpoint2Down = INVALID_HANDLE_VALUE;
m_hEndpoint2Up = INVALID_HANDLE_VALUE;
m_hEndpoint1ExitEvent = NULL;
m_hEndpoint2ExitEvent = NULL;
m_pEndpoint1Buffer = NULL;
m_pEndpoint2Buffer = NULL;
m_pWindow = NULL;
}
BOOL USBConnector::IsConnected(void) const
{
return m_bInitialized;
}
BOOL USBConnector::Disconnect(void)
{
ULONG ulIndex = GetDeviceIndex();
m_bInitialized = FALSE;
if (m_hEndpoint2Up != INVALID_HANDLE_VALUE)
{
CH375AbortRead(ulIndex);
}
if (m_hEndpoint2Down != INVALID_HANDLE_VALUE)
{
CH375AbortWrite(ulIndex);
}
if (m_hEndpoint1Up != INVALID_HANDLE_VALUE)
{
CH375AbortInter(ulIndex);
}
if (m_hEndpoint1ExitEvent)
{
SetEvent(m_hEndpoint1ExitEvent);
Sleep(10);
CloseHandle(m_hEndpoint1ExitEvent);
m_hEndpoint1ExitEvent = NULL;
}
if (m_hEndpoint2ExitEvent)
{
SetEvent(m_hEndpoint2ExitEvent);
Sleep(10);
CloseHandle(m_hEndpoint2ExitEvent);
m_hEndpoint2ExitEvent = NULL;
}
if (m_hEndpoint2Up != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hEndpoint2Up);
m_hEndpoint2Up = INVALID_HANDLE_VALUE;
}
if (m_hEndpoint2Down != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hEndpoint2Down);
m_hEndpoint2Down = INVALID_HANDLE_VALUE;
}
if (m_hEndpoint1Up != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hEndpoint1Up);
m_hEndpoint1Up = INVALID_HANDLE_VALUE;
}
if (m_hEndpoint1Down != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hEndpoint1Down);
m_hEndpoint1Down = INVALID_HANDLE_VALUE;
}
if (m_pEndpoint1Buffer)
{
delete[] m_pEndpoint1Buffer;
m_pEndpoint1Buffer = NULL;
}
if (m_pEndpoint2Buffer)
{
delete[] m_pEndpoint2Buffer;
m_pEndpoint2Buffer = NULL;
}
if (m_pWindow)
{
m_pWindow = NULL;
}
CH375CloseDevice(ulIndex);
return TRUE;
}
BOOL USBConnector::Connect(CWnd *pWindowOwner,
ULONG ulDeviceIndex,
UINT unEndpoint1BufSize,
UINT unEndpoint2BufSize,
UINT unWriteTimeout,
UINT unReadTimeout,
UINT unEndpoint1Msg,
UINT unEndpoint2Msg,
UINT unConnectionMsg)
{
assert(NULL != pWindowOwner);
g_pMainWindow = m_pWindow = pWindowOwner;
if (IsConnected())
{
Disconnect();
}
if (!m_pEndpoint1Buffer)
{
m_unEndpoint1Size = unEndpoint1BufSize;
m_pEndpoint1Buffer = new UCHAR[unEndpoint1BufSize];
}
if (!m_pEndpoint2Buffer)
{
m_unEndpoint2Size = unEndpoint2BufSize;
m_pEndpoint2Buffer = new UCHAR[unEndpoint2BufSize];
}
HANDLE hDevice = NULL;
SetDeviceIndex(ulDeviceIndex);
hDevice = CH375OpenDevice(ulDeviceIndex);
if (hDevice == INVALID_HANDLE_VALUE)
{
return FALSE;
}
CH375SetTimeout(ulDeviceIndex, unWriteTimeout, unReadTimeout);
WCHAR *pwcsDeviceName = new WCHAR[256];
MultiByteToWideChar(CP_ACP, 0, (LPSTR)CH375GetDeviceName(ulDeviceIndex), 256, pwcsDeviceName, 256);
m_hEndpoint1Up = CreateFile((LPCWSTR)&pwcsDeviceName[0],
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
m_hEndpoint1Down = CreateFile((LPCWSTR)&pwcsDeviceName[0],
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
m_hEndpoint2Up = CreateFile((LPCWSTR)&pwcsDeviceName[0],
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
m_hEndpoint2Down = CreateFile((LPCWSTR)&pwcsDeviceName[0],
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
delete[] pwcsDeviceName;
if (INVALID_HANDLE_VALUE == m_hEndpoint1Up || INVALID_HANDLE_VALUE == m_hEndpoint1Down ||
INVALID_HANDLE_VALUE == m_hEndpoint2Up || INVALID_HANDLE_VALUE == m_hEndpoint2Down)
{
return FALSE;
}
if (!InitializeThreads())
{
return FALSE;
}
m_unEndpoint1Message = unEndpoint1Msg;
m_unEndpoint2Message = unEndpoint2Msg;
g_ulConnectionMessage = unConnectionMsg;
CH375SetDeviceNotify(ulDeviceIndex, NULL, DeviceNotificationCallback);
m_bInitialized = TRUE;
return TRUE;
}
UINT USBConnector::Endpoint1Transmit(UCHAR *pTxBuffer, UINT unDataLength)
{
if (!IsConnected())
{
return 0;
}
if (NULL == pTxBuffer || 0 == unDataLength)
{
return 0;
}
if (unDataLength > 8)
{
unDataLength = 8;
}
if (!CH375WriteAuxData((ULONG)m_hEndpoint1Down, pTxBuffer, (PULONG)&unDataLength))
{
return 0;
}
return unDataLength;
}
UINT USBConnector::Endpoint2Transmit(UCHAR *pTxBuffer, UINT unDataLength)
{
if (!IsConnected())
{
return 0;
}
if (NULL == pTxBuffer || 0 == unDataLength)
{
return 0;
}
if (!CH375WriteData((ULONG)m_hEndpoint2Down, pTxBuffer, (PULONG)&unDataLength))
{
return 0;
}
return unDataLength;
}
BOOL USBConnector::InitializeThreads(void)
{
m_hEndpoint1ExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == m_hEndpoint1ExitEvent)
{
return FALSE;
}
ResetEvent(m_hEndpoint1ExitEvent);
m_hEndpoint2ExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == m_hEndpoint2ExitEvent)
{
return FALSE;
}
ResetEvent(m_hEndpoint2ExitEvent);
HANDLE hWorkerThread = NULL;
DWORD dwThreadId = 0;
Sleep(2);
hWorkerThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)Endpoint1RxWorker, this, 0, &dwThreadId);
if (NULL == hWorkerThread)
{
return FALSE;
}
hWorkerThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)Endpoint2RxWorker, this, 0, &dwThreadId);
if (NULL == hWorkerThread)
{
return FALSE;
}
CloseHandle(hWorkerThread);
hWorkerThread = NULL;
return TRUE;
}
DWORD USBConnector::Endpoint1RxWorker(LPVOID lpParam)
{
assert(NULL != lpParam);
USBConnector *pObj = (USBConnector *)lpParam;
assert(NULL != pObj);
ULONG ulDataSize = 0;
UCHAR *pRxBuffer = new UCHAR[pObj->m_unEndpoint1Size];
while (1)
{
if (WaitForSingleObject(pObj->m_hEndpoint1ExitEvent, 0) == WAIT_OBJECT_0)
{
break;
}
if (pObj->IsConnected())
{
ulDataSize = pObj->m_unEndpoint1Size;
if (CH375ReadInter((ULONG)(pObj->m_hEndpoint1Up), &pRxBuffer[0], &ulDataSize))
{
if (ulDataSize && pObj->m_pEndpoint1Buffer)
{
memset(pObj->m_pEndpoint1Buffer, 0, pObj->m_unEndpoint1Size);
memcpy(pObj->m_pEndpoint1Buffer, pRxBuffer, ulDataSize);
pObj->m_unEndpoint1DataLen = ulDataSize;
::SendMessage((pObj->m_pWindow)->m_hWnd,
pObj->m_unEndpoint1Message,
(WPARAM)ENDPOINT_1_RX, 0);
}
}
}
}
delete[] pRxBuffer;
Sleep(10);
return 0;
}
DWORD USBConnector::Endpoint2RxWorker(LPVOID lpParam)
{
assert(NULL != lpParam);
USBConnector *pObj = (USBConnector *)lpParam;
assert(NULL != pObj);
ULONG ulDataSize = 0;
UCHAR *pRxBuffer = new UCHAR[pObj->m_unEndpoint2Size];
UINT usStatus = 0;
while (1)
{
if (WaitForSingleObject(pObj->m_hEndpoint2ExitEvent, 0) == WAIT_OBJECT_0)
{
break;
}
if (pObj->IsConnected())
{
ulDataSize = pObj->m_unEndpoint2Size;
if (CH375ReadData((ULONG)(pObj->m_hEndpoint2Up), &pRxBuffer[0], &ulDataSize))
{
if (ulDataSize && pObj->m_pEndpoint2Buffer)
{
memset(pObj->m_pEndpoint2Buffer, 0, pObj->m_unEndpoint2Size);
memcpy(pObj->m_pEndpoint2Buffer, pRxBuffer, ulDataSize);
usStatus |= ENDPOINT_2_RX;
pObj->m_unEndpoint2DataLen = ulDataSize;
::SendMessage((pObj->m_pWindow)->m_hWnd,
pObj->m_unEndpoint2Message,
(WPARAM)ENDPOINT_2_RX, 0);
}
}
}
Sleep(1);
}
delete[] pRxBuffer;
Sleep(10);
return 0;
}
UINT USBConnector::Endpoint1Receive(UCHAR *pRxBuffer)
{
if (!IsConnected())
{
return 0;
}
memcpy(pRxBuffer, m_pEndpoint1Buffer, m_unEndpoint1Size);
return m_unEndpoint1DataLen;
}
UINT USBConnector::Endpoint2Receive(UCHAR *pRxBuffer)
{
if (!IsConnected())
{
return 0;
}
memcpy(pRxBuffer, m_pEndpoint2Buffer, m_unEndpoint2Size);
return m_unEndpoint2DataLen;
}
void USBConnector::SetDeviceIndex(ULONG ulIndex)
{
m_ulDeviceIndex = ulIndex;
}
ULONG USBConnector::GetDeviceIndex() const
{
return m_ulDeviceIndex;
}
이 클래스는 CH372/CH375 USB 칩의 내장 펌웨어 모드를 활용하여 USB 통신을 수행합니다. Endpoint1은 최대 8바이트의 소량 데이터 전송에 적합하며, Endpoint2는 대용량 데이터 전송에 사용됩니다. 수신은 별도의 스레드에서 비동기적으로 처리되며, 데이터 수신 시 지정된 윈도우 메시지를 통해 애플리케이션에 통보됩니다.