最近在研究一款USB控制的排插:BellWin 出品的带有USB接口的排插(USB Power Splitter)型号是:UP520US。它带有五个插孔,一个用于控制插孔供电的USB接口和一个供电开关。这款排插不是给家用设计的,上面的的插孔和插头都是专门为机房这种特别场合设计的,在家用环境中必须使用转接头才能工作。
电器特性如下【参考1】:
输入:220V,20A
输入频率:50/60Hz
功耗:5W
每一路最高可支持15A,五组最大支持输出20A
工作温度:0-25℃
接口:IEC60320 C13
认证:RoHS,IPC,CUS
首先,测试一下它自带的控制程序。
用户可以通过下面的按钮控制每一路的开关。
接下来,使用 USB Package Viewer 设备抓取USB 数据包。USB Package Viewer(简称UPV)是一款国产的USB数据逻辑分析仪,能够抓取从 Low Speed 到 High Speed的数据包。有兴趣的朋友可以在【参考2】看到之前我的开箱视频。这个仪器有一个比较大的优点是能够实时显示,这个在分析研究数据量不大的USB设备时非常有用。比如,上位机进行一个动作立刻能够在分析软件上看到数据,这样便于对应数据和操作。
从设备管理器上也可以看出,这个设备使用USB HID 协议。
按下试验软件上的开关两次之后,在UPV中可以看到抓到了2个数据包:
经过多次试验,总结数据如下:
操作 | 抓到的数据,总长度64Bytes |
Port1 设置为 OFF | 0B 00 00 00 00 00 00 5A……5A |
Port2 设置为OFF | 0B 00 00 00 00 01 00 5A……5A |
Port3 设置为OFF | 0B 00 00 00 00 02 00 5A……5A |
Port4 设置为OFF | 0B 00 00 00 00 03 00 5A……5A |
Port5 设置为OFF | 0B 00 00 00 00 04 00 5A……5A |
Port1 设置为 ON | 0B 00 00 00 00 00 01 5A……5A |
Port2 设置为ON | 0B 00 00 00 00 01 01 5A……5A |
Port3 设置为ON | 0B 00 00 00 00 02 01 5A……5A |
Port4 设置为ON | 0B 00 00 00 00 03 01 5A……5A |
Port5 设置为ON | 0B 00 00 00 00 04 01 5A……5A |
需要注意的是,上面的数据末端,使用0x5A 填充,实际上使用任意数据进行填充都可以, USB 排插以前面的有效数据为准。
根据上述表格,很容易编写一个控制这个排插开关的代码(VS2019):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | #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 SetAll( HANDLE hUsb, bool AllOn) { BOOL Result; UCHAR WriteReportBuffer[65]; for ( int i = 0; i < 8; i++) { WriteReportBuffer[i] = 0x00; } for ( int i = 8; i < 65; i++) { WriteReportBuffer[i] = 0x5a; } WriteReportBuffer[1] = 0x0b; for (byte i = 0; i < 5; i++) { WriteReportBuffer[6] = i; if (AllOn) { WriteReportBuffer[7] = 0x01; } DWORD lpNumberOfBytesWritten; Result = WriteFile(hUsb, WriteReportBuffer, 65, &lpNumberOfBytesWritten, NULL); //printf("Written %d bytes Result [%d]\n", lpNumberOfBytesWritten, Result); } } int main() { GUID HidGuid; BOOL Result; int counter = -1; HidD_GetHidGuid(&HidGuid); 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]); 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); 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 ((0x04D8 == strtAttrib.VendorID) && (0xFEDC == 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); SetAll(hUsb,TRUE); Sleep(3000); SetAll(hUsb, FALSE); 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; } |
这个代码没有使用厂家提供的API (厂家提供了 DLL)而是直接对 HID设备编程,这样做的好处是无需外挂文件,一个 EXE 都可以搞定。坏处是,没有实现厂家API 提供的诸如取得序列号这样的功能,如果有需求还需要另外研究编写。但是无论如何,能够实现控制端口开关已经足够了。
综上所述:BellWin的UP520US是一款可以通过USB控制的排插,满足实验室安规要求。有需要的朋友可以进一步研究。
参考: