zoukankan      html  css  js  c++  java
  • USB HID类的编写

       使用CH372 USB芯片进行USB数据通信时,CH372默认有2种模式,一种是内置固件模式,另外一种是外置固件模式。当设置CH372为外置固件模式时,上位机界面可以脱离调用该USB芯片公司提供的dll,使用微软提供的DDK文件提供的函数实现。

        该类库有两个文件,分别是CHIDUSB.cpp和CHIDUSB.h。

    CHIDUSB.h

    1 /***************************************************
    2 *作 者:温子祺
    3 *联系方式:wenziqi@hotmail.com
    4 *说 明:CHIDUSB.h
    5 不要忘记引用以下的代码
    6 #include "dbt.h"
    7
    8 extern "C" {
    9 #include "hidsdi.h"
    10 #include "setupapi.h"
    11 }
    12 #pragma comment(lib,"hid")
    13 #pragma comment(lib,"setupapi")
    14 ***************************************************/
    15 #ifndef __CHIDUSB_H__
    16  #define __CHIDUSB_H__
    17
    18 #include "dbt.h"
    19
    20  extern "C" {
    21 #include "hidsdi.h"
    22 #include "setupapi.h"
    23 }
    24
    25  #pragma comment(lib,"hid")
    26  #pragma comment(lib,"setupapi")
    27
    28
    29
    30  class CHIDUSB
    31 {
    32  public:
    33 CHIDUSB();
    34 virtual ~CHIDUSB();
    35
    36 BOOL Close(void);
    37 BOOL Open (CWnd *pPortOwner,
    38 DWORD VID,
    39 DWORD PID,
    40 UINT unUSBRecvBufSize,
    41 UINT unRecvMsg,
    42 UINT unConnectMsg
    43 );
    44
    45 UINT Send(UCHAR *pSendBytes,UINT unSendLen);
    46 UINT Recv(UCHAR *pRecvBytes);
    47 void GetDevicePath(CString &str);
    48
    49 protected:
    50
    51 BOOL Ready(void)const; //USB是否已经打开
    52 BOOL CreateThreadAndEvent(void); //创建线程和事件
    53 static
    54 DWORD RecvThread(LPVOID lpArg); //接收线程
    55
    56 void RegisterHIDDevice(CWnd *pPortOwner,GUID HidGuid);
    57
    58 private:
    59
    60 CWnd * m_pOwner;
    61
    62 BOOL m_bInit;
    63
    64 HIDP_CAPS m_Capabilities;
    65
    66 OVERLAPPED m_ovUSBRead;
    67 OVERLAPPED m_ovUSBWrite;
    68
    69 HANDLE m_hUSBWrite;
    70 HANDLE m_hUSBRead;
    71 HANDLE m_hRecvExitEvent;
    72
    73 UCHAR *m_szUSBRecvBuf;
    74 UINT m_unUSBRecvBufSize;
    75 UINT m_unRecvLength;
    76
    77 UINT m_unRecvMsg;
    78 UINT m_unConnectMsg;
    79 CString m_strDevicePath;
    80
    81 };
    82
    83
    84
    85 #endif

    CHIDUSB.cpp

    1 /***************************************************
    2 *作 者:温子祺
    3 *联系方式:wenziqi@hotmail.com
    4 *说 明:CHIDUSB.cpp
    5 ***************************************************/
    6 #include "StdAfx.h"
    7 #include "CHIDUSB.h"
    8 #include <assert.h> //使用断言
    9
    10 CHIDUSB::CHIDUSB()
    11 {
    12 m_pOwner=NULL;
    13 m_hUSBWrite=NULL;
    14 m_hUSBWrite=NULL;
    15 m_hUSBRead=NULL;
    16 m_hRecvExitEvent=NULL;
    17 m_unRecvLength=0;
    18 m_bInit=FALSE;
    19 m_szUSBRecvBuf=NULL;
    20 }
    21
    22 CHIDUSB::~CHIDUSB()
    23 {
    24 m_pOwner=NULL;
    25 m_hUSBWrite=NULL;
    26 m_hUSBWrite=NULL;
    27 m_hUSBRead=NULL;
    28 m_hRecvExitEvent=NULL;
    29 m_szUSBRecvBuf=NULL;
    30 }
    31
    32 BOOL CHIDUSB::Close(void)
    33 {
    34 m_bInit=FALSE;
    35
    36 if (m_hUSBRead)
    37 {
    38 CloseHandle(m_hUSBRead);
    39 m_hUSBRead=NULL;
    40 }
    41
    42 if (m_hUSBWrite)
    43 {
    44 CloseHandle(m_hUSBWrite);
    45 m_hUSBWrite=NULL;
    46 }
    47
    48 if (m_ovUSBRead.hEvent)
    49 {
    50 CloseHandle(m_ovUSBRead.hEvent);
    51 m_ovUSBRead.hEvent=NULL;
    52 }
    53
    54 if (m_ovUSBWrite.hEvent)
    55 {
    56 CloseHandle(m_ovUSBWrite.hEvent);
    57 m_ovUSBWrite.hEvent=NULL;
    58 }
    59
    60 if (m_hRecvExitEvent)
    61 {
    62 SetEvent(m_hRecvExitEvent); //退出线程
    63 CloseHandle(m_hRecvExitEvent);
    64 m_hRecvExitEvent=NULL;
    65 }
    66
    67 if (m_pOwner)
    68 {
    69 m_pOwner=NULL;
    70 }
    71
    72 if (m_szUSBRecvBuf)
    73 {
    74 delete []m_szUSBRecvBuf;
    75 m_szUSBRecvBuf=NULL;
    76 }
    77
    78
    79 return TRUE;
    80 }
    81
    82 BOOL CHIDUSB::Ready(void)const
    83 {
    84
    85 return m_bInit;
    86 }
    87
    88 BOOL CHIDUSB::CreateThreadAndEvent(void)
    89 {
    90 m_ovUSBRead.Offset=0;
    91 m_ovUSBRead.OffsetHigh=0;
    92 m_ovUSBRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
    93
    94 if (NULL==m_ovUSBRead.hEvent)
    95 {
    96 return FALSE;
    97 }
    98 else
    99 {
    100 ResetEvent(m_ovUSBRead.hEvent);
    101 }
    102
    103
    104 m_ovUSBWrite.Offset=0;
    105 m_ovUSBWrite.OffsetHigh=0;
    106 m_ovUSBWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
    107
    108 if (NULL==m_ovUSBWrite.hEvent)
    109 {
    110
    111 return FALSE;
    112 }
    113 else
    114 {
    115 ResetEvent(m_ovUSBWrite.hEvent);
    116 }
    117
    118
    119 m_hRecvExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL); /* 创建接收线程退出事件*/
    120
    121 if (NULL==m_hRecvExitEvent)
    122 {
    123 return FALSE;
    124 }
    125
    126 ResetEvent(m_hRecvExitEvent); //设置线程没有退出
    127
    128 HANDLE hThread=NULL;
    129 DWORD dwThreadID =0;
    130
    131 // 创建接收线程
    132 hThread=CreateThread( 0,
    133 0,
    134 (LPTHREAD_START_ROUTINE)RecvThread,
    135 this,
    136 0,
    137 &dwThreadID);
    138
    139 if (NULL==hThread)
    140 {
    141 return FALSE;
    142 }
    143
    144 CloseHandle(hThread);
    145 hThread=NULL;
    146
    147 return TRUE;
    148 }
    149
    150 BOOL CHIDUSB::Open(CWnd *pPortOwner,
    151 DWORD VID,
    152 DWORD PID,
    153 UINT unUSBRecvBufSize,
    154 UINT unRecvMsg,
    155 UINT unConnectMsg)
    156 {
    157 assert(NULL != pPortOwner);
    158
    159 m_pOwner = pPortOwner;
    160
    161 if (Ready())
    162 {
    163 Close();
    164 }
    165
    166 if (!m_szUSBRecvBuf)
    167 {
    168 m_szUSBRecvBuf=new UCHAR[unUSBRecvBufSize];
    169 m_unUSBRecvBufSize=unUSBRecvBufSize;
    170 }
    171
    172
    173 ULONG Required;
    174 //定义strUsbPath 设备路径
    175 //CString strUsbPath;
    176 //定义一个GUID的结构体HidGuid来保存HID设备的接口类GUID。
    177 GUID HidGuid;
    178 //定义一个DEVINFO的句柄hDevInfoSet来保存获取到的设备信息集合句柄。
    179 HDEVINFO hDevInfoSet;
    180 //定义MemberIndex,表示当前搜索到第几个设备,0表示第一个设备。
    181 DWORD MemberIndex;
    182 //DevInfoData,用来保存设备的驱动接口信息
    183 SP_DEVICE_INTERFACE_DATA DevInfoData;
    184 //定义一个BOOL变量,保存函数调用是否返回成功
    185 BOOL Result;
    186 //定义一个RequiredSize的变量,用来接收需要保存详细信息的缓冲长度。
    187 DWORD RequiredSize;
    188 //定义一个指向设备详细信息的结构体指针。
    189 PSP_DEVICE_INTERFACE_DETAIL_DATA pDevDetailData;
    190 //定义一个用来保存打开设备的句柄。
    191 HANDLE DevHandle;
    192 //定义一个HIDD_ATTRIBUTES的结构体变量,保存设备的属性。
    193 HIDD_ATTRIBUTES DevAttributes;
    194
    195 // Request to receive messages when a device is attached or removed.
    196 // Also see WM_DEVICECHANGE in BEGIN_MESSAGE_MAP(CUsbhidiocDlg, CDialog).
    197 DEV_BROADCAST_DEVICEINTERFACE DevBroadcastDeviceInterface;
    198
    199 //对DevInfoData结构体的cbSize初始化为结构体大小
    200 DevInfoData.cbSize=sizeof(DevInfoData);
    201 //对DevAttributes结构体的Size初始化为结构体大小
    202 DevAttributes.Size=sizeof(DevAttributes);
    203 //根据HidGuid来获取设备信息集合。其中Flags参数设置为
    204 //DIGCF_DEVICEINTERFACE|DIGCF_PRESENT,前者表示使用的GUID为
    205 //接口类GUID,后者表示只列举正在使用的设备,因为我们这里只
    206 //查找已经连接上的设备。返回的句柄保存在hDevinfo中。注意设备
    207 //信息集合在使用完毕后,要使用函数SetupDiDestroyDeviceInfoList
    208 //销毁,不然会造成内存泄漏。
    209
    210 //调用HidD_GetHidGuid函数获取HID设备的GUID,并保存在HidGuid中。
    211 HidD_GetHidGuid(&HidGuid);
    212
    213 hDevInfoSet=SetupDiGetClassDevs(&HidGuid,
    214 NULL,
    215 NULL,
    216 DIGCF_DEVICEINTERFACE|DIGCF_PRESENT);
    217 //然后对设备集合中每个设备进行列举,检查是否是我们要找的设备
    218 //当找到我们指定的设备,或者设备已经查找完毕时,就退出查找。
    219 //首先指向第一个设备,即将MemberIndex置为0。
    220 MemberIndex=0;
    221
    222
    223 while(1)
    224 {
    225 //调用SetupDiEnumDeviceInterfaces在设备信息集合中获取编号为
    226 //MemberIndex的设备信息。
    227 Result=SetupDiEnumDeviceInterfaces(hDevInfoSet,
    228 0,
    229 &HidGuid,
    230 MemberIndex,
    231 &DevInfoData);
    232
    233 //如果获取信息失败,则说明设备已经查找完毕,退出循环。
    234 if(Result==FALSE) break;
    235
    236 //将MemberIndex指向下一个设备
    237 MemberIndex++;
    238
    239 //如果获取信息成功,则继续获取该设备的详细信息。在获取设备
    240 //详细信息时,需要先知道保存详细信息需要多大的缓冲区,这通过
    241 //第一次调用函数SetupDiGetDeviceInterfaceDetail来获取。这时
    242 //提供缓冲区和长度都为NULL的参数,并提供一个用来保存需要多大
    243 //缓冲区的变量RequiredSize。
    244 Result=SetupDiGetDeviceInterfaceDetail(hDevInfoSet,
    245 &DevInfoData,
    246 NULL,
    247 0,
    248 &RequiredSize,
    249 NULL);
    250
    251 //然后,分配一个大小为RequiredSize缓冲区,用来保存设备详细信息。
    252 pDevDetailData=(PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(RequiredSize);
    253
    254 if(pDevDetailData==NULL) //如果内存不足,则直接返回。
    255 {
    256 TRACE("内存不足!");
    257 SetupDiDestroyDeviceInfoList(hDevInfoSet);
    258 return FALSE;
    259 }
    260
    261 //并设置pDevDetailData的cbSize为结构体的大小(注意只是结构体大小,
    262 //不包括后面缓冲区)。
    263 pDevDetailData->cbSize=sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
    264
    265 //然后再次调用SetupDiGetDeviceInterfaceDetail函数来获取设备的
    266 //详细信息。这次调用设置使用的缓冲区以及缓冲区大小。
    267 Result=SetupDiGetDeviceInterfaceDetail(hDevInfoSet,
    268 &DevInfoData,
    269 pDevDetailData,
    270 RequiredSize,
    271 &Required,
    272 NULL);
    273
    274 //将设备路径复制出来,然后销毁刚刚申请的内存。
    275 m_strDevicePath=pDevDetailData->DevicePath;
    276 free(pDevDetailData);
    277
    278 //如果调用失败,则查找下一个设备。
    279 if(Result==FALSE) continue;
    280
    281 //如果调用成功,则使用不带读写访问的CreateFile函数
    282 //来获取设备的属性,包括VID、PID、版本号等。
    283 //对于一些独占设备(例如USB键盘),使用读访问方式是无法打开的,
    284 //而使用不带读写访问的格式才可以打开这些设备,从而获取设备的属性。
    285 DevHandle=CreateFile(m_strDevicePath,
    286 0,
    287 FILE_SHARE_READ|FILE_SHARE_WRITE,
    288 (LPSECURITY_ATTRIBUTES)NULL,
    289 OPEN_EXISTING,
    290 0,
    291 NULL);
    292
    293 //如果打开成功,则获取设备属性。
    294 if(DevHandle!=INVALID_HANDLE_VALUE)
    295 {
    296 //获取设备的属性并保存在DevAttributes结构体中
    297 Result=HidD_GetAttributes(DevHandle,
    298 &DevAttributes);
    299
    300 //获取失败,查找下一个
    301 if(Result==FALSE)
    302 {
    303 //关闭刚刚打开的设备
    304 CloseHandle(DevHandle);
    305 DevHandle=NULL;
    306 continue;
    307 }
    308 //如果获取成功,则将属性中的VID、PID以及设备版本号与我们需要的
    309 //进行比较,如果都一致的话,则说明它就是我们要找的设备。
    310 if(DevAttributes.VendorID==VID) //如果VID相等
    311 if(DevAttributes.ProductID==PID) //并且PID相等
    312 //if(DevAttributes.VersionNumber==StUsbID.m_dwPVN) //并且设备版本号相等
    313 {
    314 DevBroadcastDeviceInterface.dbcc_size = sizeof(DevBroadcastDeviceInterface);
    315 DevBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    316 DevBroadcastDeviceInterface.dbcc_classguid = HidGuid;
    317 //获取设备属性结构体
    318
    319 PHIDP_PREPARSED_DATA PreparsedData;
    320
    321 HidD_GetPreparsedData(DevHandle,
    322 &PreparsedData
    323 );
    324
    325 HidP_GetCaps (PreparsedData,
    326 &m_Capabilities
    327 );
    328 //释放资源
    329 HidD_FreePreparsedData(PreparsedData);
    330
    331 //那么就是我们要找的设备,分别使用读写方式打开之,并保存其句柄
    332 //并且选择为异步访问方式。
    333 //读方式打开设备
    334
    335 m_hUSBRead=CreateFile(m_strDevicePath,
    336 GENERIC_READ,
    337 FILE_SHARE_READ|FILE_SHARE_WRITE,
    338 (LPSECURITY_ATTRIBUTES)NULL,
    339 OPEN_EXISTING,
    340 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
    341 NULL);
    342
    343 if (INVALID_HANDLE_VALUE==m_hUSBRead)
    344 {
    345 TRACE("读访问打开HidUsb设备失败......!");
    346 }
    347 else
    348 {
    349 TRACE("读访问打开HidUsb设备成功......!");
    350
    351 }
    352
    353 //写方式打开设备
    354 m_hUSBWrite=CreateFile(m_strDevicePath,
    355 GENERIC_WRITE,
    356 FILE_SHARE_READ|FILE_SHARE_WRITE,
    357 (LPSECURITY_ATTRIBUTES)NULL,
    358 OPEN_EXISTING,
    359 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
    360 NULL);
    361
    362 if (INVALID_HANDLE_VALUE==m_hUSBWrite)
    363 {
    364 TRACE("写访问打开HidUsb设备失败......!");
    365 }
    366 else
    367 {
    368 TRACE("写访问打开HidUsb设备成功......!");
    369
    370 }
    371
    372 if (m_hUSBRead == INVALID_HANDLE_VALUE
    373 &&m_hUSBWrite == INVALID_HANDLE_VALUE)
    374 {
    375 return FALSE;
    376 }
    377
    378 if (!CreateThreadAndEvent())
    379 {
    380 return FALSE;
    381 }
    382
    383 m_bInit=TRUE;
    384
    385 m_unRecvMsg=unRecvMsg;
    386 m_unConnectMsg=unConnectMsg;
    387
    388 RegisterHIDDevice(pPortOwner,HidGuid);
    389
    390 return TRUE;
    391 }
    392 }
    393 //如果打开失败,则查找下一个设备
    394 else continue;
    395 }
    396
    397
    398 //调用SetupDiDestroyDeviceInfoList函数销毁设备信息集合
    399 SetupDiDestroyDeviceInfoList(hDevInfoSet);
    400
    401 return FALSE;
    402
    403 }
    404 UINT CHIDUSB::Send(UCHAR *pSendBytes,UINT unSendLen)
    405 {
    406 if (NULL == pSendBytes ||0==unSendLen)
    407 {
    408 return 0;
    409 }
    410
    411 if (m_hUSBWrite==INVALID_HANDLE_VALUE \
    412 ||m_hUSBWrite==NULL)
    413 {
    414 return 0;
    415 }
    416
    417 UCHAR szSendBuf[65] ={0};
    418 DWORD dwSendBytes=0;
    419 INT rt=0;
    420
    421 //HID发送报告第一个字节必须为0
    422 //所以发送总长度为0x41=65
    423
    424 memcpy(&szSendBuf[1],pSendBytes,64);
    425
    426 rt=WriteFile(m_hUSBWrite,
    427 szSendBuf,
    428 m_Capabilities.OutputReportByteLength,
    429 NULL,
    430 &m_ovUSBWrite);
    431
    432 WaitForSingleObject(m_ovUSBWrite.hEvent,3000);
    433 ResetEvent (m_ovUSBWrite.hEvent);
    434
    435 GetOverlappedResult(m_hUSBWrite,&m_ovUSBWrite,&dwSendBytes,TRUE);
    436
    437 return (UINT)(dwSendBytes-1);
    438 }
    439
    440 UINT CHIDUSB::Recv(UCHAR *pRecvBytes)
    441 {
    442 if (NULL == pRecvBytes || 0==m_unRecvLength)
    443 {
    444 return 0;
    445 }
    446
    447 if (m_hUSBRead==INVALID_HANDLE_VALUE \
    448 ||m_hUSBRead==NULL)
    449 {
    450 return 0;
    451 }
    452
    453 UINT unRecvLength=m_unRecvLength;
    454
    455 m_unRecvLength=0;
    456
    457 memcpy(pRecvBytes,m_szUSBRecvBuf,64);
    458
    459 return unRecvLength;
    460 }
    461
    462 DWORD CHIDUSB::RecvThread(LPVOID lpArg)
    463 {
    464 assert(NULL != lpArg);
    465
    466 CHIDUSB *pArg=(CHIDUSB *)lpArg;
    467
    468 assert(NULL != pArg);
    469
    470 UCHAR szRecvBuf[65]={0};
    471 DWORD dwRecvBytes=0;
    472
    473 while(1)
    474 {
    475 if (WaitForSingleObject(pArg->m_hRecvExitEvent,0)==WAIT_OBJECT_0)
    476 {
    477 break; //线程退出
    478 }
    479
    480 if (pArg->Ready())
    481 {
    482 memset(pArg->m_szUSBRecvBuf,0,sizeof(pArg->m_szUSBRecvBuf));
    483 memset(szRecvBuf,0,sizeof(szRecvBuf));
    484
    485 ReadFile(pArg->m_hUSBRead,
    486 szRecvBuf,
    487 pArg->m_Capabilities.InputReportByteLength,
    488 NULL,
    489 &pArg->m_ovUSBRead
    490 );
    491
    492 WaitForSingleObject(pArg->m_ovUSBRead.hEvent,INFINITE);
    493 ResetEvent(pArg->m_ovUSBRead.hEvent);
    494 //通过GetOverlappedResult函数来获取实际读取到的字节数。
    495 GetOverlappedResult(pArg->m_hUSBRead,&pArg->m_ovUSBRead,&dwRecvBytes,TRUE);
    496
    497 if (dwRecvBytes)
    498 {
    499 memcpy(pArg->m_szUSBRecvBuf,&szRecvBuf[1],64);
    500 pArg->m_unRecvLength=(UINT)(dwRecvBytes-1);//默认返回65个字节,所以要减1
    501 dwRecvBytes=0;
    502 //完成这个消息才进行下个操作,因而不需要加上同步事件。
    503 //如果使用PostMessage()就需要加上同步事件
    504 ::SendMessage((pArg->m_pOwner)->m_hWnd,
    505 pArg->m_unRecvMsg,
    506 0,
    507 0);
    508
    509 }
    510
    511 }
    512 Sleep(1);
    513 }
    514
    515 Sleep(10);
    516
    517 return 0;
    518 }
    519
    520 void CHIDUSB::GetDevicePath(CString &str)
    521 {
    522 str=m_strDevicePath;
    523 }
    524
    525 void CHIDUSB::RegisterHIDDevice(CWnd *pPortOwner,GUID HidGuid)
    526 {
    527 DEV_BROADCAST_DEVICEINTERFACE DevBroadcastDeviceInterface;
    528 HDEVNOTIFY DeviceNotificationHandle;
    529
    530 DevBroadcastDeviceInterface.dbcc_size = sizeof(DevBroadcastDeviceInterface);
    531 DevBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    532 DevBroadcastDeviceInterface.dbcc_classguid = HidGuid;
    533
    534 DeviceNotificationHandle =
    535 RegisterDeviceNotification(pPortOwner->m_hWnd, &DevBroadcastDeviceInterface, DEVICE_NOTIFY_WINDOW_HANDLE);
    536 }
    537
    538
  • 相关阅读:
    Python常用模块——第三方开源模块的安装使用
    Python常用模块——模块介绍与导入
    Linux文本编辑器vim
    Linux文件和文件夹的操作
    网络编程之IO模型——selectors模块
    设计模式学习(19)- 中介者模式
    设计模式学习(18)- 迭代器模式
    设计模式学习(17)- 解释器模式
    设计模式学习(16)- 命令模式
    设计模式学习(15)- 责任链模式
  • 原文地址:https://www.cnblogs.com/wenziqi/p/1769213.html
Copyright © 2011-2022 走看看