最近有一个需求,需要检测一个物体的运动速度,经过研究我决定使用激光距离传感器来完成这个要求。在模块选择的问题上,我再次遇到了“面粉比面包贵” 的问题—–激光测距模块价格比成品要高。最终选择的是带有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;
}
运行结果:

