【USB】C#使用HID通訊

China Soft發表於2024-09-02

https://blog.csdn.net/gzylongxingtianxia/article/details/137151349

最近做了一個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();
1
2
3
4
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();
}
1
2
3
4
5
6
7
8
9
10
監聽插拔


/// <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);
}
}

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
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);
1
2
3
4
5
6
7
8
9
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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
4. 讀寫
當裝置連線成功後,會建立一個位元組流

this.fileStream = new FileStream(new SafeFileHandle(this.handle, false), FileAccess.ReadWrite, this.hidCaps.InputReportByteLength, true);
1
透過這個位元組流讀寫資料

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();
}
}


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
Ojbkey了,剩下的開多少個執行緒,如何處理傳送接收就自己發揮吧,也比較簡單,下班了,週末愉快,老鐵們~~!!
————————————————

版權宣告:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連結和本宣告。

原文連結:https://blog.csdn.net/gzylongxingtianxia/article/details/137151349

相關文章