加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

VB.NET 串口访问之三

发布时间:2020-12-16 22:47:51 所属栏目:大数据 来源:网络整理
导读:程序如下: Imports SystemImports System.Collections.GenericImports System.ComponentModelImports System.DataImports System.DrawingImports System.LinqImports System.TextImports System.IO.PortsImports System.ThreadingImports System.Text.Regul

程序如下:

Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Linq
Imports System.Text
Imports System.IO.Ports
Imports System.Threading
Imports System.Text.RegularExpressions

'例如:AA 44 05 01 02 03 04 05 EA
'    这里我假设的一条数据,协议如下:
'    数据头:     AA 44
'    数据长度: 05
'    数据正文: 01 02 03 04 05
'    校验:       EA
'    一般数据的校验,都会采用常用的方式,CRC16,CRC32,Xor。

Public Class Form1

    WithEvents Comm As SerialPort = New SerialPort
    Private Builder As StringBuilder = New StringBuilder '避免在事件处理方法中反复的创建,所以定义到外面
    Private ReceiveCount As Long = 0     '接收计数
    Private SendCount As Long = 0        '发送计数

    Private Listening As Boolean = False  '是否没有执行完invoke相关操作 
    Private Closingg As Boolean = False     '是否正在关闭串口,执行Application.DoEvents,并阻止再次invoke   

    Private Buffer As List(Of Byte) = New List(Of Byte)(4096) '默认分配1页内存,并始终限制不允许超过   
    Private Binary_Data_1(9) As Byte      'AA 44 05 01 02 03 04 05 EA 

    Public Delegate Sub UpdateData(ByVal mByte() As Byte)


    Public Sub ShowData(ByVal mByte() As Byte)
        Console.WriteLine(mByte)
        ReceiveCount += mByte.Length          '统计字节总数
        Builder.Clear()                       '清除字符串构造器的内容 
        Console.WriteLine("Main1() invoke on thread{0}.",Thread.CurrentThread.ManagedThreadId)
        If CheckBoxHex.Checked Then
            For Each b As Byte In mByte
                Builder.Append(b.ToString("X2") + " ")
            Next
        Else
            Builder.Append(Encoding.ASCII.GetString(mByte))
        End If
        TxtGet.AppendText(Builder.ToString)
        labelGetCount.Text = "Get:" + ReceiveCount.ToString
    End Sub

    Public Delegate Sub UpdateStr(ByVal mByte As String)

    Public Sub ShowStr(ByVal mByte As String)
        TxtGet.Text = mByte
    End Sub

    Private Sub Form1_Load(sender As System.Object,e As System.EventArgs) Handles MyBase.Load

        '初始化下拉串口名称列表框
        Dim Ports() As String = SerialPort.GetPortNames
        Array.Sort(Ports)
        ComboPortName.Items.AddRange(Ports)
        ComboPortName.SelectedIndex = IIf(ComboPortName.Items.Count > 0,-1)
        ComboBaudrate.SelectedIndex = ComboBaudrate.Items.IndexOf("9600")
        '初始化Serialport对象
        Comm.NewLine = vbCrLf
        Comm.RtsEnable = True

        'AddHandler Obj.Ev_Event,AddressOf EventHandler
        'RemoveHandler Obj.Ev_Event,AddressOf EventHandler
        'AddHandler Comm.DataReceived,AddressOf Comm_DataReceived
        BtnXReset.PerformClick()


    End Sub

    Private Sub Comm_DataReceived(sender As Object,e As System.IO.Ports.SerialDataReceivedEventArgs) Handles Comm.DataReceived
        If Closingg Then Return '如果正在关闭,忽略操作,直接返回,尽快的完成串口监听线程的一次循环   

        Try
            Listening = True                    '设置标记,说明我已经开始处理数据,一会儿要使用系统UI的。
            Dim n As Long = Comm.BytesToRead    '先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致   
            Dim Buf(n - 1) As Byte              '声明一个临时数组存储当前来的串口数据 

            Comm.Read(Buf,n)                '读取缓冲数据

            Dim Data_1_Catched As Boolean = False     '缓存记录数据是否捕获到
            Console.WriteLine("Main0() invoke on thread{0}.",Thread.CurrentThread.ManagedThreadId)


            Buffer.AddRange(Buf)

            While Buffer.Count >= 4
                '请不要担心使用>=,因为>=已经和>,<,=一样,是独立操作符,并不是解析成>和=2个符号   
                '查找数据头  

                If (Buffer(0) = &HAA And Buffer(1) = &H44) Then

                    '探测缓存数据是否有一条数据的字节,如果不够,就不用费劲的做其他验证了   
                    '前面已经限定了剩余长度>=4,那我们这里一定能访问到buffer[2]这个长度  

                    Dim Len As Integer = Buffer(2)
                    '数据完整判断第一步,长度是否足够   
                    'len是数据段长度,4个字节是while行注释的3部分长度   

                    If Buffer.Count < Len + 4 Then Exit While '数据不够的时候什么都不做,退出循环

                    '这里确保数据长度足够,数据头标志找到,我们开始计算校验   
                    '2.3 校验数据,确认数据正确   
                    '异或校验,逐个字节异或得到校验码   
                    Dim CheckSum As Byte = 0

                    For i As Integer = 0 To Len + 3
                        CheckSum = CheckSum Xor Buffer(i)
                    Next

                    If CheckSum <> Buffer(Len + 3) Then
                        Buffer.RemoveRange(0,Len + 4)
                        Continue While
                    End If
                    '至此,已经被找到了一条完整数据。我们将数据直接分析,或是缓存起来一起分析   
                    '我们这里采用的办法是缓存一次,好处就是如果你某种原因,数据堆积在缓存buffer中   
                    '已经很多了,那你需要循环的找到最后一组,只分析最新数据,过往数据你已经处理不及时   
                    '了,就不要浪费更多时间了,这也是考虑到系统负载能够降低。 

                    '复制一条完整数据到具体的数据缓存   
                    Buffer.CopyTo(0,Binary_Data_1,Len + 4)

                    Data_1_Catched = True
                    Buffer.RemoveRange(0,Len + 4)
                Else
                    Buffer.RemoveAt(0)  '这里是很重要的,如果数据开始不是头,则删除数据   
                End If
            End While

            If Data_1_Catched Then

                '我们的数据都是定好格式的,所以当我们找到分析出的数据1,就知道固定位置一定是这些数据,我们只要显示就可以了

                Dim data As String = Binary_Data_1(3).ToString("X2") + " " + Binary_Data_1(4).ToString("X2") +
                                     Binary_Data_1(5).ToString("X2") + " " + Binary_Data_1(6).ToString("X2") +
                                     Binary_Data_1(7).ToString("X2")

                Dim a As UpdateStr = New UpdateStr(AddressOf ShowStr)
                Me.BeginInvoke(a,data)

            End If

            '如果需要别的协议,只要扩展这个data_n_catched就可以了。往往我们协议多的情况下,还会包含数据编号,给来的数据进行   
            '编号,协议优化后就是: 头+编号+长度+数据+校验   
            '</协议解析>   

            Dim b As UpdateData = New UpdateData(AddressOf ShowData)
            Me.BeginInvoke(b,Buf)
            
        Catch ex As Exception
            Err.Clear()
        Finally
            Listening = False                    '我用完了,ui可以关闭串口了。
        End Try
    End Sub

    Private Sub BtnXOpen_Click(sender As System.Object,e As System.EventArgs) Handles BtnXOpen.Click
        '根据当前串口对象,来判断操作 
        If Comm.IsOpen Then
            Closingg = True '
            While Listening
                Application.DoEvents()
            End While
            '打开时点击,则关闭串口
            Comm.Close()
            Closingg = False
        Else
            Comm.PortName = ComboPortName.Text
            Comm.BaudRate = Integer.Parse(ComboBaudrate.Text)
            Try
                Comm.Open()
            Catch ex As Exception
                '捕获到异常信息,创建一个新的comm对象,之前的不能用了。 
                Comm = New SerialPort
                '现实异常信息给客户。 
                MessageBox.Show(ex.Message)
            End Try
        End If

        '设置按钮的状态   
        BtnXOpen.Text = IIf(Comm.IsOpen,"Close","Open")
        BtnXOpen.Enabled = Comm.IsOpen

    End Sub

    '动态的修改获取文本框是否支持自动换行。
    Private Sub CheckBoxNewLineGet_CheckedChanged(sender As System.Object,e As System.EventArgs) Handles CheckBoxNewLineGet.CheckedChanged
        TxtGet.WordWrap = CheckBoxNewLineGet.Checked
    End Sub

    Private Sub BtnXSend_Click(sender As System.Object,e As System.EventArgs) Handles BtnXSend.Click
        Dim n As Integer = 0  '定义一个变量,记录发送了几个字节 
        If checkBoxHexSend.Checked Then   '16进制发送 
            '我们不管规则了。如果写错了一些,我们允许的,只用正则得到有效的十六进制数   
            Dim Mc As MatchCollection = Regex.Matches(TxtSend.Text.Trim,"(?i)[/da-f]{2}")   '"(?i)[/da-f]{2}"
            Dim buf As List(Of Byte) = New List(Of Byte)

            '依次添加到列表中   
            For Each m As Match In Mc
                '  buf.Add(Byte.Parse(m.Value))
                buf.Add(Byte.Parse(m.Value,System.Globalization.NumberStyles.HexNumber))
            Next

            '转换列表为数组后发送  
            Comm.Write(buf.ToArray,buf.Count)
            n = buf.Count
        Else                             'ascii编码直接发送 
            '包含换行符
            If checkBoxNewlineSend.Checked Then
                Comm.WriteLine(TxtSend.Text)
                n = TxtSend.Text.Length + 2
            Else
                Comm.Write(TxtSend.Text)
                n = TxtSend.Text.Length
            End If
        End If

        SendCount += n    '累加发送字节数 
        labelSendCount.Text = "Send:" + SendCount.ToString
    End Sub

    Private Sub BtnXReset_Click(sender As System.Object,e As System.EventArgs) Handles BtnXReset.Click

        '复位接受和发送的字节数计数器并更新界面。
        SendCount = 0
        ReceiveCount = 0
        labelGetCount.Text = "Get:0"
        labelSendCount.Text = "Send:0"
        Builder.Clear()

    End Sub

    Private Sub BtxClear_Click(sender As System.Object,e As System.EventArgs) Handles BtxClear.Click
        TxtGet.Text = ""
        Builder.Clear()
    End Sub
End Class

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读