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

