最近有一个需求,需要检测一个物体的运动速度,经过研究我决定使用激光距离传感器来完成这个要求。在模块选择的问题上,我再次遇到了“面粉比面包贵” 的问题—–激光测距模块价格比成品要高。最终选择的是带有USB接口的优利德 UT395B。
最远可以达到150米,我选择的是可以达到70米的
他自带一个软件可以在 Windows 下获得距离,因此可以使用 USBlyzer 来抓取通讯数据数据分析协议。首先抓取Descriptor:
USB 输入设备
Connection Status | Device connected |
Current Configuration | 1 |
Speed | Full (12 Mbit/s) |
Device Address | 12 |
Number Of Open Pipes | 2 |
Device Descriptor SDWAY
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 12h | |
1 | bDescriptorType | 1 | 01h | Device |
2 | bcdUSB | 2 | 0200h | USB Spec 2.0 |
4 | bDeviceClass | 1 | 00h | Class info in Ifc Descriptors |
5 | bDeviceSubClass | 1 | 00h | |
6 | bDeviceProtocol | 1 | 00h | |
7 | bMaxPacketSize0 | 1 | 40h | 64 bytes |
8 | idVendor | 2 | 0483h | SGS Thomson Microelectronics |
10 | idProduct | 2 | 5751h | |
12 | bcdDevice | 2 | 0200h | 2.00 |
14 | iManufacturer | 1 | 01h | “MyUSB_HID” |
15 | iProduct | 1 | 02h | ” SDWAY “ |
16 | iSerialNumber | 1 | 03h | |
17 | bNumConfigurations | 1 | 01h |
Configuration Descriptor 1
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 09h | |
1 | bDescriptorType | 1 | 02h | Configuration |
2 | wTotalLength | 2 | 0029h | |
4 | bNumInterfaces | 1 | 01h | |
5 | bConfigurationValue | 1 | 01h | |
6 | iConfiguration | 1 | 00h | |
7 | bmAttributes | 1 | C0h | Self Powered |
4..0: Reserved | …00000 | |||
5: Remote Wakeup | ..0….. | No | ||
6: Self Powered | .1…… | Yes | ||
7: Reserved (set to one) (bus-powered for 1.0) |
1……. | |||
8 | bMaxPower | 1 | C0h | 384 mA |
Interface Descriptor 0/0 HID, 2 Endpoints
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 09h | |
1 | bDescriptorType | 1 | 04h | Interface |
2 | bInterfaceNumber | 1 | 00h | |
3 | bAlternateSetting | 1 | 00h | |
4 | bNumEndpoints | 1 | 02h | |
5 | bInterfaceClass | 1 | 03h | HID |
6 | bInterfaceSubClass | 1 | 00h | |
7 | bInterfaceProtocol | 1 | 00h | |
8 | iInterface | 1 | 00h |
HID Descriptor
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 09h | |
1 | bDescriptorType | 1 | 21h | HID |
2 | bcdHID | 2 | 0110h | 1.10 |
4 | bCountryCode | 1 | 00h | |
5 | bNumDescriptors | 1 | 01h | |
6 | bDescriptorType | 1 | 22h | Report |
7 | wDescriptorLength | 2 | 0021h | 33 bytes |
Endpoint Descriptor 82 2 In, Interrupt, 10 ms
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 07h | |
1 | bDescriptorType | 1 | 05h | Endpoint |
2 | bEndpointAddress | 1 | 82h | 2 In |
3 | bmAttributes | 1 | 03h | Interrupt |
1..0: Transfer Type | ……11 | Interrupt | ||
7..2: Reserved | 000000.. | |||
4 | wMaxPacketSize | 2 | 0040h | 64 bytes |
6 | bInterval | 1 | 0Ah | 10 ms |
Endpoint Descriptor 01 1 Out, Interrupt, 16 ms
Offset | Field | Size | Value | Description |
0 | bLength | 1 | 07h | |
1 | bDescriptorType | 1 | 05h | Endpoint |
2 | bEndpointAddress | 1 | 01h | 1 Out |
3 | bmAttributes | 1 | 03h | Interrupt |
1..0: Transfer Type | ……11 | Interrupt | ||
7..2: Reserved | 000000.. | |||
4 | wMaxPacketSize | 2 | 0040h | 64 bytes |
6 | bInterval | 1 | 10h | 16 ms |
Interface 0 HID Report Descriptor
Item Tag (Value) | Raw Data |
Usage Page (Bar Code Scanner) | 05 8C |
Usage | 09 01 |
Collection (Application) | A1 01 |
Usage | 09 03 |
Logical Minimum (0) | 15 00 |
Logical Maximum (-256) | 26 00 FF |
Report Size (8) | 75 08 |
Report Count (24) | 95 18 |
Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) | 81 02 |
Usage | 09 04 |
Logical Minimum (0) | 15 00 |
Logical Maximum (-256) | 26 00 FF |
Report Size (8) | 75 08 |
Report Count (24) | 95 18 |
Output (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) | 91 02 |
End Collection | C0 |
从上面的结果可以看出这个设备是一个自定义的 HID设备,因此,需要进步一抓取通讯数据。
上面是一次完整的获得测量距离的过程,一共有8笔数据,4 笔是主机发送给设备,4笔是设备的返回值。 OUTPUT Report是主机发送给 UT395B的数据。第一个数据包: 41 54 4B 30 30 31 23 00 00 00 00 41 00 00 00 54 00 00 00 44 00 00 00 00
发送到设备之后,设备会打开激光(手册上说这是打开瞄准的功能)。第5笔数据和第1笔数据相同,设备会关闭激光。
经过研发总结: 41 54 4B 30 30 31 23 …….这个命令是打开/关闭激光。 41 54 44 30 30 31 23 …….是主机要求返回距离信息的命令。
上面8个数据的含义如下:
- 主机要求打开激光
- 设备 ECHO
- 主机要求获得上一笔数据
- 设备返回上一笔数据
- 主机要求关闭激光
- 设备 ECHO
- 主机要求获得距离数据
- 设备返回数据,就是我们需要的距离信息
一个典型的返回值:
41 54 44 00 00 3A E8 00 …… 其中的 3A E8 就是距离信息。0x3AE8=15080 对应显示在屏幕上的距离是1.508米。经过多次实验,显示小数点后3位的数值,但是实际上的数据是小数点后4位,最后一位会进行四舍五入。
最终编写 Windows 下 Console 代码如下:
//http://blog.csdn.net/xuxinhua/article/details/6329182 #include "stdafx.h" #include <stdio.h> #include <tchar.h> #include <windows.h> #include <setupapi.h> extern "C" { void __stdcall HidD_GetHidGuid ( OUT LPGUID HidGuid ); typedef struct _HIDD_ATTRIBUTES { ULONG Size; // = sizeof (struct _HIDD_ATTRIBUTES) // // Vendor ids of this hid device // USHORT VendorID; USHORT ProductID; USHORT VersionNumber; // // Additional fields will be added to the end of this structure. // } HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; BOOLEAN __stdcall HidD_GetAttributes ( IN HANDLE HidDeviceObject, OUT PHIDD_ATTRIBUTES Attributes ); BOOLEAN __stdcall HidD_SetFeature( _In_ HANDLE HidDeviceObject, _In_reads_bytes_(ReportBufferLength) PVOID ReportBuffer, _In_ ULONG ReportBufferLength ); } #pragma comment( lib, "hid.lib" ) #pragma comment( lib, "setupapi.lib" ) void Switch(HANDLE hUsb) { BOOL Result; //Output DataµÄ»º³åÇø£¬ÒòΪÏÂÃæ·¢ËÍ HID ±¨ÎÄÐèÒª9×Ö½Ú£¬ËùÒÔÕâÀïÖ±½Ó°´ÕÕ×î´óµÄ¶¨Òå UCHAR WriteReportBuffer[25]; WriteReportBuffer[00] = 0x00; WriteReportBuffer[1] = 0x41; WriteReportBuffer[2] = 0x54; WriteReportBuffer[3] = 0x4b; WriteReportBuffer[4] = 0x30; WriteReportBuffer[5] = 0x30; WriteReportBuffer[6] = 0x31; WriteReportBuffer[7] = 0x23; WriteReportBuffer[8] = 0x00; WriteReportBuffer[9] = 0x00; WriteReportBuffer[10] = 0x00; WriteReportBuffer[11] = 0x00; WriteReportBuffer[12] = 0x41; WriteReportBuffer[13] = 0x00; WriteReportBuffer[14] = 0x00; WriteReportBuffer[15] = 0x00; WriteReportBuffer[16] = 0x54; WriteReportBuffer[17] = 0x00; WriteReportBuffer[18] = 0x00; WriteReportBuffer[19] = 0x00; WriteReportBuffer[20] = 0x44; WriteReportBuffer[21] = 0x00; WriteReportBuffer[22] = 0x00; WriteReportBuffer[23] = 0x00; WriteReportBuffer[24] = 0x00; DWORD lpNumberOfBytesWritten; //µ÷ÓÃWriteFileº¯Êý·¢ËÍÊý¾Ý //±¨Îij¤¶ÈÊÇ9×Ö½Ú Result = WriteFile(hUsb, WriteReportBuffer, 25, &lpNumberOfBytesWritten, NULL); printf("Written %d bytes Result [%d]\n", lpNumberOfBytesWritten, Result); } void SendData(HANDLE hUsb) { BOOL Result; //Output DataµÄ»º³åÇø£¬ÒòΪÏÂÃæ·¢ËÍ HID ±¨ÎÄÐèÒª9×Ö½Ú£¬ËùÒÔÕâÀïÖ±½Ó°´ÕÕ×î´óµÄ¶¨Òå UCHAR WriteReportBuffer[25]; WriteReportBuffer[00] = 0x00; WriteReportBuffer[1] = 0x41; WriteReportBuffer[2] = 0x54; WriteReportBuffer[3] = 0x44; WriteReportBuffer[4] = 0x30; WriteReportBuffer[5] = 0x30; WriteReportBuffer[6] = 0x31; WriteReportBuffer[7] = 0x23; WriteReportBuffer[8] = 0x00; WriteReportBuffer[9] = 0x00; WriteReportBuffer[10] = 0x00; WriteReportBuffer[11] = 0x00; WriteReportBuffer[12] = 0x41; WriteReportBuffer[13] = 0x00; WriteReportBuffer[14] = 0x00; WriteReportBuffer[15] = 0x00; WriteReportBuffer[16] = 0x54; WriteReportBuffer[17] = 0x00; WriteReportBuffer[18] = 0x00; WriteReportBuffer[19] = 0x00; WriteReportBuffer[20] = 0x44; WriteReportBuffer[21] = 0x00; WriteReportBuffer[22] = 0x00; WriteReportBuffer[23] = 0x00; WriteReportBuffer[24] = 0x00; DWORD lpNumberOfBytesWritten; //µ÷ÓÃWriteFileº¯Êý·¢ËÍÊý¾Ý //±¨Îij¤¶ÈÊÇ9×Ö½Ú Result = WriteFile(hUsb, WriteReportBuffer, 25, &lpNumberOfBytesWritten, NULL); printf("Written %d bytes Result [%d]\n", lpNumberOfBytesWritten, Result); } void GetData(HANDLE hUsb) { //½ÓÊÕ±¨¸æµÄ»º³åÇø£¬1×Ö½Ú±¨¸æID+8×Ö½Ú±¨¸æÊý¾Ý¡£ UCHAR ReadReportBuffer[25]; DWORD lpNumberOfBytesRead; UINT LastError; BOOL Result; DWORD tmp; //µ÷ÓÃReadFile ½ÓÊÕÊý¾Ý Result = ReadFile( hUsb, ReadReportBuffer, 25, &lpNumberOfBytesRead, NULL); //printf("Read %d bytes\n", lpNumberOfBytesRead); //Èç¹ûº¯Êý·µ»Øʧ°Ü£¬Ôò¿ÉÄÜÊÇÕæµÄʧ°Ü£¬Ò²¿ÉÄÜÊÇIO¹ÒÆð if (Result == FALSE) { //»ñÈ¡×îºó´íÎó´úÂë LastError = GetLastError(); //¿´ÊÇ·ñÊÇÕæµÄIO¹Ò if ((LastError == ERROR_IO_PENDING) || (LastError == ERROR_SUCCESS)) { exit; } //·ñÔò£¬ÊǺ¯Êýµ÷ÓÃʱ·¢Éú´íÎó£¬ÏÔʾ´íÎó´úÂë else { printf("Sending error£º%d \n", LastError); //Èç¹û×îºó´íÎóΪ1£¬ËµÃ÷¸ÃÉ豸²»Ö§³Ö¸Ãº¯Êý¡£ if (LastError == 1) { printf("This device doesn't support WriteFile function \n"); } } } else //Õý³£¶ÁÈ¡ { for (DWORD Index = 0; Index < lpNumberOfBytesRead; Index++) { printf("%02X ", ReadReportBuffer[Index]); } printf("\n"); if (0x44 == ReadReportBuffer[0x03]) { tmp = (ReadReportBuffer[0x06] << 8) + ReadReportBuffer[0x07]; printf("%d.%d\n", tmp / 10000, (tmp - tmp /10000 * 10000 +5)/10); } } printf("\n"); } int main(array<System::String ^> ^args) { GUID HidGuid; BOOL Result; int counter=-1; //»ñÈ¡HIDÉ豸µÄ½Ó¿ÚÀàGUDI HidD_GetHidGuid(&HidGuid); //Êä³öһϿ´¿´GUID printf("HID GUID: {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n" , HidGuid.Data1, HidGuid.Data2, HidGuid.Data3 , HidGuid.Data4[0], HidGuid.Data4[1], HidGuid.Data4[2], HidGuid.Data4[3], HidGuid.Data4[4] , HidGuid.Data4[5], HidGuid.Data4[6], HidGuid.Data4[7] ); //¸ù¾Ý»ñµÃµÄGUIDö¾ÙHIDÉ豸 HDEVINFO hDevInfo = SetupDiGetClassDevs( &HidGuid, NULL, 0, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE ); if( INVALID_HANDLE_VALUE != hDevInfo ) { SP_DEVICE_INTERFACE_DATA strtInterfaceData = { sizeof(SP_DEVICE_INTERFACE_DATA) }; for( DWORD index=0; SetupDiEnumDeviceInterfaces(hDevInfo,NULL,&HidGuid,index,&strtInterfaceData); ++index ) { char buf[1000]; SP_DEVICE_INTERFACE_DETAIL_DATA& strtDetailData = (SP_DEVICE_INTERFACE_DETAIL_DATA&)buf[0]; strtDetailData.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); if( SetupDiGetDeviceInterfaceDetail(hDevInfo,&strtInterfaceData,&strtDetailData,_countof(buf),NULL,NULL) ) { printf("[%d] path: %ls\n", index, strtDetailData.DevicePath); //ÕâÀï´ò¿ªµÄÓпÉÄÜÊÇUSB¼üÅÌÊó±êÕâÑù±È½ÏÌرðµÄÉ豸£¨Ö»Äܲéѯ£© HANDLE hUsb = CreateFile( strtDetailData.DevicePath, NULL, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); // ²éѯÉ豸±êʶ HIDD_ATTRIBUTES strtAttrib = { sizeof(HIDD_ATTRIBUTES) }; Result=HidD_GetAttributes(hUsb,&strtAttrib); //ËùÒÔÕâÀïÒª¹Ø±Õһϣ¬ºóÃæÕÒµ½ÎÒÃÇ×Ô¼ºµÄÉ豸ȷ¶¨¿ÉÒÔдÈëÔÙ´ò¿ªÒ»´Î CloseHandle( hUsb ); if(TRUE==Result) { if ((0x0483 == strtAttrib.VendorID) && (0x5751 == strtAttrib.ProductID)) //ÕÒµ½ÎÒÃÇ×Ô¼ºµÄÉ豸 { printf("VendorID : %hX\n", strtAttrib.VendorID); printf("ProductID: %hX\n", strtAttrib.ProductID); printf("VerNumber: %hX\n", strtAttrib.VersionNumber); //È·¶¨ÊÇÎÒÃÇ×Ô¼ºµÄÉ豸£¬ÔÙ´ò¿ªÒ»´Î,×¢ÒâÎÒÃÇÕâÀïʹÓõÄÊÇͬ²½·¢ËÍ hUsb = CreateFile(strtDetailData.DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); Switch(hUsb); GetData(hUsb); //Sleep(100); SendData(hUsb); GetData(hUsb); Switch(hUsb); GetData(hUsb); SendData(hUsb); GetData(hUsb); CloseHandle( hUsb ); }//if ((0x8888==strtAttrib.VendorID) && } //if(TRUE==Result) } // if( SetupDiGetDeviceInterfaceDetail(hDevInfo,&strtInterfaceData,&strtDetailData,_countof(buf),NULL,NULL) ) } //for( DWORD index=0; if( GetLastError() != ERROR_NO_MORE_ITEMS ) {printf("No more items!\n"); } SetupDiDestroyDeviceInfoList( hDevInfo ); } //if( INVALID_HANDLE_VALUE != hDevInfo ) system("PAUSE"); return 0; }
运行结果: