.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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM