0

我正在尝试制作一个可以从串行端口读取 12 字节十六进制字符串并将其显示到文本框中的应用程序,源是使用 RS232-USB 转换器连接到我的 USB 端口的 RFID 模块。

我的第一个问题是由于无效的跨线程操作而访问 datarecieved 事件中的文本框。在谷歌上搜索后,我复制/粘贴了让它工作所需的代码,这些都是跨线程函数。

OpenPort 函数和 Datarecieved 事件是我自己的代码,其余的我从谷歌上下来。

然后应用程序工作了,但我会得到一个不完整的代码,第一次滑动我会得到所有 12 位数字,并且每隔一次滑动,第一个字节就会消失。在阅读更多内容后,我决定创建一个数组,即传入数据的大小(如下面的代码所示),但现在我再次遇到跨线程错误。

我知道我使用数组的方式有问题。这超出了我的 VB 知识范围,我在这里不知所措,可以使用一些专家帮助。我是一名电子工程师,并且自学了一点编程来为我的硬件创建一些应用程序。

谢谢。

更新代码:

Public Class Form3
    Dim msg As String

    Private Sub Form3_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        For Each s In System.IO.Ports.SerialPort.GetPortNames()
            lstPorts.Items.Add(s)
        Next s

    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        If lstPorts.SelectedIndex = -1 Then
            MessageBox.Show("Please select a port")
            Exit Sub
        Else
            SerialPort1.BaudRate = 9600
            SerialPort1.DataBits = 8
            SerialPort1.Parity = IO.Ports.Parity.None
            SerialPort1.StopBits = IO.Ports.StopBits.One
            SerialPort1.PortName = lstPorts.SelectedItem.ToString
            SerialPort1.Open()
        End If
    End Sub


    Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived

        msg = SerialPort1.ReadExisting


        Me.Invoke(New Action(
        Sub()
            txtReceived.Clear()
            txtReceived.Text += msg
        End Sub))

    End Sub



    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        SerialPort1.Close()
        txtReceived.Text = ""
    End Sub
End Class
4

1 回答 1

0

你在这里读到的任何东西:

If SerialPort1.IsOpen Then
   msg = SerialPort1.ReadExisting()         
End If

当你在这里覆盖它时你会丢失:

msg = enc.GetString(bytesarray) 

我通常不建议尝试直接从串行端口(或 TCP/IP 连接)读取字符串。仅坚持使用数组方法并摆脱 ReadExisting。只有当你知道你有一个完整的“消息”时,我才会将字节转换为字符串。

一些通用提示(尽管这因协议而异):

  1. 定义一个缓冲区来读取您的消息,该缓冲区足以容纳您的整个消息,并且不要使其成为局部变量。
  2. 跟踪该缓冲区的偏移量。
  3. 当您第一次读取时,您的偏移量将为 0,假设您读取了 2 个字节。然后,您的偏移量将为 +2,值为 1。
  4. 在您第二次(和第三次等)读取时,您从该偏移量开始插入,有效地附加新字节。
  5. 每次读取后,您必须检查缓冲区以查看您是否收到了完整的消息。您可以通过读取的总量(如果您有固定长度的消息)或缓冲区中的某些信息(如长度前缀)来执行此操作。阅读消息后,您需要将其从缓冲区中删除。
  6. 我建议从一种简单的方法开始,在删除消息后将偏移量重置为零并继续该过程。如果您收到的数据比您删除的消息长,您需要将这些额外的字节移到“左侧”,以便它们从位置 0 开始。(更有效的方法是使用循环缓冲区,但 KISS for现在)。

在您的 DataRecv 事件中,您应该执行以下操作来更新您的 UI:

   Me.Invoke(New Action(
      Sub()
         'this code runs on the GUI thread, update textboxes!
         Me.Text += msg
      End Sub))

我仍然不是 ReadExisting 的粉丝,但如果你确定总是从设备中获取可打印字符,请尝试以下操作:

Public Class Form3 

    Private pendingMsg As New StringBuffer()

    Private Sub Form3_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
        For Each s In System.IO.Ports.SerialPort.GetPortNames() 
            lstPorts.Items.Add(s) 
        Next s 

    End Sub 

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 
        If lstPorts.SelectedIndex = -1 Then 
            MessageBox.Show("Please select a port") 
            Exit Sub 
        Else 
            SerialPort1.BaudRate = 9600 
            SerialPort1.DataBits = 8 
            SerialPort1.Parity = IO.Ports.Parity.None 
            SerialPort1.StopBits = IO.Ports.StopBits.One 
            SerialPort1.PortName = lstPorts.SelectedItem.ToString 
            SerialPort1.Open() 
        End If 
    End Sub 


    Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived 

        Dim completedMsg As String
        completedMsg = String.Empty

        pendingMsg.Append(SerialPort1.ReadExisting())

        If pendingMsg.Length >= 24 Then '12 bytes in hex is 24 chars

           completedMsg = pendingMsg.ToString(0, 24) '1st 24 bytes are a msg

           pendingMsg.Remove(0, 24) 'pop off the msg from our buffer

           Me.Invoke(New Action( 
           Sub()                
               txtReceived.Text = completedMsg                               
           End Sub)) 

        End If

    End Sub 


    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click 
        SerialPort1.Close() 
        txtReceived.Text = "" 
    End Sub 
End Class
于 2012-04-23T12:48:05.720 回答