拔下USB时COM端口消失

时间:2013-05-23 14:28:25

标签: c# .net serial-port arduino

我正在使用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连接器?

2 个答案:

答案 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设备的地址缓存。

参考:http://en.wikipedia.org/wiki/USB_hub