【GD32L233C-START评测】6. 为HID设备做个简单的上位机
[复制链接]
【GD32L233C-START评测】1.开箱测试点亮彩屏
【GD32L233C-START评测】2.超级玛丽跑起来
【GD32L233C-START评测】3.USB键盘的实现
【GD32L233C-START评测】4.USB鼠标的实现
【GD32L233C-START评测】5.USB自定义HID设备点灯
上一篇中实现了自定义HID设备,并且使用《圈圈教你玩USB》中的上位机小工具。圈圈的工具是使用MFC写的,而且是在VC6.0下的。VC6.0已经太老了,现在写上位机C#显然要比MFC要方便的许多。所以就使用C#的Winform写个简单的HID上位机。
上位机的界面如下:
界面比较简单,使用combox显示系统里面所有的HID设备的设备路径。然后中间四个按钮代表开发板上的四个灯。最下面的文本框显示操作过程中的信息的输出。
使用C#写HID上位机其实和C++差不多,也是调用系统中HID.dll export出来的HidD_GetHidGuid之类函数, setupapi.dll export出来的SetupDiEnumDeviceInterfaces之类函数,已经Windows系统中操作文件的kernel32.dll中的createfile这类函数。
首先是枚举系统中所有的HID设备。
public static void GetHidDeviceList(ref List<string> deviceList)
{
Guid hUSB = Guid.Empty;
uint index = 0;
deviceList.Clear();
// 取得hid设备全局id
HidD_GetHidGuid(ref hUSB);
//取得一个包含所有HID接口信息集合的句柄
IntPtr hidInfoSet = SetupDiGetClassDevs(ref hUSB, 0, IntPtr.Zero, DIGCF.DIGCF_PRESENT | DIGCF.DIGCF_DEVICEINTERFACE);
if (hidInfoSet != IntPtr.Zero)
{
SP_DEVICE_INTERFACE_DATA interfaceInfo = new SP_DEVICE_INTERFACE_DATA();
interfaceInfo.cbSize = Marshal.SizeOf(interfaceInfo);
//查询集合中每一个接口
for (index = 0; index < MAX_USB_DEVICES; index++)
{
//得到第index个接口信息
if (SetupDiEnumDeviceInterfaces(hidInfoSet, IntPtr.Zero, ref hUSB, index, ref interfaceInfo))
{
int buffsize = 0;
// 取得接口详细信息:第一次读取错误,但可以取得信息缓冲区的大小
SetupDiGetDeviceInterfaceDetail(hidInfoSet, ref interfaceInfo, IntPtr.Zero, buffsize, ref buffsize, null);
//构建接收缓冲
IntPtr pDetail = Marshal.AllocHGlobal(buffsize);
SP_DEVICE_INTERFACE_DETAIL_DATA detail = new SP_DEVICE_INTERFACE_DETAIL_DATA();
detail.cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DETAIL_DATA));
Marshal.StructureToPtr(detail, pDetail, false);
if (SetupDiGetDeviceInterfaceDetail(hidInfoSet, ref interfaceInfo, pDetail, buffsize, ref buffsize, null))
{
deviceList.Add(Marshal.PtrToStringAuto((IntPtr)((int)pDetail + 4)));
}
Marshal.FreeHGlobal(pDetail);
}
}
}
SetupDiDestroyDeviceInfoList(hidInfoSet);
}
然后就是打开我们在界面上选中的设备:
public HID_RETURN OpenDevice(string deviceString)
{
if (deviceOpened == true)
return HID_RETURN.DEVICE_OPENED;
IntPtr device = CreateFile(deviceString,
DESIREDACCESS.GENERIC_READ | DESIREDACCESS.GENERIC_WRITE,
0,
0,
CREATIONDISPOSITION.OPEN_EXISTING,
FLAGSANDATTRIBUTES.FILE_FLAG_OVERLAPPED,
0);
if (device != INVALID_HANDLE_VALUE)
{
//HIDD_ATTRIBUTES attributes;
//HidD_GetAttributes(device, out attributes);
IntPtr preparseData;
HIDP_CAPS caps;
HidD_GetPreparsedData(device, out preparseData);
HidP_GetCaps(preparseData, out caps);
HidD_FreePreparsedData(preparseData);
outputReportLength = caps.OutputReportByteLength;
inputReportLength = caps.InputReportByteLength;
hidDevice = new FileStream(new SafeFileHandle(device, false), FileAccess.ReadWrite, inputReportLength, true);
deviceOpened = true;
BeginAsyncRead();
hHubDevice = device;
return HID_RETURN.SUCCESS;
}
else
{
return HID_RETURN.DEVICE_NOT_FIND;
}
}
然后就是向设备发送数据,对于上层来说也就是向文件中写入数据:
public HID_RETURN Write(report r)
{
if (deviceOpened)
{
try
{
byte[] buffer = new byte[outputReportLength];
buffer[0] = r.reportID;
int maxBufferLength = 0;
if (r.reportBuff.Length < outputReportLength - 1)
maxBufferLength = r.reportBuff.Length;
else
maxBufferLength = outputReportLength - 1;
for (int i = 0; i < maxBufferLength; i++)
buffer[i + 1] = r.reportBuff[i];
hidDevice.Write(buffer, 0, OutputReportLength);
return HID_RETURN.SUCCESS;
}
catch
{
EventArgs ex = new EventArgs();
OnDeviceRemoved(ex);//发出设备移除消息
CloseDevice();
return HID_RETURN.NO_DEVICE_CONECTED;
}
}
return HID_RETURN.WRITE_FAILD;
}
上述的三个函数基本上就完成了HID设备的遍历,然后对指定设备的打开,最后向设备写入数据。对应到界面上就是点击一个按钮,那么开发板上对应的LED灯就会被点亮。
感兴趣的小伙伴可以git clone代码自己研究,代码在https://github.com/bzhou830/GD32/tree/main/HIDApp中。
|