最近做了一个USB通信SDK, 通过HID跟单片机通信,之前研究了一下Libusb, Cyusb, 要么死的太早,要么封装的不好,最后绕来绕去发现还是HID好用,反编译了一个SimpleHid, 别说,用起来还是很酸爽的~~~
1.设备识别
首先你要指定VID和PID, 这2个不知道的可以不用往下看了,就是一个人的名字和性别
设别识别很容易,直接获取设备列表,对比一下vid,pid就搞定了,不多墨迹了
直接Linq一下了
// 获取所有目标设备信息集合List<HIDInfoSet> acceptableDevices = HIDManager.GetInfoSets().Where(set => this.m_OpenOptions.IsVidAndPidAcceptable(set.Vid, set.Pid)).ToList();
2.插拔识别
这里有3中方法
方法1.使用winform pnpinvoke 监听消息句柄,判断插拔, 对上位机来说这种方式很常见了,但是有限制,下面说。
方法2.使用cpp dll, 方法1的限制在于有些native软件不能使用winform,比如Unity I2cpp 打包就不行,所以需要自己用C++封装dll,然后使用委托回调函数触发,这个也不难,可以看看我之前的文章
https://blog.csdn.net/gzylongxingtianxia/article/details/136683845
方法3. 简单粗暴,直接开一个监控线程
private void ThreadProcDeviceDiscovery(){while (!this.bKillUsbDiscoverThread){this.DoDeviceDiscovery();Thread.Sleep(200);}this.KillDiscoveryThread();}
监听插拔
/// <summary>/// The do device discovery./// </summary>private void DoDeviceDiscovery(){if (this.m_HidDevices == null){return;}// 获取当前已连接的设备的序列号列表List<string> connectedSerialNumbers = new List<string>();lock (this.m_HidDevices){foreach (UsbHidDevice device in this.m_HidDevices){connectedSerialNumbers.Add(device.hidInfoSet.SerialNumberString);}}// 获取所有目标设备信息集合List<HIDInfoSet> acceptableDevices = HIDManager.GetInfoSets().Where(set => this.m_OpenOptions.IsVidAndPidAcceptable(set.Vid, set.Pid)).ToList();// 获取新发现的设备信息集合List<HIDInfoSet> newDevices = acceptableDevices.Where(set => !connectedSerialNumbers.Contains(set.SerialNumberString)).ToList();// 获取已断开的设备列表List<UsbHidDevice> disconnectedDevices = this.m_HidDevices?.Where(device => !acceptableDevices.Any(set => set.SerialNumberString == device.hidInfoSet.SerialNumberString)).ToList();if (disconnectedDevices == null){return;}// 断开已断开的设备并移除foreach (UsbHidDevice device in disconnectedDevices){lock (this.m_HidDevices){this.m_HidDevices.Remove(device);EventDispatcher.TriggerEvent(EventEnum.DEVICE_DETACHED, device);}}// 实例化新发现的设备并添加到已连接列表中foreach (HIDInfoSet set in newDevices){UsbHidDevice device = new UsbHidDevice(this, set);lock (this.m_HidDevices){this.curHidDevice = device;this.m_HidDevices.Add(device);}EventDispatcher.TriggerEvent(EventEnum.DEVICE_ATTACHED, this.curHidDevice);}}
3.HID通信
1.创建通信句柄要开启读写
static class DESIREDACCESS
{public const uint GENERIC_READ = 0x80000000;public const uint GENERIC_WRITE = 0x40000000;public const uint GENERIC_EXECUTE = 0x20000000;public const uint GENERIC_ALL = 0x10000000;
}this.handle = NativeMethods.CreateFile(devicePath, DESIREDACCESS.GENERIC_READ | DESIREDACCESS.GENERIC_WRITE, 3, IntPtr.Zero, 3, 0x40000000, IntPtr.Zero);
NativeMethods.CreateFile第二个参数要开启读写
2.HidCaps
[StructLayout(LayoutKind.Sequential, Pack=1)]public struct HidCaps{public short Usage;public short UsagePage;public short InputReportByteLength;public short OutputReportByteLength;public short FeatureReportByteLength;[MarshalAs(UnmanagedType.ByValArray, SizeConst=0x11)]public short[] Reserved;public short NumberLinkCollectionNodes;public short NumberInputButtonCaps;public short NumberInputValueCaps;public short NumberInputDataIndices;public short NumberOutputButtonCaps;public short NumberOutputValueCaps;public short NumberOutputDataIndices;public short NumberFeatureButtonCaps;public short NumberFeatureValueCaps;public short NumberFeatureDataIndices;}
InputReportByteLength 和 OutputReportByteLength 这2个长度很重要,一般是65个字节,ReportId + Data
3. 构建报文数据
Report Id 单个包来说都是 0 ,如果一次发多个包,就要递增,我这里是一个包的情况
总长度 0x41 = 65 , ReportId + Data ,数据占64个字节
长度一定不能错,否则会包HID参数异常
下面是一个参考方法
public void MakeBytes(EMessage cmd, byte value){this.reportId = 0;this.value = (int)value;this.length = 1;this.cmd = (int)cmd;var i = 0;this.sendData = new byte[0x41];this.sendData[i] = this.reportId;this.sendData[i++] = 0x05;this.sendData[i++] = 0XAA;this.sendData[i++] = 0XAA;this.sendData[i++] = (byte)cmd;this.sendData[i++] = 1;this.sendData[i++] = value;for (; i < 0x41; i++){this.sendData[i] = 0;}}
4. 读写
当设备连接成功后,会建立一个字节流
this.fileStream = new FileStream(new SafeFileHandle(this.handle, false), FileAccess.ReadWrite, this.hidCaps.InputReportByteLength, true);
通过这个字节流读写数据
public byte[] ReadRawInputReport(){byte reportID = 0;byte[] buffer = this.CreateRawInputReport(reportID);try{this.fileStream.Read(buffer, 0, buffer.Length);}catch (Exception e){return buffer;}return buffer;}public void WriteRawOutputReport(byte[] data){try{this.fileStream.Write(data, 0, data.Length);this.fileStream.Flush();}catch (Exception e){throw new HIDDeviceException();}}
Ojbkey了,剩下的开多少个线程,如何处理发送接收就自己发挥吧,也比较简单,下班了,周末愉快,老铁们~~!!