最近在研究一款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):
#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控制的排插,满足实验室安规要求。有需要的朋友可以进一步研究。
参考: