.Net串口通讯中的若干问题(C#多串口硬件识别、热插拔、Close方法报错问题、IsOpen的可靠性问题)

一、需求场景

       最近有时间静下心来研究SDK,串口通讯的。要求实现识别cp210x和cp2303驱动的两款硬件,并且2303的优先级高,即有2303识别之,没有再识别210x;要求实现热插拔,拔掉自动断开,插上自动连接。

二、问题一:如何实现串口硬件的识别呢?

1、如果方便的话,SerialPort的Handshake这个字段值得深入研究,可以利用这个实现;

2、添加自定义的握手协议,本人用一个5字节的串进行校验(第三位是硬件版本标识),校验算法如下:

                    for (int i = 0; i < btData.Length; i++)
                    {
                        if ((i % 5) == 0) commit = 0;
                        if (btData[i].ToString().Equals(byteMitt[i % 5]) || i % 5 == 3)
                            commit++;
                        if (commit == 5)
                        {
                            SendMessage(EnumDataConverter.GetCommandStatus(SendCommandType.Test) +
                                        intDeviceCount.ToString().PadLeft(2, '0'));
                            tpVersion = btData[3].ToString();
                            IsConnected = true;
                            strDeviceName = tempPort.Value;
                            return true;
                        }
                    }   //校验完毕没有成功,进入下次循环

二、热插拔的监听问题

经过实践,cp210x可以在串口设备表中查询到,cp2303不可不发现,故而采用查询计算机设备表的方式

备注:

System.IO.Ports.SerialPort.GetPortNames()只能获取设备名,不能获取详细信息(类型等),我下面用来进行一些比对的逻辑操作
  private void CreateUSBWatcher()
        {
            Ports = System.IO.Ports.SerialPort.GetPortNames();
            //建立监听
            ManagementScope scope = new ManagementScope("root\\CIMV2");
            scope.Options.EnablePrivileges = true;
            //建立插入监听
            try
            {
                WqlEventQuery USBInsertQuery = new WqlEventQuery("__InstanceCreationEvent", "TargetInstance ISA 'Win32_PnPEntity'");
                USBInsertQuery.WithinInterval = new TimeSpan(0, 0, 2);
                USBInsert = new ManagementEventWatcher(scope, USBInsertQuery);
                USBInsert.EventArrived += USBInsert_EventArrived;
                USBInsert.Start();
            }
            catch (Exception ex)
            {
                if (USBInsert != null)
                {
                    USBInsert.Stop();
                }
                throw ex;
            }
            //建立拔出监听
            try
            {
                WqlEventQuery USBRemoveQuery = new WqlEventQuery("__InstanceDeletionEvent", "TargetInstance ISA 'Win32_PnPEntity'");
                USBRemoveQuery.WithinInterval = new TimeSpan(0, 0, 2);
                USBRemove = new ManagementEventWatcher(scope, USBRemoveQuery);
                USBRemove.EventArrived += USBRemove_EventArrived;
                USBRemove.Start();
            }
            catch (Exception ex)
            {
                if (USBRemove != null)
                {
                    USBRemove.Stop();
                }
                throw ex;
            }
        }
        /// <summary>
        /// USB设备插入
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void USBInsert_EventArrived(object sender, EventArrivedEventArgs e)
        {
            string[] tempPorts = System.IO.Ports.SerialPort.GetPortNames();
            if (tempPorts.Count() == Ports.Count())
                return;
            else
                Ports = tempPorts;

            if (IsConnected)
                return;

            if (blnDesireConnected && Open())
                commExecuteInterface?.DeviceArrivaled();
        }

        /// <summary>
        /// USB设备拔出
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void USBRemove_EventArrived(object sender, EventArrivedEventArgs e)
        {
            string[] tempPorts = System.IO.Ports.SerialPort.GetPortNames();
            if (tempPorts.Count() == Ports.Count())
                return;
            else
                Ports = tempPorts;

            if (!IsConnected)
                return;

            IsConnected = false;
            spUSB.Close();
            commExecuteInterface?.DeviceRemoved();          
        }

三、调用Close方法会提示“ unsafe handler 已关闭”

微软的方法有问题的,微软的SerialPort的这个类有问题,一但断开硬件的连接,会触发部分对象资源的释放(SerialPort不是单纯的托管资源,非托管资源被释放了),因此调用Close会出问题,除非此进程完全退出,所以SerialPort的资源需要重置;SerialPort的初始化方法中有一个是有IContainer参数的,IContainer提供了非托管资源的管理方法,因此,把SerialPort对象放到一个容器中可解决Close问题;即,想实现热插拔,必须把SerialPort放到容器中!

                    spUSB = new SerialPort(container);
                    KeyValuePair<string, string> tempPort = queueSerialPorts.Dequeue();
                    spUSB.BaudRate = 115200;
                    spUSB.PortName = tempPort.Key;

                    spUSB.Open();
                    SendMessage(EnumDataConverter.GetCommandStatus(SendCommandType.ECC));  //尝试让主控机复位

                    byte[] btData = ReadData();

四、IsOpen属性并不可靠,尽量少用

有问题可以加qq群:568055323

posted @ 2019-05-06 17:30  卖雨伞的小男孩  阅读(3497)  评论(0编辑  收藏  举报