描述
有时工作测试中遇到要一拖多去进行串口通讯,例如多串口同时进行烧录,多串口对设备进行控制,指令发送等,平时我用的串口工具都是人为的一对一手动操作,用电脑同时一对多操作串口通讯我还真没用过,但是想到平时工作使用的场景蛮多的,感觉还是很有必要研究下,对工作生产还是很有效率的
首先做之前我们必须先了解下串口,主要了解串口在电脑重启后,COM口会不会变,插多个串口后COM口会不会变。了解这些是因为我们要依靠COM去绑定依赖关系,COM数据获取简单方便。经检验,串口插上后,更换USB插槽, COM口会跟着变;其他串口插上后不会改变之前插上去的串口的COM口,并重启后COM不会改变。所以我们绑定串口后就不要再插拔串口了,有可能会引起COM改变
效果图
目标
我们要做的主要实现一下功能
1. 监听USB设备插拔,有USB设备插拔时,更新COM口列表
2. 绑定的串口可以保存为配置文件,下次启动根据配置文件直接加载配置并绑定
3. 可以对目标设备绑定多个串口,可以删除绑定的COM口,绑定后COM列表将已绑定的信息移除
4. 可以观察到哪些绑定的串口是可用的,哪些是断开的
代码逻辑
为了方便知道哪根线接的哪个串口,合理的连接到对应的设备上,我增加了USB设备监听功能,当串口插上时,未绑定列表就会新增一条COM信息,我就知道该串口的COM名称了,然后再做绑定,这样也不容易出错
// 在生命周期loaded的时候初始化监听器
// 这里的代码适用与WPF,如果你是Winform,请查看我写的USB监听的博文
private void OnLayoutLoaded(object sender, RoutedEventArgs e)
{
HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;
if (null != hwndSource)
hwndSource.AddHook(new HwndSourceHook(DevieceChanged));
}
private IntPtr DevieceChanged(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_DEVICECHANGE)
{
switch (wParam.ToInt32())
{
case DBT_DEVICEARRIVAL:
Console.WriteLine("串口连接成功!");
break;
case DBT_DEVICEREMOVECOMPLETE:
Console.WriteLine("串口断开!");
break;
}
if(wParam.ToInt32() == DBT_DEVICEARRIVAL || wParam.ToInt32() == DBT_DEVICEREMOVECOMPLETE)
{
// 这里用线程主要事因为,有时一个串口设备有2个COM口,在线程里面等待两个口都连接
// 上后,再刷新界面,只用刷新一次
Thread thread = new Thread(new ThreadStart(OnDiveceChanged));
thread.Start();
}
}
return IntPtr.Zero;
}
private bool isUpdataComsList = false;
private object lockObj = new object();
private void OnDiveceChanged()
{
lock (lockObj)
{
if (isUpdataComsList) return;
isUpdataComsList = true;
}
Thread.Sleep(1000);
m_SyncContext.Post(UpdataComsList, null);
}
设备绑定:如果单设备的话,我们只需要将COM口和设备对应即可,用键值对的方式存储,例如----->DUT1:COM3,DUT2:COM8。等你需要连接串口时, 你根据自己的编号取对应的COM口即可,比如自己的编号为“DUT2”,那我们就连接COM8的串口即可
对于一个设备时需要连接多个串口, 而且需要控制多个设备时,我引入了Tag的概念,比如说功能1的Tag为CMS1,功能2的Tag为CMS2,在保存数据的形式还是键值对的方式,不过值变成了一个对象,对象里保存了COM口信息和TAG信息。例如DUT1:[{Serial:COM3,Tag:CMS1},{Serial:COM5,Tag:CMS2}],当某一个设备在使用某一项功能时,只要知道保存的TAG,就可以获取到配置的串口
我保存的串口是以XML形式保存的,可以根据自己喜欢的方式进行选择
解析保存的配置文件略
public static void SaveSerialsBindInfo(Dictionary<string,List<SerialsBindInfo>> infos)
{
CheckSaveConfigFilesPath();
XmlDocument xmlDoc = new XmlDocument();
XmlElement root = xmlDoc.CreateElement("SerialsConfig");
xmlDoc.AppendChild(root);
XmlElement DutBindInfoElement;
foreach (KeyValuePair<string, List<SerialsBindInfo>> kv in infos){
if (kv.Value.Count == 0) continue;
DutBindInfoElement = xmlDoc.CreateElement("DutBindInfo");
DutBindInfoElement.SetAttribute("DutNum" , kv.Key);
XmlElement SerialNameElement;
XmlElement RemarkElement;
foreach (SerialsBindInfo serialInfo in kv.Value)
{
if (serialInfo.SerialName.Trim().Length == 0 && serialInfo.Remark.Trim().Length == 0) continue;
XmlElement BindInfoItemElement = xmlDoc.CreateElement("InfoItem");
SerialNameElement = xmlDoc.CreateElement("SerialName");
SerialNameElement.InnerText = serialInfo.SerialName;
RemarkElement = xmlDoc.CreateElement("Remark");
RemarkElement.InnerText = serialInfo.Remark;
BindInfoItemElement.AppendChild(SerialNameElement);
BindInfoItemElement.AppendChild(RemarkElement);
DutBindInfoElement.AppendChild(BindInfoItemElement);
}
root.AppendChild(DutBindInfoElement);
}
xmlDoc.Save(FilePathConfig.SerialsBindConfigPath);
}
COM口绑定后,需要将已绑定的串口从待绑定的列表中删除,不然串口多了比较乱,分不清哪一个是哪一个,列表只显示待绑定的
如果解除绑定后,也需要将解除的COM添加到待绑定列表里面
如果从COM1切换到COM2,这里面既有添加,也有删除,为了方便和满足上面的3种需求,我就先将所有COM获取,再将已经绑定的数据从所有COM中移除,就只剩未绑定的了
private void SerialsSelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox comboBox = (ComboBox)sender;
Label SelectItem = (Label)comboBox.SelectedItem;
if (SelectItem == null) return;
string content = (string)SelectItem.Content;
if (ContentNone.Equals(content))
comboBox.Items.Clear();
else
unBindSerials.Remove(content);
DockPanel itemLayout = (DockPanel)comboBox.Parent;
// 先刷新刚刚选中的COM口的UI
UpdataBindData(itemLayout);
// 再过滤未绑定的数据
FilterUnbindComs();
// 刷新未绑定列表的UI
UpdateComsList();
}
private void FilterUnbindComs()
{
// 清除历史数据
unBindSerials.Clear();
// 遍历所有的COM,包含绑定和未绑定的所有COM口
foreach (string ComName in allComsList)
{
bool isContains = false;
// 遍历查找是否该COM口已经绑定
foreach (List<SerialsBindInfo> ComsInfos in bindData.Values)
{
foreach (SerialsBindInfo info in ComsInfos)
{
if (ComName.Equals(info.SerialName))
{
isContains = true;
break;
}
}
}
// 如果没绑定,则添加进未绑定列表
if(!isContains) unBindSerials.Add(ComName);
}
}
现在绑定和保存都可以了,但是有时候设备会出异常,我们首先要看看串口设备是不是正常的,串口都没接好,肯定没办法正常工作
检测的办法就是获取当前所有串口(当前所有可用串口),对比绑定数据,看看绑定的串口是否在当前可用串口列表中,如果没有在串口列表,则用红色的图标标记,便于工程人员检修时方便观察检修
//默认Image是显示的,当判断到符合条件时,将其隐藏
if (serialsBindInfo == null || serialsBindInfo.SerialName == null || serialsBindInfo.SerialName.Trim().Length == 0 || allComsList.Contains(serialsBindInfo.SerialName))
EnableImage.Visibility = Visibility.Hidden;
小结
串口绑定其实很简单的,只要是思想:如何一对多。上面逻辑可以帮助大家将多路串口连接,并可以多机控制
如果你喜欢我的文章请长按点赞哦