我正在使用USB端口作为连接器为平板电脑设计一种基于Arduino的扩展坞。这意味着我需要支持在平板电脑上的应用程序运行时插拔USB连接器的能力。
平板电脑运行一个c#应用程序(Win7 64位上的.net 4.5),我将连接到Arduino Uno。启动应用程序时,我使用以下命令循环所有可用的COM端口:
var ports = SerialPort.GetPortNames(); // -> [COM3,COM4,COM8]
foreach (var port in ports)
{
var serial = new SerialPort(portname, baudRate);
//attempt handshake and connect to right port
}
此工作正常,但如果我拔下并重新插入USB电缆并重新尝试重新连接到Arduino(当应用程序仍在运行时),则Arduino端口(COM8)不再列在:
SerialPort.GetPortNames(); // -> [COM3,COM4] and no COM8
即使重新启动应用程序(使用Arduino重新插入)也只会导致[COM3,COM4]被列出。
恢复工作的唯一方法是在应用程序未运行时拔下并重新插入Arduino。
令我困惑的是,当我在启动应用程序后插入Arduino Uno时,SerialClass会识别新添加的端口并允许我连接。
仅在应用程序运行时拔出并重新插入设备时才会出现此问题。似乎尽管能够重置COM端口(在代码中或在设备管理器中手动),但SerialClass(以及本机Win32_SerialPort - 我也检查了这一点)无法识别这一点,除非我重新启动应用程序
这可能是什么原因?如何确保我的应用程序可以重新连接到该端口?有没有其他方法可以使用SerialPort来处理USB连接器?
答案 0 :(得分:2)
我找到了一个可以处理插拔串口的解决方案。
首先,它需要使用SafeSerialPort,这允许您正确配置串口。
SafeSerialPort serialPort;
private void Connect()
{
string portname = "COM8";
serialPort = new SafeSerialPort(portname, 9600);
serialPort.DataReceived += port_DataReceived;
serialPort.Open();
}
其次,您需要使用LibUsbDotNet来检测USB设备是连接还是断开连接。这将允许您确定是连接到设备还是重置COM端口。
public UsbDevice MyUsbDevice;
//Find your vendor id etc by listing all available USB devices
public UsbDeviceFinder MyUsbFinder = new UsbDeviceFinder(0x2341, 0x0001);
public IDeviceNotifier UsbDeviceNotifier = DeviceNotifier.OpenDeviceNotifier();
private void OnDeviceNotifyEvent(object sender, DeviceNotifyEventArgs e)
{
if (e.Object.ToString().Split('\n')[1].Contains("0x2341"))
{
if (e.EventType == EventType.DeviceArrival)
{
Connect();
}
else if(e.EventType == EventType.DeviceRemoveComplete)
{
ResetConnection();
}
}
}
最后,处理SerialPort将确保Windows在 HKEY_LOCAL_MACHINE \ HARDWARE \ DEVICEMAP \ SERIALCOMM 中注册,这意味着SerialPort.GetPortNames()
可以重新检测端口。
private void ResetConnection()
{
try
{
//Send any data to cause an IOException
serialPort.Write("Any value");
}
catch (IOException ex)
{
//Dispose the SafeSerialPort
serialPort.Dispose();
serialPort.Close();
}
}
完成此过程后,您可以在连接USB设备时重新连接到COM端口,而无需重新启动应用程序。
完整代码:
using LibUsbDotNet;
using LibUsbDotNet.DeviceNotify;
using LibUsbDotNet.Info;
using LibUsbDotNet.Main;
SafeSerialPort serialPort;
public SerialPortTest()
{
Connect();
UsbDeviceNotifier.OnDeviceNotify += OnDeviceNotifyEvent;
}
private void Connect()
{
string portname = "COM8";
serialPort = new SafeSerialPort(portname, 9600);
serialPort.DataReceived += port_DataReceived;
serialPort.Open();
}
private void ResetConnection()
{
try
{
serialPort.Write("Any value");
}
catch (IOException ex)
{
serialPort.Dispose();
serialPort.Close();
}
}
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Console.WriteLine(serialPort.ReadExisting());
}
public UsbDevice MyUsbDevice;
//Vendor ID etc can be found through enumerating the USB devices
public UsbDeviceFinder MyUsbFinder = new UsbDeviceFinder(0x2341, 0x0001);
public IDeviceNotifier UsbDeviceNotifier = DeviceNotifier.OpenDeviceNotifier();
private void OnDeviceNotifyEvent(object sender, DeviceNotifyEventArgs e)
{
//if this is your usb device, in my case an Arduino
if (e.Object.ToString().Split('\n')[1].Contains("0x2341"))
{
if (e.EventType == EventType.DeviceArrival)
{
Connect();
}
else
{
ResetConnection();
}
}
}
答案 1 :(得分:1)
所以我相信这种情况正在发生,因为你的程序在第一次插入时会缓存USB的地址。
当有人插入设备时,集线器会检测到D +上的电压 或者D-并通过该中断向主机发出信号 端点。当主机轮询此中断端点时,它会学习到 新设备存在。然后它指示集线器(通过默认值 控制管道)重置插入新设备的端口。 ***此重置使新设备采用地址0,,主机可以 然后直接与它互动;这种互动将导致 主机为设备分配新的(非零)地址。
最好的办法是研究如何以编程方式刷新USB设备的地址缓存。