工业通信中的RS485与虚拟USB串口

在工业领域,RS485和虚拟USB串口仍然扮演着重要的角色。无论是与数控机床或其他工业机械交换数据,还是通过Modbus与暖通空调设备或器具进行接口,串行通信虽然隐蔽但无处不在。

用户界面设计的重要性

在设计SCADA系统或图形面板的用户界面时,确保为用户提供正确的视觉更新至关重要,即使在长时间的数据交换会话中,或者在执行耗时操作时也是如此。标签、图片框、仪表和其他控件需要在后台不断更新。

视觉数据表示的需求

同事们经常要求的一个功能是,通过串行线的视觉表示RX/TX数据,因为大多数便宜的串行适配器和接口没有数据LED灯。

如何使用后台工作者更新UI

本文将展示如何使用后台工作者更新两个图片框的UI,模仿红色和绿色LED灯。当然,这个概念可以扩展到所有需要异步更新的视觉控件。

将从设置表单开始。对于这个示例,只需要一个包含两个图片框的表单,一个用于TX LED,另一个用于RX LED。将它们的可见性设置为False,并调整它们的大小,以适当地模仿表单左上角的两个LED。将它们命名为pbSend(TX)和pbReceive(RX)。

UI更新必须在主线程中进行,因为后台工作者在主线程之外的线程上运行,将设置一个委托来为执行此操作。

还将声明后台工作者本身,一个用于TX LED,另一个用于RX LED。

Private WithEvents sp As New SerialPort Private Delegate Sub LEDSwitchDelegate(ByRef a As PictureBox, ByVal b As Boolean) Private WithEvents backgroundWorker1 As New BackgroundWorker() Private WithEvents backgroundWorker2 As New BackgroundWorker()

实际提供切换两个LED开和关的子程序如下。将通过传递图片框名称和其可见性作为参数来调用它。例如:

LEDSwitch(pbSend, True)

这将导致pbSend LED被切换为ON。这将在主线程和任何其他线程上工作,多亏了委托!

Private Sub LEDSwitch(ByRef a As PictureBox, ByVal b As Boolean) If Me.InvokeRequired Then Dim d As New LEDSwitchDelegate(AddressOf Me.LEDSwitch) Me.BeginInvoke(d, New Object() {a, b}) Else Try a.Visible = b Catch ex As Exception End Try End If End Sub

在项目中,LED灯将在发送或接收一个字节或多个字节时切换为ON,持续500毫秒,因此后台工作者将运行确切的时间。

Private Sub backgroundWorker1_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles backgroundWorker1.DoWork Thread.Sleep(500) End Sub Private Sub backgroundWorker2_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles backgroundWorker2.DoWork Thread.Sleep(500) End Sub

他们不会做太多事情,除了等待500毫秒(这里里程可能会有所不同,可能会更好地利用后台工作者浪费的周期)。

工作完成后(只是一个500毫秒的非阻塞小睡),LED灯将再次关闭。将使用与两个后台工作者中的每一个相关联的RunWorkerCompleted事件。

Private Sub backgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles backgroundWorker1.RunWorkerCompleted LEDSwitch(pbSend, False) End Sub Private Sub backgroundWorker2_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles backgroundWorker2.RunWorkerCompleted LEDSwitch(pbReceive, False) End Sub

需要一个专用的子程序来切换LED灯,例如当数据被接收时。将使用一个子程序来实现这一点,并将子程序绑定到Serialport类实例的DataReceived事件。因为TransmittingData()在主线程上,可以直接切换LED灯。确保后台工作者不忙,以避免多次调用它们。

Private Sub TransmittingData() LEDSwitch(pbSend, True) If Not backgroundWorker1.IsBusy Then ' Start the asynchronous operation. backgroundWorker1.RunWorkerAsync() End If End Sub Private Sub ReceivingData() LEDSwitch(pbReceive, True) If Not backgroundWorker2.IsBusy Then ' Start the asynchronous operation. backgroundWorker2.RunWorkerAsync() End If End Sub

需要的最后一个子程序就是form_Load方法,在其中添加DataReceived事件的处理程序,并将其绑定到ReceivingData()。

Private Sub AsynchPictureboxes_Load(sender As Object, e As EventArgs) Handles MyBase.Load AddHandler sp.DataReceived, AddressOf ReceivingData End Sub

不幸的是,System.IO.Ports.SerialPort类没有DataSent事件,所以不能直接绑定它,但这很容易克服,因为每当需要传输一个字节或多个字节时,可能已经有一个子程序来处理它。将使用调用Serialport.Write方法的子程序来调用TransmittingData()子程序。

如所见,不需要担心切换LED灯的关闭。后台工作者会照顾到这一点。

进一步的开发

可以扩展方法:例如,可以显示和识别端口或数据错误,例如,两个LED灯同时快速闪烁。在这种情况下,可以按照以下方式绑定事件,并编写特定的DataError方法来处理这种情况。

AddHandler sp.ErrorReceived, AddressOf DataError

可以开发进一步的方法来显示,例如,CRC,校验和错误等。

LED灯闪烁的频率可以通过程序更改,通过将适当的脉冲时间传递给后台工作者,以响应特定的消息或数据报。

一个或多个组合框和文本框可以由后台工作者以程序方式填充,例如,实时地与串行通信中传入的数据一起,而不会占用主线程并使其无响应。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485