目录
下位机这两天已经折腾出一个一二了,在配合上位机完整学习一下,下位机芯片是STM32F103VB
1、基本测试
1.1、通过Bus Hound可以看到设备列表
1.2、 通过API获取到设备接口文件列表
可以看出,每个接口有一个对应的操作对象,三个设备是可以独立操作的。
- \\?\hid#vid_0480&pid_5750&mi_00#8&eff1a39&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
- \\?\hid#vid_0480&pid_5750&mi_01#8&32d657fb&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\kbd
- \\?\hid#vid_0480&pid_5750&mi_02#8&1b12cbb6&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
用 mi_(接口编号) 区别,接口编号对应配置描述表里的接口编号,鼠标0,键盘1,自定义HID 2
此处的复合设备是单独的接口操作的,就会产生对应的操作文件。如果是通过同一个接口,用报告描述符ID来产生的对个设备,将不会出现对个设备文件,操作时需要通过报告描述符ID来寻找设备。
1.3、用BUS Hound收发测试
bus hound 不会限制发送长度,下位机端也可以不用报告描述符来描述具体发送数据的意义及格式
2、C#上位机
C# HID上位机https://download.csdn.net/download/ai5945fei/43657670
参考:
2.1、打开设备
不建议去打开一些未知的设备,确定是谁再去打开,可以通过VID和PID,接口编号三个参数确定我们的目标设备。
public HID_RETURN OpenDevice(string deviceNme)
{
if (deviceOpened == false)
{
IntPtr device = CreateFile(deviceNme,
DESIREDACCESS.GENERIC_READ | DESIREDACCESS.GENERIC_WRITE,
0,
0,
CREATIONDISPOSITION.OPEN_EXISTING,
FLAGSANDATTRIBUTES.FILE_FLAG_OVERLAPPED,
0);
if (device != INVALID_HANDLE_VALUE)
{
HIDD_ATTRIBUTES attributes;
IntPtr serialBuff = Marshal.AllocHGlobal(512);
HidD_GetAttributes(device, out attributes);
HidD_GetSerialNumberString(device, serialBuff, 512);
string deviceStr = Marshal.PtrToStringAuto(serialBuff);
Marshal.FreeHGlobal(serialBuff);
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;
}
return HID_RETURN.DEVICE_NOT_FIND;
}
else
return HID_RETURN.DEVICE_OPENED;
}
报告ReportID
测试发现:
- 如果报告描述符有问题,则获取出来的caps对应值就为0,我开始没设置端点OUT的信息,caps.OutputReportByteLength就为0。
- 实际的值会默认加上报告描述符描述表ReportID,首字节位置。默认为0,多报告表就对应变化。及caps.OutputReportByteLength = 1字节ReportID + 实际报告表描述的数据长度。
HIDP_CAPS caps;
HidP_GetCaps(preparseData, out caps);
outputReportLength = caps.OutputReportByteLength;
inputReportLength = caps.InputReportByteLength;
2.2、发送数据
public HID_RETURN Write(report r)
{
if (deviceOpened)
{
try
{
byte[] buffer = new byte[outputReportLength];
int txlen = r.reportBuff.Length;
if (txlen > outputReportLength) txlen = outputReportLength;
buffer[0] = r.reportID;
Array.Copy(r.reportBuff, 0, buffer, 1, txlen);
hidDevice.Write(buffer, 0, buffer.Length);
hidDevice.Flush();//立即发送数据
return HID_RETURN.SUCCESS;
}
catch
{
EventArgs ex = new EventArgs();
OnDeviceRemoved(ex);//发出设备移除消息
CloseDevice();
return HID_RETURN.NO_DEVICE_CONECTED;
}
}
return HID_RETURN.WRITE_FAILD;
}
注意:
- 写入数据之后调用 hidDevice.Flush,数据才会立即发送,否则就会乱发送。
- 发送数据的buffer的长度必须和outputReportLength一致,否则会报错,不知缘由。
- buffer的首字节为ReportID,一个报告下默认为0。(如果有多个时,这个参数就有用了)
测试:
同时勾选两个上这两个
会发现106.3 发送8个字节,109是9个字节,加上了ReportID,下位机收到的也是8个字节。
2.3、异步接收
private void ReadCompleted(IAsyncResult iResult)
{
byte[] readBuff = (byte[])(iResult.AsyncState);
try
{
hidDevice.EndRead(iResult);//读取结束,如果读取错误就会产生一个异常
byte[] reportData = new byte[readBuff.Length - 1];
for (int i = 1; i < readBuff.Length; i++)
reportData[i - 1] = readBuff[i];
report e = new report(readBuff[0], reportData);
OnDataReceived(e); //发出数据到达消息
if (!deviceOpened) return;
BeginAsyncRead();//启动下一次读操作
}
catch //读写错误,设备已经被移除
{
//MyConsole.WriteLine("设备无法连接,请重新插入设备");
EventArgs ex = new EventArgs();
OnDeviceRemoved(ex);//发出设备移除消息
CloseDevice();
}
}
- 接收到的数据头字节也是ReportID,数据为其后的内容。
- 如果上传的数据长度和报告描述表里规定的长度不一致,API也收不到数据,但是BusHound能监控到数据。(肯定是哪里有问题,后续再查)
3、其他测试
3.1、中断端点时间测试
连续发送两个包,查看发送时间间隔
private void button2_Click(object sender, EventArgs e)
{
SendBytes(new byte[] { 0xA5, 0, 1, 0 });
SendBytes(new byte[] { 0xA5, 0, 2, 0 });
}
OUT端点设置
结果:
点击了两次,连续发送的两个包之间间隔是31ms,设置的端点中断时间为32ms
对比:改成10ms之后,对应间隔变成8ms左右
修改之后,需要删除原来的设备重新安装设备这个值才会刷新,或者修改VID,PID也可以。否则PC会记录并使用以前的值。
所以要是需要下发的数据块速接收到,将 bInterval: Polling Interval 改小即可。