【问题标题】:COM port disappears when unplugging USB拔下 USB 时 COM 端口消失
【发布时间】:2013-05-23 14:28:24
【问题描述】:

我正在为平板电脑制作一种基于 Arduino 的扩展坞原型,使用 USB 端口作为连接器。这意味着我需要支持在平板电脑上的应用程序运行时插入/拔出 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 连接器之外,还有其他方法吗?

【问题讨论】:

  • 稍等一下,你是用串口当USB口,还是用USB口当串口? AFAIK,每次您将某些东西重新连接到 COM 端口时,您都需要重新启动它。
  • 串行端口,我一直将东西连接到串行端口而无需重新启动。真正的串口(rs-232)非常健壮。(我想)
  • 您是否尝试过处理掉那个 SerialPort* 实例并在失去连接时创建一个新实例?
  • 同样的问题。每个 USB 驱动程序都以自己的方式行为不端。您的具体问题是它没有正确更新注册表,即 SerialPort 在调用 GetPortNames() 时使用的键。信息应该很清楚,不要在端口使用时断开电缆,这是没有意义的。当 Windows 正在写入闪存驱动器时,您也不会将闪存驱动器从 USB 端口中拔出,以同样的方式对待 USB 仿真器。如果出于某种神秘原因,此功能对您来说必不可少,那么您需要购买另一个具有更好驱动程序的功能。
  • 我在链接的问题中给出了非常具体的建议。编写自己的驱动程序当然不是其中的一部分。

标签: c# .net serial-port arduino


【解决方案1】:

我找到了一个可以处理插入和拔出 SerialPort 的解决方案。

首先,它需要使用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();
                    }
                }
            }

【讨论】:

    【解决方案2】:

    所以我相信这是因为您的程序在第一次插入 USB 时缓存了它的地址。

    当有人插入设备时,集线器会检测到任一 D+ 上的电压 或 D- 并通过此中断向主机发出插入信号 端点。当主机轮询这个中断端点时,它得知 新设备存在。然后它指示集线器(通过默认 控制管道)以重置插入新设备的端口。 ***此重置使新设备采用地址 0, 并且主机可以 然后直接与之交互;这种互动将导致 主机为设备分配一个新的(非零)地址。

    最好的办法是研究如何以编程方式刷新 USB 设备的地址缓存。

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

    【讨论】:

    • 你所描述的发生在低得多的层。
    • @BenVoigt 我同意,但我仍然认为这是问题所在。他可能需要手动调用刷新。
    • 但只有在拔下设备时他的应用程序打开了串行端口时,他才会看到失败。
    猜你喜欢
    • 1970-01-01
    • 2013-02-16
    • 2016-10-28
    • 1970-01-01
    • 2016-05-13
    • 1970-01-01
    • 2013-04-16
    • 1970-01-01
    • 2011-05-13
    相关资源
    最近更新 更多