一、需求場景
最近有時間靜下心來研究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