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
本文介绍如何使用CH372 USB芯片在外置固件模式下进行USB HID数据通信,通过自定义类库CHIDUSB实现与上位机软件的数据交互。


1462

被折叠的 条评论
为什么被折叠?



