一、需求场景
最近有时间静下心来研究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属性并不可靠,尽量少用
https://www.cnblogs.com/xietianjiao/p/10820928.html