使用CH372 USB芯片進行USB數據通信時,CH372默認有2種模式,一種是內置固件模式,另外一種是外置固件模式。當設置CH372爲外置固件模式時,上位機界面可以脫離調用該USB芯片公司提供的dll,使用微軟提供的DDK文件提供的函數實現。
該類庫有兩個文件,分別是CHIDUSB.cpp和CHIDUSB.h。
CHIDUSB.h
1 /***************************************************
2 *作 者:溫子祺
3 *聯繫方式:[email protected]
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 *聯繫方式:[email protected]
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