CH372 USB 내장 펌웨어 클래스 구현

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는 대용량 데이터 전송에 사용됩니다. 수신은 별도의 스레드에서 비동기적으로 처리되며, 데이터 수신 시 지정된 윈도우 메시지를 통해 애플리케이션에 통보됩니다.

태그: CH372 USB MFC C++ embedded-firmware

6월 17일 01:22에 게시됨