让多个线程轮询同一个串口的正确方法是什么?
What is the right way to have multiple threads polling the same serial port?
我对与硬件交互还很陌生。我目前正在使用 Windows 表单在 C# 中构建 GUI。我正在使用串行 port/usb 与硬件设备交互。我想要实现的是让多个线程在不同时间轮询设备。应该定期(至少每秒)检索一些数据,例如温度、电流、功率等,并在 GUI 上更新以供用户查看。而其他数据只有在用户按下表单上的按钮时才会被检索。多线程是解决这个问题的正确方法吗?如果没有,什么是更好的解决方案?示例将不胜感激。谢谢你们的时间!
更新: 我正在尝试使用你们许多人建议的 SerialPort.DataReceived 事件和一个终止符 '\r' 来解析来自串行端口。然后我调用一个名为 DisplayText 的方法来处理该字符串。我现在的问题是我不知道如何弄清楚字符串代表什么。是否代表温度、电流等
private char terminator = '\r';
private void SerialPorts_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
output += serialPort1.ReadExisting();
if (output.IndexOf((char)this.terminator) > -1)
{
string workingString = output.Substring(0, output.IndexOf(terminator));
output = output.Substring(output.IndexOf(terminator) + 1);
this.Invoke(new EventHandler((s, a)=>DisplayText(s, a, workingString)));
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void DisplayText(object sender, EventArgs e, string s)
{
Console.WriteLine(s); // for testing only
richTextBox1.AppendText(s); // for testing only
}
我认为让多个线程轮询单个端口并尝试同步不是一个好主意。
最好有一个线程进行轮询并将所有值存储在某个 "middle" 对象(单例、静态字段,您喜欢的)中,而不是同步对此存储的访问。
所以一个线程轮询端口,收集值并将它们存储在某处,而其他线程则从那里获取值。
您可以使用互斥锁来保护所有串行端口访问功能,并以在每次离散操作(例如读取温度)后关闭串行端口的方式设计您的应用程序。一种可能的方法是编写一个 API 类型的包装器,它将:
检查互斥锁是否解锁(如果锁定则return带有错误代码),
打开串口
读取需要的值
关闭串口并清除互斥量
Return读取值
但是,使用此解决方案您将面临各种死锁,您将不得不实施额外的应用程序级别检查以防止出现这种情况。
如上所述,更好的解决方案是让单个线程定期轮询串行端口并不断更新您选择的数据结构中的所有值。
对于只需要在用户输入后读取的值,您将必须实现一个 MessageQueue 来请求此数据。
更新以响应 OP 编辑
我会推荐以下两种方法之一来处理您设备中的通信:
- 实施 request/response 机制,您可以在其中发送请求
通过串行端口将特定数据值(例如当前值)发送到您的设备,
设备解析此请求并returns 相应的值
- 让您的设备 return 在一个 "frame" 中的所有值,例如
[SOF][D0...Dn][EOF],其中 "D0...Dn" 是您的所有数据
预定顺序; SOF 和 EOF 可以是你的任何值
选择(您也可以选择完全跳过这些并依靠
分隔帧的字节数)。
如果您没有选择更改设备中的代码,则必须有某种顺序这些值由设备报告,因此维护接收到的数据的序列号将是可行的方法。但是,如果不知道有关您的设备正在做什么的更多详细信息,就很难提出解决方案。
My problem now is I have no idea how to figure out what the string
represents. Does it represent a temperature, a current, etc.
使用简单协议发送温度、电流等
SOH + DATA + ETX + BCC
SOH = 0x02;
ETX = 0x03;
BCC = 0x00-0xFF,所有DATA + ETX的异或值。
数据 = 使用“;”作为温度(Temp)和电流(Curr)之间的分隔符
Example:
TempCmd = 0x01
温度值 = 32 摄氏度 = 0x20
当前指令 = 0x02
CurrValue = 1.4 安培 = 0x0E -> 结果必须除以接收器上的 10。
SOH + DATA + ETX + BCC
SOH + TempCmd:TempValue; CurrCmd:CurrValue + ETX + BCC
02 + 01:20;02:0E + 03 + BCC
BCC = 01 xor ':' xor 20 xor ';' xor 02 xor ':' xor 0E xor 03
从微控制器“0201:20;02:0E03;XX”发送数据(以字符串形式发送)
电脑接收数据:
- 检测封装中的SOH和ETX
- 验证 BCC 是真(完整的包裹)还是假的(破损的包裹)
- 正在用 ; 解析数据作为分隔符 // data1= 01:20 data2= 02:0E03
- 用 : 作为定界符解析 data1 和 data2 // data1= 20 data2= 0E03
- 将十六进制转换为十进制并将 data2 除以 10 以获得浮点格式的电流。
我对与硬件交互还很陌生。我目前正在使用 Windows 表单在 C# 中构建 GUI。我正在使用串行 port/usb 与硬件设备交互。我想要实现的是让多个线程在不同时间轮询设备。应该定期(至少每秒)检索一些数据,例如温度、电流、功率等,并在 GUI 上更新以供用户查看。而其他数据只有在用户按下表单上的按钮时才会被检索。多线程是解决这个问题的正确方法吗?如果没有,什么是更好的解决方案?示例将不胜感激。谢谢你们的时间!
更新: 我正在尝试使用你们许多人建议的 SerialPort.DataReceived 事件和一个终止符 '\r' 来解析来自串行端口。然后我调用一个名为 DisplayText 的方法来处理该字符串。我现在的问题是我不知道如何弄清楚字符串代表什么。是否代表温度、电流等
private char terminator = '\r';
private void SerialPorts_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
output += serialPort1.ReadExisting();
if (output.IndexOf((char)this.terminator) > -1)
{
string workingString = output.Substring(0, output.IndexOf(terminator));
output = output.Substring(output.IndexOf(terminator) + 1);
this.Invoke(new EventHandler((s, a)=>DisplayText(s, a, workingString)));
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void DisplayText(object sender, EventArgs e, string s)
{
Console.WriteLine(s); // for testing only
richTextBox1.AppendText(s); // for testing only
}
我认为让多个线程轮询单个端口并尝试同步不是一个好主意。
最好有一个线程进行轮询并将所有值存储在某个 "middle" 对象(单例、静态字段,您喜欢的)中,而不是同步对此存储的访问。
所以一个线程轮询端口,收集值并将它们存储在某处,而其他线程则从那里获取值。
您可以使用互斥锁来保护所有串行端口访问功能,并以在每次离散操作(例如读取温度)后关闭串行端口的方式设计您的应用程序。一种可能的方法是编写一个 API 类型的包装器,它将:
检查互斥锁是否解锁(如果锁定则return带有错误代码),
打开串口
读取需要的值
关闭串口并清除互斥量
Return读取值
但是,使用此解决方案您将面临各种死锁,您将不得不实施额外的应用程序级别检查以防止出现这种情况。
如上所述,更好的解决方案是让单个线程定期轮询串行端口并不断更新您选择的数据结构中的所有值。
对于只需要在用户输入后读取的值,您将必须实现一个 MessageQueue 来请求此数据。
更新以响应 OP 编辑
我会推荐以下两种方法之一来处理您设备中的通信:
- 实施 request/response 机制,您可以在其中发送请求 通过串行端口将特定数据值(例如当前值)发送到您的设备, 设备解析此请求并returns 相应的值
- 让您的设备 return 在一个 "frame" 中的所有值,例如 [SOF][D0...Dn][EOF],其中 "D0...Dn" 是您的所有数据 预定顺序; SOF 和 EOF 可以是你的任何值 选择(您也可以选择完全跳过这些并依靠 分隔帧的字节数)。
如果您没有选择更改设备中的代码,则必须有某种顺序这些值由设备报告,因此维护接收到的数据的序列号将是可行的方法。但是,如果不知道有关您的设备正在做什么的更多详细信息,就很难提出解决方案。
My problem now is I have no idea how to figure out what the string represents. Does it represent a temperature, a current, etc.
使用简单协议发送温度、电流等
SOH + DATA + ETX + BCC
SOH = 0x02;
ETX = 0x03;
BCC = 0x00-0xFF,所有DATA + ETX的异或值。
数据 = 使用“;”作为温度(Temp)和电流(Curr)之间的分隔符
Example:
TempCmd = 0x01
温度值 = 32 摄氏度 = 0x20
当前指令 = 0x02
CurrValue = 1.4 安培 = 0x0E -> 结果必须除以接收器上的 10。
SOH + DATA + ETX + BCC
SOH + TempCmd:TempValue; CurrCmd:CurrValue + ETX + BCC
02 + 01:20;02:0E + 03 + BCC
BCC = 01 xor ':' xor 20 xor ';' xor 02 xor ':' xor 0E xor 03
从微控制器“0201:20;02:0E03;XX”发送数据(以字符串形式发送)
电脑接收数据:
- 检测封装中的SOH和ETX
- 验证 BCC 是真(完整的包裹)还是假的(破损的包裹)
- 正在用 ; 解析数据作为分隔符 // data1= 01:20 data2= 02:0E03
- 用 : 作为定界符解析 data1 和 data2 // data1= 20 data2= 0E03
- 将十六进制转换为十进制并将 data2 除以 10 以获得浮点格式的电流。