Imports System.ComponentModel
Imports System.IO.Ports
Imports System.Security.Cryptography
Imports System.IO


Public Class Form1

    Public Class ROW_ITEM_INFO
        Public index As Integer
        Public upgradeCheck As Boolean
        Public address As Integer
        Public devModel As String
        Public devId As String
        Public runType As String
        Public status As String
        Public btnAllChip As String
        Public btnApp As String
        Public btnEEP As String
    End Class

    Public Enum COLS
        INDEX
        UPGRADE
        ADDR
        DEV_MODEL
        DEVID
        RUN_FLAG
        STATUS
        READ_ALL_CHIP
        READ_APP
        READ_EEPROM
    End Enum

    Public Enum CMD
        LOADER_CMD_SEARCH = &H63
        LOADER_CMD_SET_PARAM = &H69
        LOADER_CMD_WRITE_FLASH = &H60
        LOADER_CMD_READ_FLASH = &H61
        LOADER_CMD_ERASE_FLASH = &H62
        LOADER_CMD_READ_EEPROM = &H65
        LOADER_CMD_JUMP_TO_APP = &H68
        LOADER_CMD_CHECK = &H67

        LOADER_CMD_APP_SEARCH = &H1
        LOADER_CMD_APP_JUMP = &H2

        'BLC-C1 special
        LOADER_CMD_READ_FEATURE_AREA = &H80
        LOADER_CMD_WRITE_FEATURE_AREA = &H81
    End Enum

    Public Enum FMT
        SN
        CMD
        LEN
        CHECK_SUM
        PARAM
    End Enum

    Public Enum FMT_C1
        SN
        CMD
        LEN_H
        LEN_L
        CHECK_SUM
        PARAM
    End Enum

    Public Enum DEV_TYPE
        NANO_485
        PB_485
        BLV_C1

    End Enum

    Public Enum OPT_PROTOCOL_MODE
        HEX_MODE
        TEXT_MODE
    End Enum

    '定义全局变量
    Private Opt_ProtocolMode As Integer = OPT_PROTOCOL_MODE.TEXT_MODE

    Const LOADER_PKT_LEN = 5100 'for C1 每一包有效数据4096byte
    Const LOADER_CMD_OFFSET = &H10
    Const HANDSHAKE_BAUD = 512000   '2400

    'V5.0 Loader_File_Size 重定义大小，应对C1使用需求
    'Const LOADER_FILE_SIZE_MAX = 100 * 1024                 '加载的文件的最大大小
    'Const LOADER_DATA_SIZE_MAX = 32 * 1024                  '下载的数据的最大字节数
    Const LOADER_FILE_SIZE_MAX = 2 * 1024 * 1024
    Const LOADER_DATA_SIZE_MAX = 2 * 1024 * 1024



    Const BOOT_TIMEROUT_MIN = 10                            'BOOT区最小超时 10s
    Property ERASE_CMD_TIMEROUT = 1000                         '擦除命令的超时时间
    Const BOOT_START_ADDR = &H6800                          'BOOT起始地址


    Private COLOR_ERROR As Color = Color.Red
    Private COLOR_NORMAL As Color = Color.Blue
    Private COLOR_MESSAGE As Color = Color.Black

    Private g_SendBuffer(LOADER_PKT_LEN - 1) As Byte
    Private g_RecvBuffer(LOADER_PKT_LEN - 1) As Byte

    'V50 Add bigger Download Package Data Len to fit to C1’
    Private g_HexStartAdd_For_C1 As UInt32 = 0
    Private g_Download_DataLenght_Pre_Package As UInt16 = 128
    Private g_Download_ParamLenght_Pre_Package As UInt16 = 130
    Private g_RecvLen As UInt16
    Private g_SearchStart As Boolean = False            '搜索标志
    Private g_GroupAddrFlag As Boolean = False          '群地址使能标志
    Private g_ParamOffset As UInt16                     '参数偏移量
    Private g_SendSn As Byte = 1                        '序列号
    Private g_UpgradeFlag As Boolean = False            '升级标志
    Private g_UpgradeBtnPressCnt As Byte = 0            '升级按钮按下次数，，针对群升级使用
    Private g_DevType As DEV_TYPE = DEV_TYPE.NANO_485
    Private g_BLVCxPackageLen As UInteger = 2048        'BLV_Cx主机串口升级包长，默认2048 - 20220509 曹聪
    Private g_BLVCxBackupAppOffsetAddr As UInt32 = &H40000 'BLV_Cx主机备份APP偏移地址

    Private g_SearchCount As Integer = 0
    Private g_SelectCount As Integer = 0
    Private g_SuccessCount As Integer = 0
    Private g_FailCount As Integer = 0

    Private g_LoaderHexFileBuffer(LOADER_FILE_SIZE_MAX - 1) As Byte
    Private g_LoaderDataBuffer(LOADER_DATA_SIZE_MAX - 1) As Byte
    Private g_LoaderEndAddr As UInt32

    Private g_BlvBus As BlvBus


    Private g_RichTextBoxLastTick As DateTime

    Private SEARCH_TIMEROUT As UInt16                              '搜索超时时间
    Private g_UpgradeTimerout As UInt16                         '升级包超时时间
    Private g_maxRetry As UInt16                                '重发次数

    Private g_ReadBtnPressCnt As Byte = 0                       '读取Flash按钮的计数
    Private g_ReadEepBtnPressCnt As Byte = 0                    '读取EEP

    Private g_SN_CustomProtocol As Byte = 0

    Private g_CustomProtocolPacketLen As Byte = 0 '自定义协议的包长
    Private g_RcvedPackageLen As Byte = 0 '接收数据包长

    Private g_UpgradeRetryCnt As Byte = 0
    Private g_UpgradeMaxRetry As Byte = 5


    Private Sub UpdateDevType()
        Select Case Cbo_BusType.Text
            Case "NANO 485"
                g_DevType = DEV_TYPE.NANO_485
                g_Download_DataLenght_Pre_Package = 128
                ERASE_CMD_TIMEROUT = 2000
            Case "PB 485"
                g_DevType = DEV_TYPE.PB_485
                g_Download_DataLenght_Pre_Package = 128
            Case "BLV_C1","CH592"
                g_DevType = DEV_TYPE.BLV_C1
                'g_Download_DataLenght_Pre_Package = Val(tb_C1PackageLen.Text)
                g_Download_DataLenght_Pre_Package = g_BLVCxPackageLen
            Case Else
                Cbo_BusType.Text = "NANO 485"
                g_DevType = DEV_TYPE.NANO_485
        End Select
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        AddCboPort()        '加载一个串口号到控件

        My.Settings.Reload()
        Cbo_Port.Text = My.Settings.CommPortNumber
        tb_CustomeProtocol_JumpToBl.Text = My.Settings.CustomeProtocol_JumpToBootloader
        tb_CustomeProtocol_Serach.Text = My.Settings.CustomeProtocol_Serach
        'Tb_HexFilePath.Text = My.Settings.HexFilePatch
        Tb_HexFilePath.Text = Nothing
        num_AddrStart.Value = My.Settings.SerachAddStart
        num_AddrEnd.Value = My.Settings.SerachAddEnd
        Cbo_BusType.Text = My.Settings.BusType
        'tb_C1PackageLen.Text = My.Settings.C1PackageLenght
        Me.Height = My.Settings.Windows_High

        If My.Settings.SerachTimeout > 0 Then TextBox1.Text = My.Settings.SerachTimeout
        If My.Settings.UpgradeTimeout > 0 Then TextBox2.Text = My.Settings.UpgradeTimeout
        If My.Settings.BootloaderTimeout > 0 Then Tb_Timeout.Text = My.Settings.BootloaderTimeout
        If My.Settings.CommBaudRate > 0 Then Cbo_Baud.Text = My.Settings.CommBaudRate

        Chk_UpgradeAllSel.Checked = My.Settings.chk_UpgradeAllSelect
        'Cbo_ProType.Text = My.Settings.AppProtocolSetectedItem
        Opt_ProtocolMode = My.Settings.Opt_ProtocolMode
        If Opt_ProtocolMode = OPT_PROTOCOL_MODE.HEX_MODE Then opt_ProtocolHexMode.Checked = True
        If Opt_ProtocolMode = OPT_PROTOCOL_MODE.TEXT_MODE Then opt_ProtocolTextMode.Checked = True

        Cbo_ProType.Text = "Customize Protocol"


        g_BlvBus = New BlvBus(RichTextBox1)
        UpdateCountInfo()
        UpdateDevType()

        SEARCH_TIMEROUT = Val(TextBox1.Text)
        g_UpgradeTimerout = Val(TextBox2.Text)
        g_maxRetry = NumericUpDown3.Value

        btn_Reload.PerformClick()
        Me.Text = "Massduino Nano485 Loader(" & Application.ProductVersion & ")" & " - " & Tb_HexFilePath.Text

        TabPage2.Parent = Nothing
        TabPage3.Parent = Nothing
    End Sub

    Private Sub AddCboPort()
        Dim pItem As String() = System.IO.Ports.SerialPort.GetPortNames

        If pItem.Length Then
            Cbo_Port.Items.Clear()
            Cbo_Port.Items.AddRange(pItem)
            Cbo_Port.SelectedIndex = 0
        End If
    End Sub

    Private Sub Chk_OneAddEn_CheckedChanged(sender As Object, e As EventArgs) Handles chk_OneAddEn.CheckedChanged
        If chk_OneAddEn.Checked Then
            chk_GroupAddEn.Checked = False
        Else
            chk_GroupAddEn.Checked = True
        End If
    End Sub

    Private Sub Chk_GroupAddEn_CheckedChanged(sender As Object, e As EventArgs) Handles chk_GroupAddEn.CheckedChanged
        If chk_GroupAddEn.Checked Then
            chk_OneAddEn.Checked = False
            Btn_Search.Enabled = False
        Else
            chk_OneAddEn.Checked = True
            Btn_Search.Enabled = True
        End If
    End Sub

    Private Sub Chk_AllSelect_CheckedChanged(sender As Object, e As EventArgs) Handles chk_AllSelect.CheckedChanged
        SetGroupAddCheckBoxAll(chk_AllSelect.Checked)
    End Sub

    Private Sub GetGroupAddr(ByRef groupAdd() As Byte)
        Dim i As Byte
        Dim j As Byte

        Array.Clear(groupAdd, 0, 4)
        For j = 0 To 3
            For i = 0 To 7
                If GetGroupAddrCheckBox(j * 8 + i) Then
                    groupAdd(j) += 2 ^ i
                End If
            Next
        Next
    End Sub

    Private Sub SetGroupAddrCheckBox(ByVal index As Byte, ByVal isEn As Boolean)
        Select Case index
            Case 0
                CheckBox1.Checked = isEn
            Case 1
                CheckBox2.Checked = isEn
            Case 2
                CheckBox3.Checked = isEn
            Case 3
                CheckBox4.Checked = isEn
            Case 4
                CheckBox5.Checked = isEn
            Case 5
                CheckBox6.Checked = isEn
            Case 6
                CheckBox7.Checked = isEn
            Case 7
                CheckBox8.Checked = isEn
            Case 8
                CheckBox9.Checked = isEn
            Case 9
                CheckBox10.Checked = isEn
            Case 10
                CheckBox11.Checked = isEn
            Case 11
                CheckBox12.Checked = isEn
            Case 12
                CheckBox13.Checked = isEn
            Case 13
                CheckBox14.Checked = isEn
            Case 14
                CheckBox15.Checked = isEn
            Case 15
                CheckBox16.Checked = isEn
            Case 16
                CheckBox17.Checked = isEn
            Case 17
                CheckBox18.Checked = isEn
            Case 18
                CheckBox19.Checked = isEn
            Case 19
                CheckBox20.Checked = isEn
            Case 20
                CheckBox21.Checked = isEn
            Case 21
                CheckBox22.Checked = isEn
            Case 22
                CheckBox23.Checked = isEn
            Case 23
                CheckBox24.Checked = isEn
            Case 24
                CheckBox25.Checked = isEn
            Case 25
                CheckBox26.Checked = isEn
            Case 26
                CheckBox27.Checked = isEn
            Case 27
                CheckBox28.Checked = isEn
            Case 28
                CheckBox29.Checked = isEn
            Case 29
                CheckBox30.Checked = isEn
            Case 30
                CheckBox31.Checked = isEn
            Case 31
                CheckBox32.Checked = isEn
        End Select
    End Sub

    Private Function GetGroupAddrCheckBox(ByVal index As Byte) As Boolean
        Dim retCheck As Boolean
        Select Case index
            Case 0
                retCheck = CheckBox1.Checked
            Case 1
                retCheck = CheckBox2.Checked
            Case 2
                retCheck = CheckBox3.Checked
            Case 3
                retCheck = CheckBox4.Checked
            Case 4
                retCheck = CheckBox5.Checked
            Case 5
                retCheck = CheckBox6.Checked
            Case 6
                retCheck = CheckBox7.Checked
            Case 7
                retCheck = CheckBox8.Checked
            Case 8
                retCheck = CheckBox9.Checked
            Case 9
                retCheck = CheckBox10.Checked
            Case 10
                retCheck = CheckBox11.Checked
            Case 11
                retCheck = CheckBox12.Checked
            Case 12
                retCheck = CheckBox13.Checked
            Case 13
                retCheck = CheckBox14.Checked
            Case 14
                retCheck = CheckBox15.Checked
            Case 15
                retCheck = CheckBox16.Checked
            Case 16
                retCheck = CheckBox17.Checked
            Case 17
                retCheck = CheckBox18.Checked
            Case 18
                retCheck = CheckBox19.Checked
            Case 19
                retCheck = CheckBox20.Checked
            Case 20
                retCheck = CheckBox21.Checked
            Case 21
                retCheck = CheckBox22.Checked
            Case 22
                retCheck = CheckBox23.Checked
            Case 23
                retCheck = CheckBox24.Checked
            Case 24
                retCheck = CheckBox25.Checked
            Case 25
                retCheck = CheckBox26.Checked
            Case 26
                retCheck = CheckBox27.Checked
            Case 27
                retCheck = CheckBox28.Checked
            Case 28
                retCheck = CheckBox29.Checked
            Case 29
                retCheck = CheckBox30.Checked
            Case 30
                retCheck = CheckBox31.Checked
            Case 31
                retCheck = CheckBox32.Checked
        End Select

        Return retCheck
    End Function

    Private Sub SetGroupAddCheckBoxAll(ByVal isEn As Boolean)
        Dim i As Byte
        For i = 0 To 31
            SetGroupAddrCheckBox(i, isEn)
        Next
    End Sub

    Private Sub Cbo_Port_DropDown(sender As Object, e As EventArgs) Handles Cbo_Port.DropDown
        Cbo_Port.Items.Clear()
        Cbo_Port.Items.AddRange(System.IO.Ports.SerialPort.GetPortNames)
    End Sub

    Private Sub SetAddrControl(ByVal isEn As Boolean)
        chk_OneAddEn.Enabled = isEn
        chk_GroupAddEn.Enabled = isEn
        num_AddrStart.Enabled = isEn
        num_AddrEnd.Enabled = isEn
    End Sub

    Private Sub Btn_Search_Click(sender As Object, e As EventArgs) Handles Btn_Search.Click

        If g_SearchStart = False Then
            RichTextBox1.Clear()
            g_SearchStart = True
            Btn_Upgrade.Enabled = False
            Btn_Search.Text = "Stop"
            'Btn_Search.BackColor = Color.Green
            SetAddrControl(False)
            SearchDevice() '搜索
            SetAddrControl(True)
            Btn_Search.Text = "Serach"
            'Btn_Search.BackColor = SystemColors.Control
            Btn_Upgrade.Enabled = True
            g_SearchStart = False
        Else
            g_SearchStart = False
        End If
    End Sub

    Private Sub JudgeGroupAddr()
        If g_DevType = DEV_TYPE.BLV_C1 Then
            g_ParamOffset = FMT_C1.PARAM + 1
        Else
            If chk_OneAddEn.Checked Then
                g_GroupAddrFlag = False
                g_ParamOffset = FMT.PARAM + 1
            Else
                g_GroupAddrFlag = True
                g_ParamOffset = FMT.PARAM + 4
            End If
        End If

    End Sub

    Public Sub SetStatus(ByVal conStatus As ToolStripStatusLabel, ByVal status As String, ByVal clr As Color)
        conStatus.Text = status
        conStatus.ForeColor = clr
        Application.DoEvents()
    End Sub

    Public Sub SetStatus(ByVal status As String, ByVal clr As Color)
        tssl_Status.Text = status
        tssl_Status.ForeColor = clr
        Application.DoEvents()
    End Sub


    '搜索的时候要充分考虑 g_SearchStart 变量的问题，应有效防止中途停止搜索的情况
    Private Sub SearchDevice()
        If Port.IsOpen = False Then MsgBox("Please Open Com Port!") : Exit Sub

        DataGridView1.Rows.Clear()

        Dim index As Integer = 0
        Dim address As Integer = 0
        Dim readValid As Boolean = False
        Dim addrStart = num_AddrStart.Value
        Dim addrEnd = num_AddrEnd.Value
        Dim isExist As Boolean = False
        Dim devId As String = ""
        Dim bootFlag As String = ""
        Dim devModel As String = ""
        Dim pRowItemInfo As New ROW_ITEM_INFO

        g_SearchCount = 0
        g_SelectCount = 0
        g_SuccessCount = 0
        g_FailCount = 0

        JudgeGroupAddr()        '判定是单发还是群发

        address = addrStart
        While g_SearchStart = True
            If Port.IsOpen = False Then MsgBox("Please Open Com Port!") : Exit Sub

            RichTextBox_Show("Serach by DevAdd：" & address, Color.Black)
            SetStatus("Serach by DevAdd：" & address, Color.Black)

            If Not Cbo_BusType.Text = "BLV_C1" Then
                SetPortBaud(2400)       '设置波特率
            Else
                SetPortBaud(HANDSHAKE_BAUD)       '设置波特率
            End If


            'Bootloder 区搜索           （地址， devID  bootFlag  devModel 重发次数，发命令后等待多久认为无回复）
            readValid = SearchDeviceCmd(address, devId, bootFlag, devModel, g_maxRetry, SEARCH_TIMEROUT) 'Boot 区搜索，采用2400bps
            If readValid = False Then
                SetPortBaud(Val(Cbo_Baud.Text))       '设置波特率
                Delay(1)
                'App 区域搜索
                readValid = AllAppSearchDeviceCmd(address, devId, bootFlag, devModel, g_maxRetry, SEARCH_TIMEROUT) 'App区搜索，采用设定波特率
            End If
            SetPortBaud(Val(Cbo_Baud.Text))       '设置波特率

            If readValid = True Then

                isExist = False
                For Each pRow As DataGridViewRow In DataGridView1.Rows
                    If pRow.Cells(COLS.ADDR).Value = address Then
                        isExist = True
                        Exit For
                    End If
                Next

                If isExist = False Then
                    pRowItemInfo.index = index + 1
                    If Chk_UpgradeAllSel.Checked Then
                        pRowItemInfo.upgradeCheck = True
                        g_SelectCount += 1
                    End If
                    pRowItemInfo.address = address
                    pRowItemInfo.devModel = devModel
                    pRowItemInfo.devId = devId
                    pRowItemInfo.runType = bootFlag
                    pRowItemInfo.btnAllChip = "Read"
                    pRowItemInfo.btnApp = "Read"
                    pRowItemInfo.btnEEP = "Read"

                    AddItem(pRowItemInfo)

                    index += 1
                    g_SearchCount += 1
                    UpdateCountInfo()       '更新搜索总数
                End If

            End If

            address += 1
            If address > addrEnd Then
                address = addrStart
            End If
            'Application.DoEvents()
            Delay(10)
        End While

        SetStatus("Serach completed. Total devices: " & g_SearchCount, Color.Green)
    End Sub

    Public Sub UpdateCountInfo()
        ToolStripStatusLabel1.Text = "Total Devices:" & " " & g_SearchCount & "    " & "Total Selected:" & " " & g_SelectCount & "    " & "Upgrade Completed:" & " " & g_SuccessCount & "    " & "Upgrade Fail:" & " " & g_FailCount
    End Sub

    Private Function AddItem(ByVal item As ROW_ITEM_INFO) As Boolean
        Dim pRow As New DataGridViewRow
        Dim pCell As DataGridViewCell

        pCell = New DataGridViewTextBoxCell
        pCell.Value = item.index
        pRow.Cells.Add(pCell)

        pCell = New DataGridViewCheckBoxCell
        pCell.Value = item.upgradeCheck
        pRow.Cells.Add(pCell)

        pCell = New DataGridViewTextBoxCell
        pCell.Value = item.address
        pRow.Cells.Add(pCell)

        pCell = New DataGridViewTextBoxCell
        pCell.Value = item.devModel
        pRow.Cells.Add(pCell)

        pCell = New DataGridViewTextBoxCell
        pCell.Value = item.devId
        pRow.Cells.Add(pCell)

        pCell = New DataGridViewTextBoxCell
        pCell.Value = item.runType
        pRow.Cells.Add(pCell)

        pCell = New DataGridViewTextBoxCell
        pCell.Value = item.status
        pRow.Cells.Add(pCell)

        pCell = New DataGridViewButtonCell
        pCell.Value = item.btnAllChip
        pRow.Cells.Add(pCell)

        pCell = New DataGridViewButtonCell
        pCell.Value = item.btnApp
        pRow.Cells.Add(pCell)

        pCell = New DataGridViewButtonCell
        pCell.Value = item.btnEEP
        pRow.Cells.Add(pCell)

        pRow.Tag = item

        DataGridView1.Rows.Add(pRow)
        Return True
    End Function

    Private Function HexCalcCheckSum(buf() As Byte, len As UInt16) As UInt64
        Dim sum As Int64
        Dim i As UInt16

        For i = 0 To len - 1
            sum += buf(i)
            sum = sum And &HFF
        Next
        sum = ((Not sum) + 1) And &HFF
        Return sum
    End Function

    Private Function CalcCheckSum(ByVal buf() As Byte, ByVal len As UInt16) As Byte
        Dim sum As Int16 = 0
        Dim i As UInt16 = 0
        Dim retByte As Byte

        For i = 0 To len - 1
            sum += buf(i)
            sum = sum And &HFF
        Next

        retByte = (Not sum) And &HFF

        Return retByte
    End Function

    Private Sub CalcCRC16(ByVal buf() As Byte, ByVal len As UInt32, ByRef outBuf() As Byte, ByRef outLen As UInt16)
        Dim i As UInt32 = g_HexStartAdd_For_C1 '从起始地址开始校验
        Dim j As UInt32 = 0
        Dim crcVal As UInt16 = 0
        Dim index As UInt16 = 0
        Dim consstr As String = ""
        Do
            'V60: BLV-C1 CRC计算长度由512Bytes改为2048Bytes
            If g_DevType = DEV_TYPE.BLV_C1 Then
                crcVal = CRC16.NetCRC16(buf, i, 2048, consstr)
                outBuf(index) = crcVal \ 256
                index += 1
                outBuf(index) = crcVal Mod 256
                index += 1

                i += 2048



            Else
                crcVal = CRC16.NetCRC16(buf, i, 512, consstr)
                outBuf(index) = crcVal \ 256
                index += 1
                outBuf(index) = crcVal Mod 256
                index += 1

                i += 512
                'RichTextBox_Show($"CRC 数据包{vbCrLf }{consstr}{vbCrLf }{Hex(outBuf(index - 2)).PadLeft(2, "0")} {Hex(outBuf(index - 1)).PadLeft(2, "0")}", Color.OrangeRed)
            End If
        Loop Until i >= len
        outLen = index
        RichTextBox_Show("CRC", outBuf, outLen, COLOR_ERROR)
    End Sub

    Private Sub CalcCheckSum(ByVal buf() As Byte, ByVal len As UInt32, ByRef resultBuf() As Byte)
        Dim sum As Int64 = 0
        Dim i As UInt32 = 0
        Dim j As UInt16 = 0
        Dim retByte As Int64

        Do
            For j = 0 To 127
                sum += buf(i + j)
                sum = sum And &HFFFFFFFF
            Next
            i += 128
        Loop Until i >= len

        'retByte = (Not sum) And &HFFFFFFFF
        retByte = sum

        'TextBox1.Text = Hex(retByte)

        resultBuf(0) = (retByte \ &H1000000) And &HFF
        resultBuf(1) = (retByte \ &H10000) And &HFF
        resultBuf(2) = (retByte \ &H100) And &HFF
        resultBuf(3) = retByte And &HFF
    End Sub

    Private Sub FillSendBuf(ByRef sendBuf() As Byte, cmd As Byte, paramLen As UInt16, Optional ByVal address As Integer = 0)
        Dim groupAddr(3) As Byte
        Dim tmpC1Len As UInt16 = 0
        'For BLV-C1, LEN有两个字节
        If g_DevType = DEV_TYPE.BLV_C1 Then
            sendBuf(FMT_C1.CMD) = cmd

            sendBuf(FMT_C1.LEN_H) = (g_ParamOffset + paramLen) \ 256
            sendBuf(FMT_C1.LEN_L) = (g_ParamOffset + paramLen) Mod 256
            sendBuf(FMT_C1.CHECK_SUM) = 0
            sendBuf(FMT_C1.SN) = g_SendSn And &H3F

            If g_GroupAddrFlag Then
                sendBuf(FMT_C1.SN) = sendBuf(FMT_C1.SN) Or &H80
                GetGroupAddr(groupAddr)
                Array.Copy(groupAddr, 0, sendBuf, FMT_C1.PARAM, 4)
            Else
                sendBuf(FMT_C1.PARAM) = address
            End If
            tmpC1Len = sendBuf(FMT_C1.LEN_H) * 256 + sendBuf(FMT_C1.LEN_L)
            sendBuf(FMT_C1.CHECK_SUM) = CalcCheckSum(sendBuf, tmpC1Len)
        Else
            sendBuf(FMT.CMD) = cmd

            sendBuf(FMT.LEN) = g_ParamOffset + paramLen
            sendBuf(FMT.CHECK_SUM) = 0
            sendBuf(FMT.SN) = g_SendSn And &H3F

            If g_GroupAddrFlag Then
                sendBuf(FMT.SN) = sendBuf(FMT.SN) Or &H80
                GetGroupAddr(groupAddr)
                Array.Copy(groupAddr, 0, sendBuf, FMT.PARAM, 4)
            Else
                sendBuf(FMT.PARAM) = address
            End If
            sendBuf(FMT.CHECK_SUM) = CalcCheckSum(sendBuf, sendBuf(FMT.LEN))
        End If

        g_SendSn = g_SendSn + 1
        If g_SendSn > &H3F Then
            g_SendSn = 0
        End If
    End Sub

    ''' <summary>
    ''' 擦除FLASH命令
    ''' </summary>
    ''' <param name="address"></param>
    ''' <param name="maxRetry"></param>
    ''' <param name="timeout"></param>
    ''' <returns></returns>
    Private Function EraseFlashCmd(ByVal address As Byte, ByVal maxRetry As UInt16, ByVal timeout As UInt16) As Boolean
        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)

        If Chk_LogicFile.Checked = True Then    '当前处于下发逻辑文件中，擦除逻辑文件
            g_SendBuffer(g_ParamOffset) = &H2
        ElseIf Chk_BackupUpgrade.Checked = True Then    '当前处于下发备份APP中，擦除备份APP
            g_SendBuffer(g_ParamOffset) = &H1
        Else
            g_SendBuffer(g_ParamOffset) = &H0
        End If

        FillSendBuf(g_SendBuffer, CMD.LOADER_CMD_ERASE_FLASH, 1, address)

        Dim tmpLen As UInt16 = 0
        If g_DevType = DEV_TYPE.BLV_C1 Then
            tmpLen = g_SendBuffer(FMT_C1.LEN_H) * 256 + g_SendBuffer(FMT_C1.LEN_L)
        Else
            tmpLen = g_SendBuffer(FMT.LEN)
        End If
        Return WriteToDevice(g_SendBuffer, tmpLen, g_RecvBuffer, g_RecvLen, g_GroupAddrFlag, maxRetry, timeout)
    End Function

    Private Function WriteFlashCmd(ByVal address As Byte, ByVal flashAddr As UInt32, ByVal flashDataBuffer() As Byte, ByVal bufferIndex As UInt32, ByVal maxRetry As UInt16, ByVal timeout As UInt16) As Boolean
        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)

        If g_DevType = DEV_TYPE.BLV_C1 Then
            'g_SendBuffer(g_ParamOffset) = flashAddr Mod 65536
            'g_SendBuffer(g_ParamOffset + 1) = g_SendBuffer(g_ParamOffset) \ 256
            'g_SendBuffer(g_ParamOffset + 2) = g_SendBuffer(g_ParamOffset) Mod 256
            g_SendBuffer(g_ParamOffset) = (flashAddr >> 16) And &HFF
            g_SendBuffer(g_ParamOffset + 1) = (flashAddr >> 8) And &HFF
            g_SendBuffer(g_ParamOffset + 2) = flashAddr And &HFF
            g_Download_ParamLenght_Pre_Package = g_Download_DataLenght_Pre_Package + 3 '往下发数据+地址总长度，C1的地址多一个byte

            Array.Copy(flashDataBuffer, bufferIndex, g_SendBuffer, g_ParamOffset + 3, g_Download_DataLenght_Pre_Package)
        Else
            g_SendBuffer(g_ParamOffset) = flashAddr \ 256
            g_SendBuffer(g_ParamOffset + 1) = flashAddr Mod 256
            g_Download_ParamLenght_Pre_Package = g_Download_DataLenght_Pre_Package + 2 '往下发数据+地址总长度

            Array.Copy(flashDataBuffer, bufferIndex, g_SendBuffer, g_ParamOffset + 2, g_Download_DataLenght_Pre_Package)
        End If

        FillSendBuf(g_SendBuffer, CMD.LOADER_CMD_WRITE_FLASH, g_Download_ParamLenght_Pre_Package, address)

        Dim tmpLen As UInt16 = 0
        If g_DevType = DEV_TYPE.BLV_C1 Then
            tmpLen = g_SendBuffer(FMT_C1.LEN_H) * 256 + g_SendBuffer(FMT_C1.LEN_L)
        Else
            tmpLen = g_SendBuffer(FMT.LEN)
        End If
        Return WriteToDevice(g_SendBuffer, tmpLen, g_RecvBuffer, g_RecvLen, g_GroupAddrFlag, maxRetry, timeout)
    End Function

    Private Function ReadEeprom(readFlashDlg As ReadFlash, ByVal address As Byte) As Boolean
        Dim eepAddr As UInt16 = 0
        Dim eepEndAddr As UInt16 = 1 * 1024
        Dim loaderNum As UInt16

        Do
            If ReadEEPROMCmd(address, eepAddr, 128) Then
                ShowFlashData(readFlashDlg.DataGridView1, eepAddr, g_RecvBuffer)

                loaderNum = (eepAddr * 100) / eepEndAddr
                If loaderNum > 100 Then loaderNum = 100
                readFlashDlg.ToolStripProgressBar1.Value = loaderNum

                Application.DoEvents()          '处理Windows消息的方法
            Else
                Return False
            End If

            eepAddr += 128
            If g_ReadEepBtnPressCnt <> 2 Then Return False
        Loop Until eepAddr >= eepEndAddr
        readFlashDlg.ToolStripProgressBar1.Value = 100
        Return True
    End Function

    Private Function ReadFlash(readFlashDlg As ReadFlash, ByVal address As Byte, ByVal endAddr As UInt32) As Boolean
        Dim flashAddr As UInt32 = 0
        Dim flashEndAddr As UInt16 = endAddr
        Dim loaderNum As UInt16
        Do
            If ReadFlashCmd(address, flashAddr, 128) Then
                ShowFlashData(readFlashDlg.DataGridView1, flashAddr, g_RecvBuffer)

                loaderNum = (flashAddr * 100) / flashEndAddr
                If loaderNum > 100 Then loaderNum = 100
                readFlashDlg.ToolStripProgressBar1.Value = loaderNum

                Application.DoEvents()          '处理Windows消息的方法
            Else
                Return False
            End If
            flashAddr += 128
            If g_ReadBtnPressCnt <> 2 Then Return False
        Loop Until flashAddr >= flashEndAddr

        readFlashDlg.ToolStripProgressBar1.Value = 100

        Return True
    End Function

    Private Sub ShowFlashData(ByRef ReadDlg_DataGridView1 As DataGridView, ByVal flashAddr As UInt32, ByVal dataBuf() As Byte)
        Dim i As UInt16 = 0
        Dim rowsNum As UInt32 = flashAddr \ 16
        Dim j As UInt16
        Dim rowsDataBuf(15) As Byte

        ReadDlg_DataGridView1.Rows.Add()
        ReadDlg_DataGridView1.Rows(rowsNum).HeaderCell.Value = Hex(flashAddr).PadLeft(4, "0")
        For i = 0 To 127
            If (i >= 16) AndAlso (i Mod 16 = 0) Then

                For j = 0 To 15
                    rowsDataBuf(j) = "&H" & ReadDlg_DataGridView1.Rows(rowsNum).Cells(j).Value
                    If rowsDataBuf(j) < &H20 OrElse rowsDataBuf(j) > &H7E Then
                        rowsDataBuf(j) = &H2E
                    End If
                Next
                ReadDlg_DataGridView1.Rows(rowsNum).Cells(16).Value = System.Text.Encoding.ASCII.GetString(rowsDataBuf)

                ReadDlg_DataGridView1.Rows.Add()
                rowsNum += 1
                ReadDlg_DataGridView1.Rows(rowsNum).HeaderCell.Value = Hex(rowsNum * 16).PadLeft(4, "0")
                ReadDlg_DataGridView1.FirstDisplayedScrollingRowIndex = ReadDlg_DataGridView1.Rows.Count - 1       '将内容滚动的作用
            End If
            ReadDlg_DataGridView1.Rows(rowsNum).Cells(i Mod 16).Value = Hex(dataBuf(FMT.PARAM + 1 + i)).PadLeft(2, "0")
            'Application.DoEvents()          '处理Windows消息的方法
        Next

        For j = 0 To 15
            rowsDataBuf(j) = "&H" & ReadDlg_DataGridView1.Rows(rowsNum).Cells(j).Value
            If rowsDataBuf(j) < &H20 OrElse rowsDataBuf(j) > &H7E Then
                rowsDataBuf(j) = &H2E
            End If
        Next
        ReadDlg_DataGridView1.Rows(rowsNum).Cells(16).Value = System.Text.Encoding.ASCII.GetString(rowsDataBuf)
    End Sub

    Private Function ReadFlashCmd(ByVal address As Byte, ByVal flashAddr As UInt32, ByVal dataLen As UInt16) As Boolean
        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)

        If g_DevType = DEV_TYPE.BLV_C1 Then
            g_SendBuffer(g_ParamOffset) = flashAddr \ 65536
            g_SendBuffer(g_ParamOffset + 1) = (g_SendBuffer(g_ParamOffset) Mod 65536) \ 256
            g_SendBuffer(g_ParamOffset + 2) = (g_SendBuffer(g_ParamOffset) Mod 65536) Mod 256

            g_SendBuffer(g_ParamOffset + 3) = dataLen \ 256
            g_SendBuffer(g_ParamOffset + 4) = dataLen Mod 256

        Else
            g_SendBuffer(g_ParamOffset) = flashAddr \ 256
            g_SendBuffer(g_ParamOffset + 1) = flashAddr Mod 256
            g_SendBuffer(g_ParamOffset + 2) = dataLen \ 256
            g_SendBuffer(g_ParamOffset + 3) = dataLen Mod 256
        End If


        FillSendBuf(g_SendBuffer, CMD.LOADER_CMD_READ_FLASH, 4, address)

        Dim tmpLen As UInt16 = 0
        If g_DevType = DEV_TYPE.BLV_C1 Then
            tmpLen = g_SendBuffer(FMT_C1.LEN_H) * 256 + g_SendBuffer(FMT_C1.LEN_L)
        Else
            tmpLen = g_SendBuffer(FMT.LEN)
        End If
        Return WriteToDevice(g_SendBuffer, tmpLen, g_RecvBuffer, g_RecvLen, g_GroupAddrFlag, g_maxRetry, g_UpgradeTimerout)
    End Function

    Private Function ReadEEPROMCmd(ByVal address As Byte, ByVal eepAddr As UInt16, ByVal eepLen As UInt16) As Boolean
        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)
        g_SendBuffer(g_ParamOffset) = eepAddr \ 256
        g_SendBuffer(g_ParamOffset + 1) = eepAddr Mod 256
        g_SendBuffer(g_ParamOffset + 2) = eepLen \ 256
        g_SendBuffer(g_ParamOffset + 3) = eepLen Mod 256
        FillSendBuf(g_SendBuffer, CMD.LOADER_CMD_READ_EEPROM, 4, address)

        Dim tmpLen As UInt16 = 0
        If g_DevType = DEV_TYPE.BLV_C1 Then
            tmpLen = g_SendBuffer(FMT_C1.LEN_H) * 256 + g_SendBuffer(FMT_C1.LEN_L)
        Else
            tmpLen = g_SendBuffer(FMT.LEN)
        End If
        Return WriteToDevice(g_SendBuffer, tmpLen, g_RecvBuffer, g_RecvLen, g_GroupAddrFlag, g_maxRetry, g_UpgradeTimerout)
    End Function


    Private Function SearchDeviceCmd(ByVal address As Byte, ByRef devId As String, ByRef bootFlag As String, ByRef devModel As String, ByVal maxRetry As Integer, ByVal timeout As Integer) As Boolean
        Dim devModelLen As Int16
        Dim tmpLen As UInt16 = 0

        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)
        Dim bootTimerout As UInt32 = Val(Tb_Timeout.Text)
        If bootTimerout < BOOT_TIMEROUT_MIN Then
            bootTimerout = BOOT_TIMEROUT_MIN
        ElseIf bootTimerout > 65535 Then
            bootTimerout = 65535
        End If
        g_SendBuffer(g_ParamOffset) = bootTimerout \ 256
        g_SendBuffer(g_ParamOffset + 1) = bootTimerout Mod 256
        FillSendBuf(g_SendBuffer, CMD.LOADER_CMD_SEARCH, 2, address)

        If g_DevType = DEV_TYPE.BLV_C1 Then
            tmpLen = g_SendBuffer(FMT_C1.LEN_H) * 256 + g_SendBuffer(FMT_C1.LEN_L)
        Else
            tmpLen = g_SendBuffer(FMT.LEN)
        End If

        If WriteToDevice(g_SendBuffer, tmpLen, g_RecvBuffer, g_RecvLen, g_GroupAddrFlag, maxRetry, timeout) Then
            If g_DevType = DEV_TYPE.BLV_C1 Then
                devId = g_RecvBuffer(FMT_C1.PARAM + 1).ToString("X02") & " " & g_RecvBuffer(FMT_C1.PARAM + 2).ToString("X02") & " " & g_RecvBuffer(FMT_C1.PARAM + 3).ToString("X02") & " " & g_RecvBuffer(FMT_C1.PARAM + 4).ToString("X02")
            Else
                devId = g_RecvBuffer(FMT.PARAM + 1).ToString("X02") & " " & g_RecvBuffer(FMT.PARAM + 2).ToString("X02") & " " & g_RecvBuffer(FMT.PARAM + 3).ToString("X02") & " " & g_RecvBuffer(FMT.PARAM + 4).ToString("X02")
            End If

            bootFlag = "BOOT"

            devModelLen = g_RecvLen - FMT.PARAM - 5
            If devModelLen > 0 Then
                devModel = System.Text.Encoding.ASCII.GetString(g_RecvBuffer, FMT.PARAM + 5, devModelLen)
            Else
                devModel = "---"
            End If
            Return True
        End If

        Return False
    End Function

    Private g_RcvedString As String = ""
    Private Function AppSerachDeviceCmd_ByCustom_TextMode(ByVal address As Byte, ByRef devId As String, ByRef bootFlag As String, ByRef devModel As String, Optional ByVal maxRetry As Integer = 3, Optional ByVal timeout As Integer = 100) As Boolean
        '自定义TEXT 模式下的APP区搜索命令，
        Dim SentString = CreateSentTextString(tb_CustomeProtocol_Serach.Text, address)

        '发送搜索命令
        If WriteToDevice_ByCustomProtocol_TextMode(SentString, g_RcvedString, maxRetry, timeout) Then
            devId = 0
            bootFlag = "APP"
            'V2.0 2020-8-26 Momo:APP搜索时，返回长度必须大于等于搜索命令，并且前面几个字节必须和搜索命令相同，后面部分分离出来默认为是机型编号
            'V2.3 2020-10-3 Momo:Text模式下的protocol：
            'sent：Searach:01
            'recv: Searach:01=MD-485
            If (g_RcvedString.Length >= SentString.Length + 1) Then
                devModel = Microsoft.VisualBasic.Right(g_RcvedString, g_RcvedString.Length - SentString.Length + 1)
                'MsgBox(devModel)
            Else
                Return False
                devModel = ""
            End If
            Return True
        End If
        Return False
    End Function
    Private Function WriteToDevice_ByCustomProtocol_TextMode(ByVal SentString As String, ByRef RcvString As String, ByVal maxRetry As Integer, ByVal timeout As Integer) As Boolean
        Dim retryCount As Byte = 0
        Dim retFlag As Boolean = False
        Dim timeTick As UInt32
        Dim rLen As Integer = 0
        Dim rLen_Last As Integer = 0
        Dim readBuffNoDataTimeCount As Integer = 0
        Dim tmpRcvString = ""
        Dim tmpIdx As Integer = 0
        Do
            RichTextBox_Show("TX retry = " & retryCount & "," & SentString, COLOR_MESSAGE)
            retFlag = UartSentBuf_TextMode(SentString)                 '发送数据
            rLen = 0
            WaitSendComp(SentString.Length)                            '等待发送出去

            If g_DevType = DEV_TYPE.PB_485 Then
                Delay(0.8 * SentString.Length)
            End If

            If retFlag Then
                'RichTextBox_Show("retFlag = true", Color.DarkGreen)
                retFlag = False
                timeTick = My.Computer.Clock.TickCount
                Do
                    Try
                        rLen = Port.BytesToRead
                    Catch ex As Exception
                        rLen = 0
                    End Try
                    If rLen > 0 Then
                        If rLen <> rLen_Last Then   '判断收到的数据长度有没有变化
                            rLen_Last = rLen
                            timeTick = My.Computer.Clock.TickCount
                            readBuffNoDataTimeCount = 0
                        Else
                            readBuffNoDataTimeCount = readBuffNoDataTimeCount + 1
                        End If
                    End If
                    Delay(1)
                Loop Until (My.Computer.Clock.TickCount - timeTick) > timeout Or readBuffNoDataTimeCount >= 20 '退出循环条件：超时到达，或者连续20次缓冲区长度没有变化

                'RichTextBox_Show("RecviedFinished: rLen=" & rLen & ", SentString.Length=" & SentString.Length, Color.DarkGreen)

                If rLen >= SentString.Length - 2 Then '缓冲区数据长度大于等于发送长度就认为接收正确
                    'RichTextBox_Show("RcvString", Color.DarkGreen)

                    '2021-3-24 V3.0 用Port.ReadLine会导致没有换行符时死等在这里
                    ' RcvString = Port.ReadLine
                    Dim bytes(rLen - 1) As Byte
                    Port.Read(bytes, 0, rLen)
                    RcvString = System.Text.UTF8Encoding.UTF8.GetString(bytes)
                    '=================================================’

                    If Microsoft.VisualBasic.Left(RcvString, SentString.Length - 2) = Microsoft.VisualBasic.Left(SentString, SentString.Length - 2) Then
                        If RcvString.Contains(vbCr) Then
                            RichTextBox_Show("接受数据成功：" & RcvString, COLOR_NORMAL)
                            Return True
                        Else
                            RichTextBox_Show("数据未含newline", COLOR_ERROR)
                            retryCount = retryCount + 1
                        End If

                    Else
                        RichTextBox_Show("接收数据头部不匹配", COLOR_ERROR)
                        retryCount = retryCount + 1
                    End If
                Else
                    RichTextBox_Show("接收数据长度不正确", COLOR_ERROR)
                    retryCount = retryCount + 1
                End If
            Else
                retryCount = retryCount + 1
            End If
        Loop Until retryCount > maxRetry
        Return False
    End Function

    Private Function UartSentBuf_TextMode(ByVal SentStringBuff As String) As Boolean
        Try
            Port.DiscardOutBuffer()
            Port.DiscardInBuffer()
        Catch ex As Exception
            Return False
        End Try

        Try
            Port.Write(SentStringBuff)
        Catch ex As Exception
            Return False
        End Try

        Return True
    End Function


    Private Function CreateSentTextString(txtString As String, address As Byte) As String
        Dim tmpString As String
        '找到 <SN> 字符串并且用SN替换
        tmpString = txtString.Replace("<ADD>", address)
        '增加vbnewline
        tmpString = tmpString & vbNewLine

        Return tmpString
    End Function

    Private Function AppSerachDeviceCmd_ByCustom(ByVal address As Byte, ByRef devId As String, ByRef bootFlag As String, ByRef devModel As String, Optional ByVal maxRetry As Integer = 3, Optional ByVal timeout As Integer = 100) As Boolean
        '自定义写一下的APP区搜索命令，Nano485和NanoPB采用这个协议
        Dim tmpSentBufLen = TextStringToByteArray(tb_CustomeProtocol_Serach.Text, address)
        Dim tmpIdx As Integer = 0
        If WriteToDevice_ByCustomProtocol(g_SendBuffer, tmpSentBufLen, g_RecvBuffer, g_RecvLen, g_GroupAddrFlag, maxRetry, timeout) Then
            devId = 0
            bootFlag = "APP"
            'V2.0 2020-8-26 Momo:APP搜索时，返回必须大于等于搜索命令，并且前面几个字节必须和搜索命令相同，后面部分分离出来默认为是机型编号
            If (g_RcvedPackageLen >= g_CustomProtocolPacketLen) And g_CustomProtocolPacketLen >= 1 Then
                For tmpIdx = 0 To g_CustomProtocolPacketLen - 1
                    If g_RecvBuffer(tmpIdx) <> g_SendBuffer(tmpIdx) Then
                        devModel = ""
                        Return False
                    End If
                Next tmpIdx
                devModel = System.Text.Encoding.ASCII.GetString(g_RecvBuffer, g_CustomProtocolPacketLen, g_RcvedPackageLen - g_CustomProtocolPacketLen)
                'MsgBox(devModel)
            Else
                Return False
                'devModel = System.Text.Encoding.ASCII.GetString(g_RecvBuffer, 0, g_RcvedPackageLen)
                devModel = ""
            End If
            Return True
        End If
        Return False
    End Function
    Private Function AppSearchDeviceCmd(ByVal address As Byte, ByRef devId As String, ByRef bootFlag As String, ByRef devModel As String, Optional ByVal maxRetry As Integer = 3, Optional ByVal timeout As Integer = 100) As Boolean
        Dim devModelLen As Int16

        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)
        FillSendBuf(g_SendBuffer, CMD.LOADER_CMD_APP_SEARCH, 0, address)

        Dim tmpLen As UInt16 = 0
        If g_DevType = DEV_TYPE.BLV_C1 Then
            tmpLen = g_SendBuffer(FMT_C1.LEN_H) * 256 + g_SendBuffer(FMT_C1.LEN_L)
        Else
            tmpLen = g_SendBuffer(FMT.LEN)
        End If

        If WriteToDevice(g_SendBuffer, tmpLen, g_RecvBuffer, g_RecvLen, g_GroupAddrFlag, maxRetry, timeout) Then
            devId = g_RecvBuffer(FMT.PARAM + 1).ToString("X02") & " " & g_RecvBuffer(FMT.PARAM + 2).ToString("X02") & " " & g_RecvBuffer(FMT.PARAM + 3).ToString("X02") & " " & g_RecvBuffer(FMT.PARAM + 4).ToString("X02")
            bootFlag = "APP"

            devModelLen = g_RecvLen - FMT.PARAM - 5
            If devModelLen > 0 Then
                devModel = System.Text.Encoding.ASCII.GetString(g_RecvBuffer, FMT.PARAM + 5, g_RecvLen - FMT.PARAM - 5)
            Else
                devModel = "---"
            End If
            Return True
        End If
        Return False
    End Function

    Private Function AllAppSearchDeviceCmd(ByVal address As Byte, ByRef devId As String, ByRef bootFlag As String, ByRef devModel As String, ByVal maxRetry As Integer, ByVal timeout As Integer) As Boolean
        'BLW Protocol
        'Public Protocol
        'Customize Protocol


        If Cbo_ProType.Text = "BLW Protocol" Then
            Return g_BlvBus.SearchDeviceCmd(Val(Tb_DevType.Text), Port, address, devId, bootFlag, devModel, g_GroupAddrFlag, g_DevType, maxRetry, timeout)
        ElseIf Cbo_ProType.Text = "Public Protocol" Then
            Return AppSearchDeviceCmd(address, devId, bootFlag, devModel, maxRetry, timeout)
        ElseIf Cbo_ProType.Text = "Customize Protocol" Then
            '用户自定义协议，Nano485采用这个协议
            'V2.3 2020-10-3 Momo:自定义协议分Text和Hex两种
            If Opt_ProtocolMode = OPT_PROTOCOL_MODE.HEX_MODE Then
                Return AppSerachDeviceCmd_ByCustom(address, devId, bootFlag, devModel, maxRetry, timeout)
            ElseIf Opt_ProtocolMode = OPT_PROTOCOL_MODE.TEXT_MODE Then
                Return AppSerachDeviceCmd_ByCustom_TextMode(address, devId, bootFlag, devModel, maxRetry, timeout)
            Else
                Return False
            End If
        End If
        Return False
    End Function

    Private Function AllAppJumpCmd(ByVal address As Byte, ByVal maxRetry As Integer, ByVal timeout As Integer) As Boolean
        If Cbo_ProType.Text = "BLW Protocol" Then
            Return g_BlvBus.JumpBootCmd(Val(Tb_DevType.Text), Port, address, g_GroupAddrFlag, g_DevType, g_maxRetry, g_UpgradeTimerout)
        ElseIf Cbo_ProType.Text = "Public Protocol" Then
            Return AppJumpCmd(address, g_maxRetry, g_UpgradeTimerout)
        ElseIf Cbo_ProType.Text = "Customize Protocol" Then
            '用户自定义协议，Nano485采用这个协议
            'V2.3 2020-10-3 Momo:自定义协议分Text和Hex两种
            If Opt_ProtocolMode = OPT_PROTOCOL_MODE.HEX_MODE Then
                Return JumpToAppByCustomProtocol(address, g_maxRetry, g_UpgradeTimerout)
            ElseIf Opt_ProtocolMode = OPT_PROTOCOL_MODE.TEXT_MODE Then
                Return JumpToAppByCustomProtocol_TEXT_MODE(address, g_maxRetry, g_UpgradeTimerout)

            End If
        End If
        Return False
    End Function


    Private Function AppJumpCmdByCustomProtocol(ByVal address As Byte, ByVal maxRetry As Integer, ByVal timeout As Integer) As Boolean
        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)
        'FillSendBuf(g_SendBuffer, CMD.LOADER_CMD_APP_JUMP, 0, address)

        Dim tmpLen As UInt16 = 0
        If g_DevType = DEV_TYPE.BLV_C1 Then
            tmpLen = g_SendBuffer(FMT_C1.LEN_H) * 256 + g_SendBuffer(FMT_C1.LEN_L)
        Else
            tmpLen = g_SendBuffer(FMT.LEN)
        End If
        Return WriteToDevice(g_SendBuffer, tmpLen, g_RecvBuffer, g_RecvLen, g_GroupAddrFlag, maxRetry, timeout)
    End Function
    Private Function AppJumpCmd(ByVal address As Byte, ByVal maxRetry As Integer, ByVal timeout As Integer) As Boolean
        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)
        FillSendBuf(g_SendBuffer, CMD.LOADER_CMD_APP_JUMP, 0, address)
        Dim tmpLen As UInt16 = 0
        If g_DevType = DEV_TYPE.BLV_C1 Then
            tmpLen = g_SendBuffer(FMT_C1.LEN_H) * 256 + g_SendBuffer(FMT_C1.LEN_L)
        Else
            tmpLen = g_SendBuffer(FMT.LEN)
        End If
        Return WriteToDevice(g_SendBuffer, tmpLen, g_RecvBuffer, g_RecvLen, g_GroupAddrFlag, maxRetry, timeout)
    End Function

    Private Sub SearchDeviceCmd(ByVal maxRetry As Integer, ByVal timeout As Integer)
        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)
        Dim bootTimerout As UInt32 = Val(Tb_Timeout.Text)
        If bootTimerout < BOOT_TIMEROUT_MIN Then
            bootTimerout = BOOT_TIMEROUT_MIN
        ElseIf bootTimerout > 65535 Then
            bootTimerout = 65535
        End If
        g_SendBuffer(g_ParamOffset) = bootTimerout \ 256
        g_SendBuffer(g_ParamOffset + 1) = bootTimerout Mod 256
        FillSendBuf(g_SendBuffer, CMD.LOADER_CMD_SEARCH, 2)

        Dim tmpLen As UInt16 = 0
        If g_DevType = DEV_TYPE.BLV_C1 Then
            tmpLen = g_SendBuffer(FMT_C1.LEN_H) * 256 + g_SendBuffer(FMT_C1.LEN_L)
        Else
            tmpLen = g_SendBuffer(FMT.LEN)
        End If
        WriteToDevice(g_SendBuffer, tmpLen, g_RecvBuffer, g_RecvLen, g_GroupAddrFlag, maxRetry, timeout)
    End Sub

    Private Function SetParamCmd(ByVal address As Byte, ByVal baud As UInt32, ByVal timerout As UInt32)


        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)
        g_SendBuffer(g_ParamOffset) = (baud \ 100) \ 256
        g_SendBuffer(g_ParamOffset + 1) = (baud \ 100) Mod 256
        g_SendBuffer(g_ParamOffset + 2) = timerout \ 256
        g_SendBuffer(g_ParamOffset + 3) = timerout Mod 256
        g_SendBuffer(g_ParamOffset + 4) = g_DevType
        FillSendBuf(g_SendBuffer, CMD.LOADER_CMD_SET_PARAM, 5, address)

        Dim tmpLen As UInt16 = 0
        If g_DevType = DEV_TYPE.BLV_C1 Then
            tmpLen = g_SendBuffer(FMT_C1.LEN_H) * 256 + g_SendBuffer(FMT_C1.LEN_L)
        Else
            tmpLen = g_SendBuffer(FMT.LEN)
        End If
        Return WriteToDevice(g_SendBuffer, tmpLen, g_RecvBuffer, g_RecvLen, g_GroupAddrFlag, g_maxRetry, g_UpgradeTimerout)
    End Function

    Private Sub SetParamCmd(ByVal baud As UInt32, ByVal timerout As UInt32, ByVal maxRetry As Integer, ByVal timeout As Integer)
        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)
        g_SendBuffer(g_ParamOffset) = (baud \ 100) \ 256
        g_SendBuffer(g_ParamOffset + 1) = (baud \ 100) Mod 256
        g_SendBuffer(g_ParamOffset + 2) = timerout \ 256
        g_SendBuffer(g_ParamOffset + 3) = timerout Mod 256
        g_SendBuffer(g_ParamOffset + 4) = g_DevType
        FillSendBuf(g_SendBuffer, CMD.LOADER_CMD_SET_PARAM, 5)

        Dim tmpLen As UInt16 = 0
        If g_DevType = DEV_TYPE.BLV_C1 Then
            tmpLen = g_SendBuffer(FMT_C1.LEN_H) * 256 + g_SendBuffer(FMT_C1.LEN_L)
        Else
            tmpLen = g_SendBuffer(FMT.LEN)
        End If
        WriteToDevice(g_SendBuffer, tmpLen, g_RecvBuffer, g_RecvLen, g_GroupAddrFlag, maxRetry, timeout)
    End Sub
    '将文本框中的数值转换为hex数组，支持关键字
    Private Function TextStringToByteArray(txtString As String, address As Byte) As UShort
        Dim tmp() As String
        Dim idx As Integer
        Dim idxOfCks As Integer
        Dim tmpStr As String
        Dim SendBufferCorrectIndex As Integer = 0
        Dim tmpIfCksByteExist As Boolean = False
        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)
        Try

            '分割文本框内的内容并生成hex数组
            tmp = Split(txtString, Chr(32))
            For idx = 0 To UBound(tmp)    'UBound()取数组内元素的个数
                'MsgBox(tmp(idx))
                tmpStr = tmp(idx)
                If tmpStr <> " " AndAlso tmpStr <> "" Then
                    If UCase(tmpStr) = "<ADD>" Then
                        g_SendBuffer(SendBufferCorrectIndex) = address
                    ElseIf UCase(tmpStr) = "<TYPE>" Then
                        g_SendBuffer(SendBufferCorrectIndex) = Tb_DevType.Text
                    ElseIf UCase(tmpStr) = "<CKS>" Then
                        tmpIfCksByteExist = True
                        idxOfCks = SendBufferCorrectIndex
                        g_SendBuffer(SendBufferCorrectIndex) = &H0
                    ElseIf UCase(tmpStr) = "<SN>" Then
                        g_SendBuffer(SendBufferCorrectIndex) = g_SN_CustomProtocol
                    Else
                        g_SendBuffer(idx) = "&H" & tmpStr
                    End If
                    SendBufferCorrectIndex = SendBufferCorrectIndex + 1
                End If
            Next idx
            '填写CheckSum字节
            If tmpIfCksByteExist Then
                g_SendBuffer(idxOfCks) = CalcCheckSum(g_SendBuffer, SendBufferCorrectIndex)
            End If
            g_CustomProtocolPacketLen = SendBufferCorrectIndex '将自定义协议字节数保存在SendBufferCorrectIndex内，便于搜索时解析出正确机型信息
            Return SendBufferCorrectIndex
        Catch ex As Exception
            Return SendBufferCorrectIndex
        End Try
        Return SendBufferCorrectIndex
    End Function
    Private Function JumpToAppByCustomProtocol_TEXT_MODE(ByVal address As Byte, ByVal maxRetry As Integer, ByVal timeout As Integer) As Boolean
        '自定义TEXT 模式下的APP区跳转命令，
        Dim SentString = CreateSentTextString(tb_CustomeProtocol_JumpToBl.Text, address)
        '发送跳转命令
        If WriteToDevice_ByCustomProtocol_TextMode(SentString, g_RcvedString, maxRetry, timeout) Then
            'V2.3 2020-10-3 Momo:Text模式下的protocol：
            'sent：Jump:01
            'recv: Jump:01=OK
            Return True
        End If
        Return False
    End Function

    Private Function JumpToAppByCustomProtocol(ByVal address As Byte, ByVal maxRetry As Integer, ByVal timeout As Integer) As Boolean
        Dim tmpSentBufLen = TextStringToByteArray(tb_CustomeProtocol_JumpToBl.Text, address)
        Return WriteToDevice_ByCustomProtocol(g_SendBuffer, tmpSentBufLen, g_RecvBuffer, g_RecvLen, g_GroupAddrFlag, maxRetry, timeout)

    End Function
    Private Function JumpToApp(ByVal address As Byte, ByVal maxRetry As Integer, ByVal timeout As Integer) As Boolean
        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)
        FillSendBuf(g_SendBuffer, CMD.LOADER_CMD_JUMP_TO_APP, 0, address)

        Dim tmpLen As UInt16 = 0
        If g_DevType = DEV_TYPE.BLV_C1 Then
            tmpLen = g_SendBuffer(FMT_C1.LEN_H) * 256 + g_SendBuffer(FMT_C1.LEN_L)
        Else
            tmpLen = g_SendBuffer(FMT.LEN)
        End If
        Return WriteToDevice(g_SendBuffer, tmpLen, g_RecvBuffer, g_RecvLen, g_GroupAddrFlag, maxRetry, timeout)
    End Function
    Private Sub JumpToApp()
        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)
        FillSendBuf(g_SendBuffer, CMD.LOADER_CMD_JUMP_TO_APP, 0)


        Dim tmpLen As UInt16 = 0
        If g_DevType = DEV_TYPE.BLV_C1 Then
            tmpLen = g_SendBuffer(FMT_C1.LEN_H) * 256 + g_SendBuffer(FMT_C1.LEN_L)
        Else
            tmpLen = g_SendBuffer(FMT.LEN)
        End If
        WriteToDevice(g_SendBuffer, tmpLen, g_RecvBuffer, g_RecvLen, g_GroupAddrFlag, g_maxRetry, g_UpgradeTimerout)
    End Sub

    Private Function WriteToDevice_ByCustomProtocol(ByVal sendBuf() As Byte, ByVal sendLen As UInt16, ByRef recvBuf() As Byte, ByRef recvLen As UInt16, ByVal groupFlag As Boolean, ByVal maxRetry As Integer, ByVal timeout As Integer) As Boolean
        Dim retryCount As Byte = 0
        Dim result As Boolean = False
        Dim retFlag As Boolean = False
        Dim timeTick As UInt32
        Dim rLen As Integer = 0
        Dim readIndex As UInt16 = 0
        Dim readBuffNoDataTimeCount As Integer = 0

        'Momo 2020-3-11 自定义协议中序列号累加
        If g_SN_CustomProtocol < &HFF Then
            g_SN_CustomProtocol = g_SN_CustomProtocol + 1
        Else
            g_SN_CustomProtocol = 0
        End If

        Do
            RichTextBox_Show("TX retry = " & retryCount & ",", sendBuf, sendLen, COLOR_MESSAGE)
            readBuffNoDataTimeCount = 0 '发送前先把接受超时清零
            retFlag = UartSendBuf(sendBuf, sendLen)                 '发送数据
            readIndex = 0
            WaitSendComp(sendLen)                                   '等待发送出去

            If g_DevType = DEV_TYPE.PB_485 Then
                Delay(0.8 * sendLen)
            End If

            If retFlag Then
                If groupFlag = False Then                           '对于 groupFlag = false 应该要做延时，不能一直不停的发
                    retFlag = False
                    timeTick = My.Computer.Clock.TickCount
                    Do
                        Try
                            rLen = Port.BytesToRead
                            'RichTextBox_Show("rLen = " & rLen & "...", sendBuf, sendLen, COLOR_MESSAGE)
                        Catch ex As Exception
                            rLen = 0
                        End Try

                        If rLen > 0 Then   '等待接收数据
                            timeTick = My.Computer.Clock.TickCount
                            Port.Read(recvBuf, readIndex, rLen)
                            readIndex += rLen
                            readBuffNoDataTimeCount = 0
                            retFlag = True
                        Else
                            readBuffNoDataTimeCount = readBuffNoDataTimeCount + 1
                            If readBuffNoDataTimeCount >= 20 Then 'Momo 2020-3-11 对于自定义协议接收数据的判断：连续5次读到接收buffer为空
                                readBuffNoDataTimeCount = 0
                                Exit Do
                            End If
                        End If
                        Delay(1)
                    Loop Until (My.Computer.Clock.TickCount - timeTick) > timeout

                    If retFlag Then
                        RichTextBox_Show("RX", recvBuf, readIndex, COLOR_MESSAGE)
                        recvLen = readIndex
                        g_RcvedPackageLen = recvLen 'g_RcvedPackageLen 用于从回复包中提取机型编号
                        result = True
                    Else
                        RichTextBox_Show("No Data Received!", COLOR_ERROR)
                    End If
                Else
                    Delay(20) '如果是群发就不接收，直接延时20mS
                    'result = True 'Momo 2020-3-11：如果是群发，可能导致一直Do循环直到最大重试次数超出，然后返回错误，因此加这个值确保群发时默认发成功。
                End If
            End If

            If result = True Then Exit Do
            retryCount += 1
        Loop Until retryCount > maxRetry
        Return result
    End Function

    Private Function WriteToDevice(ByVal sendBuf() As Byte, ByVal sendLen As UInt16, ByRef recvBuf() As Byte, ByRef recvLen As UInt16, ByVal groupFlag As Boolean, ByVal maxRetry As Integer, ByVal timeout As Integer) As Boolean
        Dim retryCount As Byte = 0
        Dim result As Boolean = False
        Dim retFlag As Boolean = False
        Dim timeTick As UInt32
        Dim rLen As Integer = 0
        Dim readIndex As UInt16 = 0

        Do
            RichTextBox_Show("TX", sendBuf, sendLen, COLOR_MESSAGE)
            retFlag = UartSendBuf(sendBuf, sendLen)                 '发送数据
            readIndex = 0 '重置接受缓存区指针
            WaitSendComp(sendLen)                                   '等待发送出去

            If g_DevType = DEV_TYPE.PB_485 Then
                Delay(0.8 * sendLen)
            End If

            If retFlag Then
                If groupFlag = False Then                           '对于 groupFlag = false 应该要做延时，不能一直不停的发
                    retFlag = False
                    timeTick = My.Computer.Clock.TickCount
                    Do
                        Try
                            rLen = Port.BytesToRead
                        Catch ex As Exception
                            rLen = 0
                        End Try

                        If rLen > 0 Then                                    '等待接收数据
                            timeTick = My.Computer.Clock.TickCount
                            Port.Read(recvBuf, readIndex, rLen)
                            readIndex += rLen

                            If g_DevType = DEV_TYPE.BLV_C1 Then
                                If readIndex > FMT_C1.LEN_L AndAlso readIndex >= (recvBuf(FMT_C1.LEN_H) * 256 + recvBuf(FMT_C1.LEN_L)) Then
                                    retFlag = True
                                    Exit Do
                                End If
                            Else
                                If readIndex > FMT.LEN AndAlso readIndex >= recvBuf(FMT.LEN) Then
                                    retFlag = True
                                    Exit Do
                                End If
                            End If
                        End If
                    Loop Until (My.Computer.Clock.TickCount - timeTick) > timeout Or (readIndex >= 255)

                    If retFlag Then
                        RichTextBox_Show("RX", recvBuf, readIndex, COLOR_MESSAGE)

                        'C1的Len是2bytes
                        If g_DevType = DEV_TYPE.BLV_C1 Then
                            If recvBuf(FMT_C1.CMD) = sendBuf(FMT_C1.CMD) + LOADER_CMD_OFFSET AndAlso
                                                        recvBuf(FMT_C1.LEN_H) * 256 + recvBuf(FMT_C1.LEN_L) = readIndex AndAlso
                                                        recvBuf(FMT_C1.PARAM) = sendBuf(FMT_C1.PARAM) AndAlso
                                                        CalcCheckSum(recvBuf, readIndex) = 0 Then
                                recvLen = readIndex
                                result = True
                            End If
                        Else
                            If recvBuf(FMT.CMD) = sendBuf(FMT.CMD) + LOADER_CMD_OFFSET AndAlso
                                                        recvBuf(FMT.LEN) = readIndex AndAlso
                                                        recvBuf(FMT.PARAM) = sendBuf(FMT.PARAM) AndAlso
                                                        CalcCheckSum(recvBuf, readIndex) = 0 Then
                                recvLen = readIndex
                                result = True
                            End If
                        End If

                    Else
                        If readIndex > 0 Then
                            RichTextBox_Show("RX", recvBuf, readIndex, COLOR_ERROR)
                        Else
                            RichTextBox_Show("No Data Received!", COLOR_ERROR)
                        End If

                    End If
                Else
                    Delay(20)
                End If
            End If

            If result = True Then Exit Do
            retryCount += 1
        Loop Until retryCount > maxRetry

        Return result
    End Function

    Private Sub WaitSendComp()
        Do
            'Delay(1)
        Loop Until Port.BytesToWrite = 0
    End Sub

    Private Sub WaitSendComp(ByVal count As Integer)
        Dim baudrate As Integer = Port.BaudRate
        Dim delayTime As Integer = 1000 * 10 * count / baudrate
        Dim timeEnd As Integer = My.Computer.Clock.TickCount + delayTime
        Do
            Application.DoEvents()
        Loop Until My.Computer.Clock.TickCount > timeEnd
    End Sub



    Private Function UartSendBuf(ByVal buf() As Byte, ByVal len As UInt16) As Boolean
        Try
            Port.DiscardOutBuffer()
            Port.DiscardInBuffer()
        Catch ex As Exception
            Return False
        End Try

        Try
            Port.Write(buf, 0, len)
        Catch ex As Exception
            Return False
        End Try

        Return True
    End Function




    Private Sub Delay(ByVal time As Integer)
        Dim timeEnd As Integer = My.Computer.Clock.TickCount + time
        Do
            Application.DoEvents()
        Loop Until My.Computer.Clock.TickCount > timeEnd
    End Sub

    Private Sub Btn_OpenPort_Click(sender As Object, e As EventArgs) Handles Btn_OpenPort.Click

        If Cbo_Port.Text = "" Then MsgBox("Please select a valid Com Port number!") ： Exit Sub
        If Cbo_Baud.Text = "" Then MsgBox("Please select a properly Baud Rate!") : Exit Sub
        If Cbo_BusType.Text = "" Then MsgBox("Please select a Device Type!") : Exit Sub
        UpdateDevType()

        If Port.IsOpen = False Then
            SetSerialInfo(Val(Cbo_Baud.Text))
            Try
                Port.Open()
            Catch ex As Exception
                MsgBox("Open Com Port fail：" & vbNewLine & ex.Message) : Exit Sub
            End Try

            Btn_OpenPort.Text = "Close Com Port"
            Cbo_Port.Enabled = False
            Cbo_Baud.Enabled = False
        Else
            Try
                Port.Close()
            Catch ex As Exception
                MsgBox("Close Com Port fail：" & vbNewLine & ex.Message)
            End Try

            Btn_OpenPort.Text = "Open Com Port"
            Cbo_Port.Enabled = True
            Cbo_Baud.Enabled = True
        End If

        If g_DevType = DEV_TYPE.PB_485 Then
            Select Case Cbo_Baud.Text
                Case 9600
                    Port.DtrEnable = False
                Case 2400
                    Port.DtrEnable = True
                Case Else
                    'MsgBox("The NanoPB just support baudrate for 2400bps or 9600bps!")
                    Cbo_Baud.Text = 9600
                    Port.BaudRate = 9600
                    Port.DtrEnable = True
            End Select
        End If

        If Port.IsOpen Then
            UpdateDevType()
            Btn_OpenPort.ForeColor = Color.Green
        Else
            Btn_OpenPort.ForeColor = Color.Red
        End If
        UpdateDevType()
    End Sub


    Public Sub SetSerialInfo(ByVal baud As UInt32)
        With Port
            .PortName = Cbo_Port.Text
            .BaudRate = baud
            .DataBits = 8
            .StopBits = IO.Ports.StopBits.One       '停止位
            .Parity = IO.Ports.Parity.None          '奇偶校验位
            '.DtrEnable = True
        End With
    End Sub

    Private Sub SetPortBaud(ByVal baud As UInt32)

        If Port.BaudRate = baud Then
            Console.WriteLine($"波特率:{baud}相同，不用设置")
            Return
        End If

        Select Case baud
            Case 9600
                Port.BaudRate = 9600
                If g_DevType = DEV_TYPE.PB_485 Then
                    Port.DtrEnable = False
                    Delay(1000)
                End If
            Case 2400
                Port.BaudRate = 2400
                If g_DevType = DEV_TYPE.PB_485 Then
                    Port.DtrEnable = True
                    Delay(1000)
                End If
            Case Else
                Port.BaudRate = baud
        End Select
        Delay(2)
        RichTextBox1.AppendText(vbCrLf & "    *** 波特率设置成功：” & Port.BaudRate & vbCrLf)
    End Sub

    Dim g_SyncFlag As Boolean = False

    Private Sub Btn_Upgrade_Click(sender As Object, e As EventArgs) Handles Btn_Upgrade.Click

        JudgeGroupAddr()                    '更新群标志

        If g_GroupAddrFlag Then             '群升级标志

            If g_UpgradeBtnPressCnt = 0 Then
                g_UpgradeBtnPressCnt += 1

                g_UpgradeFlag = True
                Btn_Upgrade.Text = "正在同步中"
                g_SyncFlag = True
                Btn_Upgrade.BackColor = Color.Lime
                Btn_Search.Enabled = False
                GroupUpgradeDevice() '群升级’
                g_UpgradeFlag = False
                g_UpgradeBtnPressCnt = 0
                Btn_Upgrade.Text = "升级"
                Btn_Upgrade.BackColor = SystemColors.Control
                'Btn_Search.Enabled = True
            ElseIf g_UpgradeBtnPressCnt = 1 Then
                g_UpgradeBtnPressCnt += 1

                g_SyncFlag = False
                Btn_Upgrade.Text = "停止"
                Btn_Upgrade.BackColor = Color.Green
            Else
                g_UpgradeBtnPressCnt = 0
                g_UpgradeFlag = False
            End If

        Else '单个升级，采用双向通讯
            If g_UpgradeFlag = False Then
                RichTextBox1.Clear()
                g_UpgradeFlag = True
                Btn_Upgrade.Text = "Stop"
                Btn_Upgrade.BackColor = Color.Green
                Btn_Search.Enabled = False
                UpgradeDevice()                 '单对单升级
                g_UpgradeFlag = False
                Btn_Upgrade.Text = "Upgrade"
                Btn_Upgrade.BackColor = SystemColors.Control
                Btn_Search.Enabled = True
            Else
                g_UpgradeFlag = False
            End If
        End If

    End Sub

    Private Sub GroupUpgradeDevice()
        If Port.IsOpen = False Then MsgBox("Please Open Com Port!") : Exit Sub
        DataGridView1.Rows.Clear()              '清除表格
        FillDataGridView()                      '填充表格

        If g_SelectCount = 0 Then
            SetStatus("Please Select the device which you want to upgrade!", Color.Red)
            Return
        End If

        If Tb_HexFilePath.Text = "" Then
            SetStatus("Please Open Hex file!", Color.Red)
            Return
        End If

        If LoadDataFromFile(Tb_HexFilePath.Text) = False Then
            SetStatus("Hex file format error!", Color.Red)
            Return
        End If

        SetStatus("Handshaking...", COLOR_MESSAGE)
        showItemStatus("Handshaking", COLOR_MESSAGE)
        Dim devAddress As Byte = 0                          '跳转指令和Boot搜索指令同时发5次
        While g_SyncFlag

            If Not Cbo_BusType.Text = "BLV_C1" Then
                SetPortBaud(2400)       '设置波特率
            Else
                SetPortBaud(HANDSHAKE_BAUD)       '设置波特率
            End If

            RichTextBox_Show("Handshak in bootloader", COLOR_ERROR)
            SearchDeviceCmd(0, SEARCH_TIMEROUT)
            SetPortBaud(Val(Cbo_Baud.Text))
            RichTextBox_Show("Jump To Bootloader", COLOR_ERROR)
            AllAppJumpCmd(devAddress, 0, SEARCH_TIMEROUT)
        End While

        SetStatus("Set Parameter", COLOR_MESSAGE)
        showItemStatus("Set Parameter", COLOR_MESSAGE)
        If Not Cbo_BusType.Text = "BLV_C1" Then
            SetPortBaud(2400)       '设置波特率
        Else
            SetPortBaud(HANDSHAKE_BAUD)       '设置波特率
        End If

        RichTextBox_Show("Set Parameter", COLOR_ERROR)
        SetParamCmd(Val(Cbo_Baud.Text), Val(Tb_Timeout.Text), g_maxRetry, g_UpgradeTimerout)
        SetPortBaud(Val(Cbo_Baud.Text))
        SetStatus("Handshak again", COLOR_MESSAGE)
        showItemStatus("Handshak again", COLOR_MESSAGE)
        RichTextBox_Show("Handshak again", COLOR_ERROR)
        SearchDeviceCmd(g_maxRetry, g_UpgradeTimerout)
        SetStatus("Upgrading", COLOR_MESSAGE)
        showItemStatus("Upgrading", COLOR_MESSAGE)
        RichTextBox_Show("Upgrading", COLOR_ERROR)
        If DownloadData() Then
            SetStatus("Verfiy", COLOR_MESSAGE)
            showItemStatus("Verfiy", COLOR_MESSAGE)
            RichTextBox_Show("Verfiy", COLOR_ERROR)
            CheckCmd()
            SetStatus("Jump To App", COLOR_MESSAGE)
            showItemStatus("Jump To App", COLOR_MESSAGE)
            RichTextBox_Show("Jump To App", COLOR_ERROR)
            JumpToApp()
            SetStatus("Upgrade Completed", COLOR_NORMAL)
            showItemStatus("Upgrade Completed", COLOR_NORMAL)

            g_SuccessCount = g_SelectCount
            UpdateCountInfo()
        Else
            showItemStatus("Abort Upgrading", COLOR_ERROR)

            g_FailCount = g_SelectCount
            UpdateCountInfo()
        End If

    End Sub

    Private Sub FillDataGridView()
        Dim groupAddr(3) As Byte
        GetGroupAddr(groupAddr)
        Chk_UpgradeAllSel.Checked = True

        g_SearchCount = 0
        g_SelectCount = 0
        g_SuccessCount = 0
        g_FailCount = 0
        UpdateCountInfo()

        Dim index As UInt16 = 1
        Dim address As Byte
        Dim pRowItemInfo As New ROW_ITEM_INFO
        Dim i As Byte
        Dim j As Byte
        Dim temp As UInt16
        For i = 0 To 3
            temp = &H1
            For j = 0 To 7
                If groupAddr(i) And temp Then
                    address = i * 8 + j

                    pRowItemInfo.index = index
                    pRowItemInfo.upgradeCheck = True
                    pRowItemInfo.address = address
                    pRowItemInfo.devId = ""
                    pRowItemInfo.runType = ""
                    pRowItemInfo.btnAllChip = "Read"
                    pRowItemInfo.btnEEP = "Read"

                    AddItem(pRowItemInfo)

                    index += 1


                    g_SelectCount += 1
                    UpdateCountInfo()

                End If
                temp *= 2
            Next
        Next
    End Sub

    Private Sub UpgradeDevice()
        '确认端口是打开的
        If Port.IsOpen = False Then MsgBox("Please open com port!") : Exit Sub

        Dim itemInfo As New ROW_ITEM_INFO
        Dim itemIndex As Integer = 0

        Dim tmpLineCnt As Int16 = 0

        SetStatus("", SystemColors.Control)
        '如果未选中设备’
        If g_SelectCount = 0 Then
            SetStatus("Please select the device which you want to upgrade!", Color.Red)
            Return
        End If

        '如果没有打开Hex文件’
        If Tb_HexFilePath.Text = "" Then
            SetStatus("Please open Hex file", Color.Red)
            Return
        End If

        '如果装载文件失败’
        '装载后hex数据放在 g_LoaderDataBuffer 数组中
        '最高地址放在 g_LoaderEndAddr 中
        '有效数据长度 = g_LoaderEndAddr
        If LoadDataFromFile(Tb_HexFilePath.Text) = False Then
            SetStatus("Error to upgrade", Color.Red)
            Return
        End If


        Dim checkSum(3) As Byte
        CalcCheckSum(g_LoaderDataBuffer, g_LoaderEndAddr, checkSum)

        g_SuccessCount = 0
        g_FailCount = 0
        UpdateCountInfo()

        For Each pRow As DataGridViewRow In DataGridView1.Rows
            pRow.Cells(COLS.STATUS).Value = ""
        Next

        '遍历表格中的每一行
        For Each pRow As DataGridViewRow In DataGridView1.Rows
            tmpLineCnt = tmpLineCnt + 1
            If Port.IsOpen = False Then MsgBox("Please open com port!") : Exit Sub

            '跳出升级
            If g_UpgradeFlag = False Then
                Exit For
            End If



            '如果该行勾选了升级’
            If pRow.Cells(COLS.UPGRADE).Value Then
                '取出该行数据到 itemInfo
                UpdateRowItemInfo(pRow, itemInfo)

                Dim succFlag As Boolean = False
                Dim devAddress As Integer = itemInfo.address

                showItemStatus(itemIndex, "Operation...", COLOR_MESSAGE)

                Dim jumpFlag As Boolean = False
                Dim devId As String
                Dim bootFlag As String
                Dim devModel As String = ""
                Dim readValid As Boolean = False

                g_UpgradeRetryCnt = 0 '重试次数

                RichTextBox1.AppendText(vbCrLf & "行号：" & tmpLineCnt & “开始握手!” & vbCrLf)

lab_Upgrade_StartHandshaking:
                SetStatus("DevAdd: " & devAddress & ", Handshaking...", COLOR_MESSAGE)
                '先用2400bps在Bootloader区搜索设备
                If Not Cbo_BusType.Text = "BLV_C1" Then
                    SetPortBaud(2400)       '设置波特率
                Else
                    SetPortBaud(HANDSHAKE_BAUD)       '设置波特率
                End If

                readValid = SearchDeviceCmd(devAddress, devId, bootFlag, devModel, g_maxRetry, 100)
                '如果在Boot区未搜索到设备，则在App区再搜索’
                If readValid = False Then
                    'App 搜索
                    SetPortBaud(Val(Cbo_Baud.Text))       '设置波特率
                    readValid = AllAppSearchDeviceCmd(devAddress, devId, bootFlag, devModel, g_maxRetry, 100)
                End If

                '握手成功
                If readValid Then
                    g_UpgradeRetryCnt = 0

                    RichTextBox1.AppendText(vbCrLf & "行号：" & tmpLineCnt & “握手成功!” & vbCrLf)
                    RichTextBox1.AppendText(vbCrLf & "行号：" & tmpLineCnt & “开始跳转到Bootloader!” & vbCrLf)

lab_Upgrade_JumpToBootLoader:
                    pRow.Cells(COLS.RUN_FLAG).Value = bootFlag
                    If pRow.Cells(COLS.RUN_FLAG).Value = "APP" Then
                        SetPortBaud(Val(Cbo_Baud.Text))       '设置波特率
                        SetStatus("DevAdd: " & devAddress & ", Jump to Bootloader", COLOR_MESSAGE)
                        If AllAppJumpCmd(devAddress, g_maxRetry, g_UpgradeTimerout) Then
                            Delay(1000)
                            If Not Cbo_BusType.Text = "BLV_C1" Then
                                SetPortBaud(2400)       '设置波特率
                            Else
                                SetPortBaud(HANDSHAKE_BAUD)       '设置波特率
                            End If
                            '设置波特率
                            If SearchDeviceCmd(devAddress, devId, bootFlag, devModel, g_maxRetry, g_UpgradeTimerout) Then
                                jumpFlag = True
                            End If
                        End If
                    Else
                        jumpFlag = True
                    End If

                    If jumpFlag Then
                        g_UpgradeRetryCnt = 0
                        RichTextBox1.AppendText(vbCrLf & "行号：" & tmpLineCnt & “跳转到Bootloader成功，开始更新参数!” & vbCrLf)
lab_Upgrade_SetParameter:

                        SetStatus("DevAdd: " & devAddress & ", Set Baudrate...", COLOR_MESSAGE)
                        If SetParamCmd(devAddress, Val(Cbo_Baud.Text), Val(Tb_Timeout.Text)) Then
                            g_UpgradeRetryCnt = 0
                            RichTextBox1.AppendText(vbCrLf & "行号：" & tmpLineCnt & “更新参数成功，开始在boot区握手!” & vbCrLf)
lab_Upgrade_HandshakInBootloader:

                            SetPortBaud(Val(Cbo_Baud.Text))
                            SetStatus("DevAdd: " & devAddress & ",Handshaking...", COLOR_MESSAGE)
                            If SearchDeviceCmd(devAddress, devId, bootFlag, devModel, g_maxRetry + 3, g_UpgradeTimerout) Then
                                g_UpgradeRetryCnt = 0
                                RichTextBox1.AppendText(vbCrLf & "行号：" & tmpLineCnt & “在boot区握手成功，开始写入Flash!” & vbCrLf)
lab_Upgrade_WriteFlash:

                                SetStatus("DevAdd: " & devAddress & ",Load Hex Data...", COLOR_MESSAGE)
                                SetStatus("DevAdd: " & devAddress & ",Upgrading...", COLOR_MESSAGE)
                                If DownloadData(devAddress) Then
                                    g_UpgradeRetryCnt = 0
                                    RichTextBox1.AppendText(vbCrLf & "行号：" & tmpLineCnt & “写入Flash成功，开始验证Flash！” & vbCrLf)
lab_Upgrade_VerfiyFlash:

                                    SetStatus("DevAdd: " & devAddress & ",Verifying...", COLOR_MESSAGE)
                                    'If CheckCmd(devAddress, g_maxRetry, g_UpgradeTimerout) Then
                                    If CheckCmd(devAddress, 0, 3000) Then
                                        g_UpgradeRetryCnt = 0
                                        RichTextBox1.AppendText(vbCrLf & "行号：" & tmpLineCnt & “验证Flash成功，跳转到App！” & vbCrLf)
lab_Upgrade_JumpToApp:

                                        SetStatus("DevAdd: " & devAddress & ",Reboot App...", COLOR_MESSAGE)
                                        If JumpToApp(devAddress, g_maxRetry, g_UpgradeTimerout) Then
                                            SetStatus("DevAdd: " & devAddress & ",Upgrade Completed!", COLOR_MESSAGE)
                                            showItemStatus(itemIndex, "Upgrade Complete", COLOR_NORMAL)
                                            succFlag = True
                                        Else
                                            If (g_UpgradeRetryCnt < g_UpgradeMaxRetry) Then
                                                g_UpgradeRetryCnt = g_UpgradeRetryCnt + 1
                                                GoTo lab_Upgrade_JumpToApp
                                            Else
                                                showItemStatus(itemIndex, "Jump to Bootloader fail", COLOR_ERROR)
                                            End If
                                        End If
                                    Else
                                        If (g_UpgradeRetryCnt < g_UpgradeMaxRetry) Then
                                            g_UpgradeRetryCnt = g_UpgradeRetryCnt + 1
                                            GoTo lab_Upgrade_VerfiyFlash
                                        Else
                                            showItemStatus(itemIndex, "Verify fail", COLOR_ERROR)
                                        End If
                                    End If
                                Else
                                    If (g_UpgradeRetryCnt < g_UpgradeMaxRetry) Then
                                        g_UpgradeRetryCnt = g_UpgradeRetryCnt + 1
                                        GoTo lab_Upgrade_WriteFlash
                                    Else
                                        showItemStatus(itemIndex, "Write Flash fail", COLOR_ERROR)
                                    End If
                                End If
                            Else
                                If (g_UpgradeRetryCnt < g_UpgradeMaxRetry) Then
                                    g_UpgradeRetryCnt = g_UpgradeRetryCnt + 1
                                    GoTo lab_Upgrade_HandshakInBootloader
                                Else
                                    showItemStatus(itemIndex, "Handshak in Bootloader fail", COLOR_ERROR)
                                End If
                            End If
                        Else
                            If (g_UpgradeRetryCnt < g_UpgradeMaxRetry) Then
                                g_UpgradeRetryCnt = g_UpgradeRetryCnt + 1
                                GoTo lab_Upgrade_SetParameter
                            Else
                                showItemStatus(itemIndex, "Set Parameter fail", COLOR_ERROR)
                            End If
                        End If
                    Else
                        If (g_UpgradeRetryCnt < g_UpgradeMaxRetry) Then
                            g_UpgradeRetryCnt = g_UpgradeRetryCnt + 1
                            GoTo lab_Upgrade_JumpToBootLoader
                        Else
                            showItemStatus(itemIndex, "Jump to Bootloader fail", COLOR_ERROR)
                        End If
                    End If
                Else
                    If (g_UpgradeRetryCnt < g_UpgradeMaxRetry) Then
                        g_UpgradeRetryCnt = g_UpgradeRetryCnt + 1
                        GoTo lab_Upgrade_StartHandshaking
                    Else
                        showItemStatus(itemIndex, "Handshak fail", COLOR_ERROR)
                    End If
                End If

                If succFlag Then
                    g_SuccessCount += 1
                Else
                    g_FailCount += 1
                End If
                UpdateCountInfo()

            End If
            itemIndex += 1
        Next
    End Sub

    Private Sub UpdateRowItemInfo(ByVal pRow As DataGridViewRow, ByRef itemInfo As ROW_ITEM_INFO)
        itemInfo.index = pRow.Cells(COLS.INDEX).Value
        itemInfo.upgradeCheck = pRow.Cells(COLS.UPGRADE).Value
        itemInfo.address = pRow.Cells(COLS.ADDR).Value
        itemInfo.devId = pRow.Cells(COLS.DEVID).Value
        itemInfo.runType = pRow.Cells(COLS.RUN_FLAG).Value
        itemInfo.status = pRow.Cells(COLS.STATUS).Value
        itemInfo.btnAllChip = pRow.Cells(COLS.READ_ALL_CHIP).Value
        itemInfo.btnEEP = pRow.Cells(COLS.READ_EEPROM).Value
    End Sub

    Private Function DownloadData(ByVal address As Byte) As Boolean
        Dim flashAddr As UInt32 = g_HexStartAdd_For_C1 '如果hex文件首行是type 04 数据则将此数据作为起始地址
        Dim flashData As UInt32 = g_HexStartAdd_For_C1
        Dim flashEndAddr As UInt32 = g_LoaderEndAddr    '结束地址

        Dim loaderNum As Byte           '下载进度
        Dim tmpPackageCnt As UInt16 = 0

        If Chk_BackupUpgrade.Checked = True Then        '如果备份程序选中，便将该程序抄录FLASH备份区中，数据地址应该偏移
            '备份区域偏移地址 - 0x040000

            flashAddr += g_BLVCxBackupAppOffsetAddr
            flashEndAddr += g_BLVCxBackupAppOffsetAddr
            RichTextBox1.AppendText(vbCrLf & “    --------- 开始写入备份数据！” & vbCrLf)
            Do
                If WriteFlashCmd(address, flashAddr, g_LoaderDataBuffer, flashData, g_maxRetry, g_UpgradeTimerout) Then
                    tmpPackageCnt = tmpPackageCnt + 1

                    RichTextBox1.AppendText(vbCrLf & “    --------- 写入备份Flash数据，包序号： ” & tmpPackageCnt & "写入地址：" & flashAddr & vbCrLf)

                    flashAddr += g_Download_DataLenght_Pre_Package
                    flashData += g_Download_DataLenght_Pre_Package
                    loaderNum = (flashAddr * 100) / flashEndAddr
                    If loaderNum > 100 Then loaderNum = 100
                    ToolStripProgressBar1.Value = loaderNum

                    If g_UpgradeFlag = False Then Return False
                Else
                    Return False
                End If
            Loop Until flashAddr >= flashEndAddr

        Else
            RichTextBox1.AppendText(vbCrLf & “    开始擦除Flash!” & vbCrLf)
            If EraseFlashCmd(address, g_maxRetry, ERASE_CMD_TIMEROUT) Then          '擦除Flash
                RichTextBox1.AppendText(vbCrLf & “    擦除Flash成功，开始写入数据！” & vbCrLf)
                Do

                    If WriteFlashCmd(address, flashAddr, g_LoaderDataBuffer, flashAddr, g_maxRetry, g_UpgradeTimerout) Then
                        tmpPackageCnt = tmpPackageCnt + 1

                        RichTextBox1.AppendText(vbCrLf & “    --------- 写入Flash数据，包序号： ” & tmpPackageCnt & vbCrLf)

                        flashAddr += g_Download_DataLenght_Pre_Package
                        loaderNum = (flashAddr * 100) / flashEndAddr
                        If loaderNum > 100 Then loaderNum = 100
                        ToolStripProgressBar1.Value = loaderNum

                        If g_UpgradeFlag = False Then Return False
                    Else
                        Return False
                    End If
                Loop Until flashAddr >= flashEndAddr
            Else
                RichTextBox1.AppendText(vbCrLf & “    擦除Flash失败！” & vbCrLf)
                Return False
            End If

        End If


        ToolStripProgressBar1.Value = 100
        Return True
    End Function

    Private Function CheckCmd(ByVal address As Byte, ByVal maxRetry As Integer, ByVal timeout As Integer) As Boolean
        Dim crcBuf(4096) As Byte
        Dim crcBufLen As UInt32

        Dim tmpResultIdx As UInt16 = 0

        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)

        '2022-05-07 曹聪 修改 数据包整包校验有误
        If g_DevType = DEV_TYPE.BLV_C1 Then

            If Chk_BackupUpgrade.Checked = True Then        '如果备份程序选中，便将该程序抄录FLASH备份区中，数据地址应该偏移
                Dim flashEndAddr As UInt32 = g_LoaderEndAddr

                flashEndAddr += g_BLVCxBackupAppOffsetAddr

                g_SendBuffer(g_ParamOffset) = (flashEndAddr >> 16) And &HFF
                g_SendBuffer(g_ParamOffset + 1) = (flashEndAddr >> 8) And &HFF
                g_SendBuffer(g_ParamOffset + 2) = flashEndAddr And &HFF

            Else
                g_SendBuffer(g_ParamOffset) = (g_LoaderEndAddr >> 16) And &HFF
                g_SendBuffer(g_ParamOffset + 1) = (g_LoaderEndAddr >> 8) And &HFF
                g_SendBuffer(g_ParamOffset + 2) = g_LoaderEndAddr And &HFF
            End If


            g_Download_ParamLenght_Pre_Package = g_Download_DataLenght_Pre_Package + 3 '往下发数据+地址总长度，C1的地址多一个byte
        Else
            g_SendBuffer(g_ParamOffset) = g_LoaderEndAddr \ 256
            g_SendBuffer(g_ParamOffset + 1) = g_LoaderEndAddr Mod 256

            g_Download_ParamLenght_Pre_Package = g_Download_DataLenght_Pre_Package + 2 '往下发数据+地址总长度，C1的地址多一个byte
        End If

        CalcCRC16(g_LoaderDataBuffer, g_LoaderEndAddr, crcBuf, crcBufLen)
        Array.Copy(crcBuf, 0, g_SendBuffer, g_ParamOffset + 3, crcBufLen)
        FillSendBuf(g_SendBuffer, CMD.LOADER_CMD_CHECK, crcBufLen + 3, address)

        If g_DevType = DEV_TYPE.BLV_C1 Then
            tmpResultIdx = FMT_C1.PARAM + 1
        Else
            tmpResultIdx = FMT.PARAM + 1
        End If


        Dim tmpLen As UInt16 = 0
        If g_DevType = DEV_TYPE.BLV_C1 Then
            tmpLen = g_SendBuffer(FMT_C1.LEN_H) * 256 + g_SendBuffer(FMT_C1.LEN_L)
        Else
            tmpLen = g_SendBuffer(FMT.LEN)
        End If

        If WriteToDevice(g_SendBuffer, tmpLen, g_RecvBuffer, g_RecvLen, g_GroupAddrFlag, maxRetry, timeout) Then
            If g_RecvBuffer(tmpResultIdx) = &H1 Then
                Return True
            Else
                RichTextBox1.AppendText(vbCrLf & "    xxxx CRC 校验错误！")
                Return False
            End If
        Else
            Return False
        End If
    End Function

    Private Sub CheckCmd()
        Dim crcBuf(128) As Byte
        Dim crcBufLen As UInt16
        Dim tmpLen As UInt16 = 0

        Array.Clear(g_SendBuffer, 0, g_SendBuffer.Length)
        Array.Clear(g_RecvBuffer, 0, g_RecvBuffer.Length)

        If g_DevType = DEV_TYPE.BLV_C1 Then

            If Chk_BackupUpgrade.Checked = True Then        '如果备份程序选中，便将该程序抄录FLASH备份区中，数据地址应该偏移
                Dim flashEndAddr As UInt32 = g_LoaderEndAddr

                flashEndAddr += g_BLVCxBackupAppOffsetAddr

                g_SendBuffer(g_ParamOffset) = (flashEndAddr >> 16) And &HFF
                g_SendBuffer(g_ParamOffset + 1) = (flashEndAddr >> 8) And &HFF
                g_SendBuffer(g_ParamOffset + 2) = flashEndAddr And &HFF

            Else
                g_SendBuffer(g_ParamOffset) = (g_LoaderEndAddr >> 16) And &HFF
                g_SendBuffer(g_ParamOffset + 1) = (g_LoaderEndAddr >> 8) And &HFF
                g_SendBuffer(g_ParamOffset + 2) = g_LoaderEndAddr And &HFF
            End If

            CalcCRC16(g_LoaderDataBuffer, g_LoaderEndAddr, crcBuf, crcBufLen)
            Array.Copy(crcBuf, 0, g_SendBuffer, g_ParamOffset + 3, crcBufLen)
            FillSendBuf(g_SendBuffer, CMD.LOADER_CMD_CHECK, crcBufLen + 2)

            tmpLen = g_SendBuffer(FMT_C1.LEN_H) * 256 + g_SendBuffer(FMT_C1.LEN_L)
        Else
            g_SendBuffer(g_ParamOffset) = g_LoaderEndAddr \ 256
            g_SendBuffer(g_ParamOffset + 1) = g_LoaderEndAddr Mod 256

            CalcCRC16(g_LoaderDataBuffer, g_LoaderEndAddr, crcBuf, crcBufLen)
            Array.Copy(crcBuf, 0, g_SendBuffer, g_ParamOffset + 2, crcBufLen)
            FillSendBuf(g_SendBuffer, CMD.LOADER_CMD_CHECK, crcBufLen + 2)

            tmpLen = g_SendBuffer(FMT.LEN)
        End If

        WriteToDevice(g_SendBuffer, tmpLen, g_RecvBuffer, g_RecvLen, g_GroupAddrFlag, g_maxRetry, g_UpgradeTimerout)
    End Sub

    Private Function DownloadData() As Boolean
        Dim flashAddr As UInt16 = 0
        Dim loaderNum As Byte           '下载进度
        Dim address As Byte
        Dim i As Byte

        For i = 0 To g_maxRetry
            EraseFlashCmd(address, 0, ERASE_CMD_TIMEROUT)              '擦除Flash
            Delay(ERASE_CMD_TIMEROUT)
        Next

        Do
            WriteFlashCmd(address, flashAddr, g_LoaderDataBuffer, flashAddr, g_maxRetry, g_UpgradeTimerout)
            flashAddr += 128

            loaderNum = (flashAddr * 100) / g_LoaderEndAddr
            If loaderNum > 100 Then loaderNum = 100
            ToolStripProgressBar1.Value = loaderNum

            If g_UpgradeFlag = False Then Return False

        Loop Until flashAddr >= g_LoaderEndAddr

        Return True
    End Function

    ''' <summary>
    ''' 获取文件的MD5值
    ''' 返回Byte数组值
    ''' </summary>
    ''' <param name="filepath">文件路径</param>
    ''' <returns></returns>
    Public Function GetMd5(filepath As String) As Byte()
        Dim FileStr As FileStream = New FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read)
        Dim data(FileStr.Length - 1) As Byte
        FileStr.Read(data, 0, data.Length)

        Dim md5s As Byte() = MD5.Create().ComputeHash(data)

        Console.WriteLine("Get MD5 File:" & BitConverter.ToString(md5s).Replace("-", " "))
        Return md5s
    End Function


    Private Function LoadDataFromFile(ByVal path As String) As Boolean
        Array.Clear(g_LoaderHexFileBuffer, 0, g_LoaderHexFileBuffer.Length)
        Dim fileLen As Int32 = 0

        Try
            Dim fs As New System.IO.FileStream(path, IO.FileMode.Open, IO.FileAccess.Read)
            If fs.Length <= LOADER_FILE_SIZE_MAX Then
                fileLen = fs.Length
                fs.Read(g_LoaderHexFileBuffer, 0, fs.Length)
                'Dim data(fs.Length - 1) As Byte

                'fs.Read(data, 0, data.Length)
                'Dim md5s As Byte() = MD5.Create().ComputeHash(data)
                'Console.WriteLine("MD5 File:" & BitConverter.ToString(md5s).Replace("-", " "))
            Else
                Return False
            End If
            fs.Close()
        Catch ex As Exception
            Return False
        End Try

        'GetMd5(path)

        If Chk_LogicFile.Checked = True Then
            Return LogicFileToData(g_LoaderHexFileBuffer, fileLen, g_LoaderDataBuffer, g_HexStartAdd_For_C1, g_LoaderEndAddr)
        Else
            '起始这函数中有重新复制写入的起始地址和结束地址，下发配置版本可以改这个函数直接将起始地址和结束修改了，其他都可以不变
            Return HexFileToData(g_LoaderHexFileBuffer, g_LoaderDataBuffer, g_LoaderEndAddr)
        End If

    End Function

    Private Function HexFileToData(ByVal hexBuffer() As Byte, ByVal dataBuffer() As Byte, ByRef endAddr As UInt32) As Boolean
        Dim i As UInt32

        '行数
        Dim LineCnt As UInt32 = 0
        Dim DataType_00_Idx As UInt32 = 0

        '清空数据缓存区域’
        For i = 0 To dataBuffer.Length - 1
            dataBuffer(i) = &HFF
        Next

        '本行数据缓存’
        Dim dataStrBuf(31) As Byte
        Dim dataValBuf(15) As Byte

        '首地址标记’
        Dim headAddrFlag As Boolean = True

        '本行Cks值’


        '本行地址值’
        Dim tmpLineAdd As Integer = 0

        '偏移地址 0x04 数据类型指示偏移地址
        Dim LineAddOffestBase As Integer = 0

        '结束地址’
        endAddr = 0

        Dim flashAddressBuf(3) As Byte
        Dim flashAddress As UInt32

        'Hex格式解析，每一行以0D 0A结束
        '文本串：:1000000000800020A500010061090100AF0001008F
        '分解为20 Bytes Hex数据
        '
        '--- | ----- | ---| ----------------------------------------------------------|----
        'Len |  ADD  |Type|                      Data                                 | CKS
        '--- | ----- | ---| ----------------------------------------------------------|----
        ' B0 | B1 B2 | B3 | B4 B5 B6 B7 B8 B9 B10 B11 B12 B13 B14 B15 B16 B17 B18 B19 | B20
        ':10 | 00 00 | 00 | 00 80 00 20 A5 00 01  00  61  09  01  00  AF  00  01  00  | 8F 
        ' ---|  -----| ---| ----------------------------------------------------------|----

        '1: 每行固定以“：”开头
        '2：B0       ：数据长度
        '3：B1~B2    ：数据地址 B1为地址高位，B2为地址低位
        '4：B3       ：数据类型
        '               00' Data Record                      //数据记录
        '               01' End of File Record               //文件结束记录
        '               02' Extended Segment Address Record  //扩展段地址记录
        '               03' Start Segment Address Record     //开始段地址记录
        '               04' Extended Linear Address Record   //扩展线性地址记录
        '               05' Start Linear Address Record      //开始线性地址记录
        '5：B4~B19   ：数据内容
        '6：B20      ：校验码
        '’'''''''''''’''
        'Hex 数据首地址确认方式：将第一个00类型的数据的地址作为起始地址
        '因此，最低地址必须出现在第一个00类型数据中

        For i = 0 To hexBuffer.Length - 1
            '首先找到冒号
            If hexBuffer(i) = &H3A Then   '&H3A = ":"  
                LineCnt += 1
                '----------------------------------------------------------------------------------------’
                'B0：数据长度
                '----------------------------------------------------------------------------------------’
                Dim dataLen As UInt32 = (StrToHex(hexBuffer(i + 1)) * 16 + StrToHex(hexBuffer(i + 2)))
                If dataLen > 0 Then
                    '----------------------------------------------------------------------------------------’
                    'By：CheckSum
                    '----------------------------------------------------------------------------------------’
                    Dim checkSum As Byte = (StrToHex(hexBuffer(i + 9 + dataLen * 2)) * 16 + StrToHex(hexBuffer(i + 10 + dataLen * 2)))

                    '取出整行数据
                    Dim rowDataStrBuf(41) As Byte
                    Dim rowDataValBuf(20) As Byte
                    Dim rowLen As UInt16
                    Array.Clear(rowDataStrBuf, 0, 42)
                    Array.Clear(rowDataValBuf, 0, 21)

                    rowLen = (dataLen * 2) + 10
                    Array.Copy(hexBuffer, i + 1, rowDataStrBuf, 0, rowLen)

                    StrToHex(rowDataValBuf, rowDataStrBuf, rowLen / 2)

                    Dim tempCheckSum As Byte = CalcCheckSum(rowDataValBuf, rowLen / 2)

                    If tempCheckSum <> &HFF Then
                        Return False 'CheckSum校验不通过，返回错误
                    End If

                    '----------------------------------------------------------------------------------------’
                    'B4~Bx：数据内容
                    '----------------------------------------------------------------------------------------’
                    Array.Clear(dataStrBuf, 0, 32)
                    Array.Clear(dataValBuf, 0, 16)
                    Array.Copy(hexBuffer, i + 9, dataStrBuf, 0, dataLen * 2)
                    StrToHex(dataValBuf, dataStrBuf, dataLen)

                End If

                '----------------------------------------------------------------------------------------’
                'B1~B2：取出地址位的值
                '----------------------------------------------------------------------------------------’
                Array.Copy(hexBuffer, i + 3, flashAddressBuf, 0, 4)

                '本行地址取出来放在 flashAddress中’
                StrToHex(tmpLineAdd, flashAddressBuf)
                flashAddress = LineAddOffestBase + tmpLineAdd

                ''第一行取得的地址必须是0，否则就返回错误’
                'If headAddrFlag Then
                '    headAddrFlag = False
                '    If flashAddress <> 0 Then Return False
                'End If

                '----------------------------------------------------------------------------------------’
                'B3：数据类型
                '----------------------------------------------------------------------------------------’
                Dim dataType = StrToHex(hexBuffer(i + 8))
                Select Case dataType
                    Case 0 '数据记录
                        DataType_00_Idx = DataType_00_Idx + 1
                        '将第一个00类型数据的地址作为起始地址， 保存到  g_HexStartAdd_For_C1
                        If DataType_00_Idx = 1 Then
                            g_HexStartAdd_For_C1 = flashAddress
                        End If

                        Array.Copy(dataValBuf, 0, dataBuffer, flashAddress, dataLen)
                        endAddr = flashAddress + dataLen
                        Console.WriteLine("包：" & DataType_00_Idx.ToString & "地址：" & flashAddress &"  长度：" & dataLen)
                    Case 1 '文件结束
                        If endAddr = 0 Then
                            Return False
                        Else
                            'MsgBox(“数据装载成功，行数=” & LineCnt & “ ， EndAdd = ” & endAddr)
                            Return True
                        End If

                    Case 2 '扩展段地址’
                        'MsgBox(“数据装载异常，第 " & LineCnt & " 行数据类型为 02，代码当前未对此类数据做处理！")
                        Continue For

                    Case 3 '开始段地址’
                        'MsgBox(“数据装载异常，第 " & LineCnt & " 行数据类型为 03，代码当前未对此类数据做处理！")
                        Continue For

                    Case 4 '扩展线性地址’
                        If dataLen = 2 Then
                            LineAddOffestBase = (dataValBuf(0) * 256 + dataValBuf(1)) * 65536
                        Else
                            Return False  '确认DataLen必须是2，否则报错’
                        End If

                    Case 5 '开始线性地址’
                        'MsgBox(“数据装载异常，第 " & LineCnt & " 行数据类型为 05，代码当前未对此类数据做处理！")
                        Continue For

                    Case Else
                        Return False
                End Select


                'If headAddrFlag Then
                '    headAddrFlag = False
                '    If flashAddress <> 0 Then Return False
                'End If

            End If
        Next
        Return False
    End Function

    ''' <summary>
    ''' 逻辑文件转数据
    ''' </summary>
    ''' <param name="logicBuffer">文件缓存</param>
    ''' <param name="dataBuffer">数据</param>
    ''' <param name="endAddr"></param>
    ''' <returns> False:文件打开失败、True:文件打开成功</returns>
    Private Function LogicFileToData(ByVal logicBuffer() As Byte, fileLen As Int32, ByVal dataBuffer() As Byte, ByRef startAddr As UInt32, ByRef endAddr As UInt32) As Boolean
        '逻辑文件地址 0x090000~0x0FFFFF 文件最大为0x6FFFF - 458751Byte
        '#define LOGIC_DataFlag						0xCC060001	//逻辑标志
        '#define FLASH_LOGIC_FILE_ADDRESS			0x090000
        '#define FLASH_LOGIC_DataFlag_ADDRESS		0x090000	//文件标志位 - 4Byte			
        '#define FLASH_LOGIC_DataSize_ADDRESS		0x090004	//文件长度 - 4Byte
        '#define FLASH_LOGIC_DataMD5_ADDRESS		0x090008	//文件MD5校验值 - 16Byte

        '#define FLASH_LOGIC_DataStart_ADDRESS		0x090200    //数据存放起始地址
        '#define FLASH_LOGIC_DataEnd_ADDRESS		0x0FFFFF    //数据存放结束地址

        '将内容填充正确

        'g_HexStartAdd_For_C1   - 文件写入起始地址
        'endAddr                - 文件写入结束地址

        Dim flashAddress As Int32 = &H90000
        Dim logicDataLen As Int32 = fileLen

        If logicDataLen > 458751 Then
            Return False
        End If

        g_HexStartAdd_For_C1 = flashAddress

        Dim temp_buff(28) As Byte

        temp_buff(0) = &H1
        temp_buff(1) = &H0
        temp_buff(2) = &H6
        temp_buff(3) = &HCC

        temp_buff(4) = logicDataLen And &HFF
        temp_buff(5) = (logicDataLen >> 8) And &HFF
        temp_buff(6) = (logicDataLen >> 16) And &HFF
        temp_buff(7) = (logicDataLen >> 24) And &HFF
        Array.Copy(temp_buff, 0, dataBuffer, flashAddress, 8)
        flashAddress += 8
        Console.WriteLine($"数据长度：{logicDataLen} - " & Hex(logicDataLen) & " BUFF:" & BitConverter.ToString(temp_buff).Replace("-", " "))

        temp_buff = MD5.Create.ComputeHash(logicBuffer, 0, logicDataLen)
        Array.Copy(temp_buff, 0, dataBuffer, flashAddress, 16)
        Console.WriteLine("MD5:" & BitConverter.ToString(temp_buff).Replace("-", " "))

        flashAddress = &H90200
        Array.Copy(logicBuffer, 0, dataBuffer, flashAddress, logicDataLen)

        endAddr = flashAddress + logicDataLen

        Return True
    End Function

    Private Function toupper(val As Byte) As Byte
        If val >= &H61 AndAlso val <= &H7A Then
            Return (val - &H20)
        Else
            Return val
        End If
    End Function

    Private Sub StrToHex(pbDest() As Byte, pbSrc() As Byte, nLen As UInt16)
        Dim h1 As Byte
        Dim h2 As Byte
        Dim s1 As Byte
        Dim s2 As Byte
        Dim i As UInt16

        For i = 0 To nLen - 1
            h1 = pbSrc(2 * i)
            h2 = pbSrc(2 * i + 1)

            s1 = toupper(h1) - &H30
            If s1 > 9 Then
                s1 -= 7
            End If

            s2 = toupper(h2) - &H30
            If s2 > 9 Then
                s2 -= 7
            End If

            pbDest(i) = s1 * 16 + s2
        Next
    End Sub

    Private Sub StrToHex(ByRef pbDest As UInt16, pbSrc() As Byte)
        Dim pBufDest(1) As Byte
        Dim h1 As Byte
        Dim h2 As Byte
        Dim s1 As Byte
        Dim s2 As Byte
        Dim i As UInt16

        For i = 0 To 2 - 1
            h1 = pbSrc(2 * i)
            h2 = pbSrc(2 * i + 1)

            s1 = toupper(h1) - &H30
            If s1 > 9 Then
                s1 -= 7
            End If

            s2 = toupper(h2) - &H30
            If s2 > 9 Then
                s2 -= 7
            End If

            pBufDest(i) = s1 * 16 + s2
        Next

        'pbDest = (pBufDest(0) << 8) + pBufDest(1)
        pbDest = pBufDest(0) * 256 + pBufDest(1)
    End Sub

    Private Function StrToHex(ByVal src) As Byte
        If src >= &H30 AndAlso src <= &H39 Then
            Return (src - &H30)
        ElseIf src >= &H41 AndAlso src <= &H46 Then
            Return (src - &H41 + 10)
        ElseIf src >= &H61 AndAlso src <= &H66 Then
            Return (src - &H61 + 10)
        Else
            Return 0
        End If
    End Function

    Private Sub showItemStatus(ByVal rowIndex As Integer, ByVal status As String, ByVal clr As Color)
        DataGridView1.Rows.Item(rowIndex).Cells(COLS.STATUS).Value = status
        DataGridView1.Rows.Item(rowIndex).Cells(COLS.STATUS).Style.ForeColor = clr
    End Sub

    Private Sub showItemStatus(ByVal status As String, ByVal clr As Color)
        For Each pRow As DataGridViewRow In DataGridView1.Rows
            pRow.Cells(COLS.STATUS).Value = status
            pRow.Cells(COLS.STATUS).Style.ForeColor = clr
        Next
    End Sub
    Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click

        If Chk_LogicFile.Checked = True Then
            '选中当前是下发逻辑配置文件
            Dim dlg As New OpenFileDialog
            dlg.InitialDirectory = Application.StartupPath
            dlg.Filter = "Logic File|*.dat;*.ihex"
            Dim oldPath As String = Tb_HexFilePath.Text
            If System.IO.File.Exists(oldPath) Then
                dlg.InitialDirectory = System.IO.Path.GetDirectoryName(oldPath)
                dlg.FileName = oldPath
            Else
                dlg.InitialDirectory = Application.StartupPath
            End If

            If dlg.ShowDialog() = Windows.Forms.DialogResult.OK Then
                Tb_HexFilePath.Text = dlg.FileName
                btn_Reload.PerformClick()
            End If
        Else
            Dim dlg As New OpenFileDialog
            dlg.InitialDirectory = Application.StartupPath
            dlg.Filter = "HEX File|*.hex;*.ihex"
            Dim oldPath As String = Tb_HexFilePath.Text
            If System.IO.File.Exists(oldPath) Then
                dlg.InitialDirectory = System.IO.Path.GetDirectoryName(oldPath)
                dlg.FileName = oldPath
            Else
                dlg.InitialDirectory = Application.StartupPath
            End If

            If dlg.ShowDialog() = Windows.Forms.DialogResult.OK Then
                Tb_HexFilePath.Text = dlg.FileName
                btn_Reload.PerformClick()
            End If
        End If



        'lab_HexLastModifyDate.Text = IO.File.GetLastWriteTime(Tb_HexFilePath.Text)

    End Sub

    Private Sub Chk_UpgradeAllSel_CheckedChanged(sender As Object, e As EventArgs) Handles Chk_UpgradeAllSel.CheckedChanged
        If g_GroupAddrFlag = False Then
            If Chk_UpgradeAllSel.Checked Then
                For Each pRow As DataGridViewRow In DataGridView1.Rows
                    pRow.Cells.Item(COLS.UPGRADE).Value = True
                Next
                g_SelectCount = DataGridView1.Rows.Count
            Else
                For Each pRow As DataGridViewRow In DataGridView1.Rows
                    pRow.Cells.Item(COLS.UPGRADE).Value = False
                Next
                g_SelectCount = 0
            End If

            UpdateCountInfo()
        End If
    End Sub

    Private Sub DataGridView1_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick
        Try


            If e.ColumnIndex = COLS.READ_ALL_CHIP Then
                ShowReadFlash(32 * 1024)
            ElseIf e.ColumnIndex = COLS.READ_APP Then
                ShowReadFlash(BOOT_START_ADDR)
            ElseIf e.ColumnIndex = COLS.READ_EEPROM Then
                ShowReadEEPROM()
            ElseIf e.ColumnIndex = COLS.UPGRADE Then
                If DataGridView1.Rows(e.RowIndex).Cells(COLS.UPGRADE).EditedFormattedValue Then
                    g_SelectCount += 1
                Else
                    If g_SelectCount > 0 Then
                        g_SelectCount -= 1
                    End If
                End If
                UpdateCountInfo()
            End If
        Catch ex As Exception

        End Try
    End Sub

    Private Sub ShowReadEEPROM()
        Dim readFlashDlg As New ReadFlash
        Dim rowIndex As UInt16 = DataGridView1.CurrentCell.RowIndex
        Dim address As Byte = DataGridView1.Rows(rowIndex).Cells(COLS.ADDR).Value
        Dim devId As String
        Dim bootFlag As String
        Dim devModel As String

        If g_ReadEepBtnPressCnt = 0 Then
            g_ReadEepBtnPressCnt = 1

            DataGridView1.Rows(rowIndex).Cells(COLS.READ_EEPROM).Value = "正在同步"
            While g_ReadEepBtnPressCnt = 1
                If Not Cbo_BusType.Text = "BLV_C1" Then
                    SetPortBaud(2400)       '设置波特率
                Else
                    SetPortBaud(HANDSHAKE_BAUD)       '设置波特率
                End If
                RichTextBox_Show("发送Boot搜索命令", COLOR_ERROR)
                If SearchDeviceCmd(address, devId, bootFlag, devModel, 0, SEARCH_TIMEROUT) Then
                    DataGridView1.Rows(rowIndex).Cells(COLS.READ_EEPROM).Value = "BOOT区"
                End If
                SetPortBaud(Val(Cbo_Baud.Text))
                RichTextBox_Show("发送APP跳转命令", COLOR_ERROR)
                AllAppJumpCmd(address, 0, SEARCH_TIMEROUT)
                Application.DoEvents()
            End While
        ElseIf g_ReadEepBtnPressCnt = 1 Then
            g_ReadEepBtnPressCnt = 2

            If DataGridView1.Rows(rowIndex).Cells(COLS.READ_EEPROM).Value = "BOOT区" Then
                DataGridView1.Rows(rowIndex).Cells(COLS.READ_EEPROM).Value = "正在读取"
                readFlashDlg.Show()
                SetStatus(readFlashDlg.ToolStripStatusLabel1, "读取EEPROM：正在读取地址" & address， COLOR_MESSAGE)

                If Not Cbo_BusType.Text = "BLV_C1" Then
                    SetPortBaud(2400)       '设置波特率
                Else
                    SetPortBaud(HANDSHAKE_BAUD)       '设置波特率
                End If
                If SetParamCmd(address, Val(Cbo_Baud.Text), Val(Tb_Timeout.Text)) Then
                    SetPortBaud(Val(Cbo_Baud.Text))
                    If SearchDeviceCmd(address, devId, bootFlag, devModel, g_maxRetry, g_UpgradeTimerout) Then
                        If ReadEeprom(readFlashDlg, address) Then
                            SetStatus(readFlashDlg.ToolStripStatusLabel1, "地址" & address & "读取完成"， COLOR_MESSAGE)
                            g_ReadBtnPressCnt = 0
                            DataGridView1.Rows(rowIndex).Cells(COLS.READ_EEPROM).Value = "读取"
                        End If
                    End If
                End If
            Else
                SetStatus("还没有跳到BOOT区，读取失败"， COLOR_ERROR)
            End If
        ElseIf g_ReadEepBtnPressCnt = 2 Then
            g_ReadEepBtnPressCnt = 0
        End If
    End Sub
    'ColumnIndex
    Private Sub ShowReadFlash(ByVal endAddr As UInt32)
        Dim readFlashDlg As New ReadFlash
        Dim rowIndex As UInt16 = DataGridView1.CurrentCell.RowIndex                 '行号
        Dim columnIndex As UInt16 = DataGridView1.CurrentCell.ColumnIndex           '列号
        Dim address As Byte = DataGridView1.Rows(rowIndex).Cells(COLS.ADDR).Value
        Dim devId As String = ""
        Dim bootFlag As String = ""
        Dim devModel As String = ""


        If g_ReadBtnPressCnt = 0 Then
            g_ReadBtnPressCnt = 1
            DataGridView1.Rows(rowIndex).Cells(columnIndex).Value = "正在同步"
            While g_ReadBtnPressCnt = 1
                If Not Cbo_BusType.Text = "BLV_C1" Then
                    SetPortBaud(2400)       '设置波特率
                Else
                    SetPortBaud(HANDSHAKE_BAUD)       '设置波特率
                End If
                RichTextBox_Show("发送Boot搜索命令", COLOR_ERROR)
                If SearchDeviceCmd(address, devId, bootFlag, devModel, 0, SEARCH_TIMEROUT) Then
                    DataGridView1.Rows(rowIndex).Cells(columnIndex).Value = "BOOT区"
                End If
                SetPortBaud(Val(Cbo_Baud.Text))
                RichTextBox_Show("发送APP跳转命令", COLOR_ERROR)
                AllAppJumpCmd(address, 0, SEARCH_TIMEROUT)
                Application.DoEvents()
            End While
        ElseIf g_ReadBtnPressCnt = 1 Then
            g_ReadBtnPressCnt = 2

            If DataGridView1.Rows(rowIndex).Cells(columnIndex).Value = "BOOT区" Then
                DataGridView1.Rows(rowIndex).Cells(columnIndex).Value = "正在读取"

                readFlashDlg.Show()
                SetStatus(readFlashDlg.ToolStripStatusLabel1, "读取FLASH：正在读取地址" & address， COLOR_MESSAGE)

                If Not Cbo_BusType.Text = "BLV_C1" Then
                    SetPortBaud(2400)       '设置波特率
                Else
                    SetPortBaud(HANDSHAKE_BAUD)       '设置波特率
                End If
                If SetParamCmd(address, Val(Cbo_Baud.Text), Val(Tb_Timeout.Text)) Then
                    SetPortBaud(Val(Cbo_Baud.Text))
                    If SearchDeviceCmd(address, devId, bootFlag, devModel, g_maxRetry, g_UpgradeTimerout) Then
                        If ReadFlash(readFlashDlg, address, endAddr) Then
                            SetStatus(readFlashDlg.ToolStripStatusLabel1, "地址" & address & "读取完成"， COLOR_MESSAGE)
                            g_ReadBtnPressCnt = 0
                            DataGridView1.Rows(rowIndex).Cells(columnIndex).Value = "读取"
                        End If
                    End If
                End If
            Else
                SetStatus("还没有跳到BOOT区，读取失败"， COLOR_ERROR)
                g_ReadBtnPressCnt = 0
                DataGridView1.Rows(rowIndex).Cells(columnIndex).Value = "读取"
            End If
        ElseIf g_ReadBtnPressCnt = 2 Then
            g_ReadBtnPressCnt = 0
            DataGridView1.Rows(rowIndex).Cells(columnIndex).Value = "读取"
        End If
    End Sub

    Private Sub RichTextBox_AddTick()
        Dim str As String = ""
        Dim sp As TimeSpan
        Try
            str = "["
            str += $"{Now.Year }{Now.Month.ToString.PadLeft(2, "0")}{Now.Day.ToString.PadLeft(2, "0")}{Now.Hour.ToString.PadLeft(2, "0")}{Now.Minute.ToString.PadLeft(2, "0")}{Now.Second.ToString.PadLeft(2, "0")}{Now.Millisecond.ToString.PadLeft(3, "0")}"
            str += "] ["
            sp = (Now - g_RichTextBoxLastTick)
            str += sp.TotalMilliseconds.ToString.PadLeft(5, " ")
            str += "]"
            RichTextBox1.AppendText(str)
            g_RichTextBoxLastTick = Now
        Catch ex As Exception

        End Try

    End Sub

    Public Sub RichTextBox_Show(ByVal str As String, ByVal cor As Color)
        Dim tempSelStart As Integer
        Dim tempSelLenght As Integer
        Try
            RichTextBox_AddTick()

            tempSelStart = RichTextBox1.TextLength
            RichTextBox1.AppendText(str)
            tempSelLenght = RichTextBox1.TextLength - tempSelStart
            RichTextBox1.Select(tempSelStart, tempSelLenght)        '选中文本
            RichTextBox1.SelectionColor = cor                       '当前选中的文本显示颜色
            RichTextBox1.AppendText(vbNewLine)
            RichTextBox1.ScrollToCaret()      '将内容滚动的作用
        Catch ex As Exception

        End Try

    End Sub

    Public Sub RichTextBox_Show(ByVal str1 As String, ByVal buf() As Byte, ByVal len As UInt16, ByVal cor As Color)
        Dim tempSelStart As Integer
        Dim tempSelLenght As Integer
        Dim i As UInt16
        Dim str As String = ""

        Try
            RichTextBox_AddTick()

            tempSelStart = RichTextBox1.TextLength
            RichTextBox1.AppendText(str1)
            RichTextBox1.AppendText("--")
            If len > 0 Then
                For i = 0 To len - 1
                    str += Hex(buf(i)).PadLeft(2, "0")
                    str += " "
                Next
            End If
            RichTextBox1.AppendText(str)
            tempSelLenght = RichTextBox1.TextLength - tempSelStart
            RichTextBox1.Select(tempSelStart, tempSelLenght)        '选中文本
            RichTextBox1.SelectionColor = cor                       '当前选中的文本显示颜色
            RichTextBox1.AppendText(vbNewLine)
            RichTextBox1.ScrollToCaret()      '将内容滚动的作用
        Catch ex As Exception

        End Try

    End Sub

    Private Sub LinkLabel1_LinkClicked(sender As Object, e As LinkLabelLinkClickedEventArgs)
        If Tb_HexFilePath.Text = "" Then
            MsgBox("Please select a file!")
            Return
        End If

        Dim readFlashDlg As New ReadFlash

        readFlashDlg.Button1.Visible = False
        readFlashDlg.Show()
        ShowHexFileData(readFlashDlg.DataGridView1, Tb_HexFilePath.Text)
    End Sub

    Private Sub ShowHexFileData(showDataGridView1 As DataGridView, ByVal path As String)
        Dim i As UInt32
        Dim dataLen As UInt32
        Dim flashAddressBuf(3) As Byte
        Dim flashAddress As UInt16
        Dim dataType As Byte
        Dim dataStrBuf(31) As Byte
        Dim dataValBuf(15) As Byte
        Dim checkSum As Byte
        Dim rowsNum As UInt16
        Dim j As UInt16
        Dim k As UInt16
        Dim h As UInt16
        Dim rowDataStrBuf(41) As Byte
        Dim rowDataValBuf(20) As Byte
        Dim rowLen As UInt16
        Dim hexBuf(1000 * 1024 - 1) As Byte
        Dim hexLen As UInt32
        Dim asciiDataValBuf(15) As Byte


        showDataGridView1.Rows.Clear()              '清除表格

        Try
            Dim fs As New System.IO.FileStream(path, IO.FileMode.Open, IO.FileAccess.Read)
            If fs.Length <= hexBuf.Length Then
                fs.Read(hexBuf, 0, fs.Length)
                hexLen = fs.Length
            End If
            fs.Close()
        Catch ex As Exception
            MsgBox("文件读取失败，可能的原因" & vbNewLine & ex.Message)
        End Try

        For i = 0 To hexLen
            If hexBuf(i) = &H3A Then
                dataLen = (StrToHex(hexBuf(i + 1)) * 16 + StrToHex(hexBuf(i + 2)))
                If dataLen = 0 Then
                    Continue For         '结束当次循环
                End If
                Array.Copy(hexBuf, i + 3, flashAddressBuf, 0, 4)
                StrToHex(flashAddress, flashAddressBuf)
                dataType = StrToHex(hexBuf(i + 8))
                Array.Clear(dataStrBuf, 0, 32)
                Array.Clear(dataValBuf, 0, 16)
                Array.Copy(hexBuf, i + 9, dataStrBuf, 0, dataLen * 2)
                StrToHex(dataValBuf, dataStrBuf, dataLen)
                checkSum = (StrToHex(hexBuf(i + 9 + dataLen * 2)) * 16 + StrToHex(hexBuf(i + 10 + dataLen * 2)))

                Array.Clear(rowDataStrBuf, 0, 42)
                Array.Clear(rowDataValBuf, 0, 21)
                rowLen = (dataLen * 2) + 10                  '一整行的长度
                Array.Copy(hexBuf, i + 1, rowDataStrBuf, 0, rowLen)
                StrToHex(rowDataValBuf, rowDataStrBuf, rowLen / 2)

                If HexCalcCheckSum(rowDataValBuf, rowLen / 2) = 0 Then         '计算校验和
                    If dataType <> 0 Then
                        Exit Sub
                    Else
                        rowsNum = flashAddress / 16         '得到行号
                        If rowsNum = showDataGridView1.Rows.Count Then
                            showDataGridView1.Rows.Add()
                            showDataGridView1.Rows(rowsNum).HeaderCell.Value = Hex(flashAddress).PadLeft(4, "0")
                            For k = 0 To 15
                                showDataGridView1.Rows(rowsNum).Cells(k).Value = "--"
                            Next
                        ElseIf rowsNum > showDataGridView1.Rows.Count Then
                            For h = 0 To rowsNum - DataGridView1.Rows.Count
                                showDataGridView1.Rows.Add()
                                showDataGridView1.Rows(DataGridView1.Rows.Count - 1).HeaderCell.Value = Hex((DataGridView1.Rows.Count - 1) * 16).PadLeft(4, "0")      ' + "  " + (DataGridView1.Rows.Count - 1).ToString
                                For k = 0 To 15
                                    showDataGridView1.Rows(DataGridView1.Rows.Count - 1).Cells(k).Value = "--"
                                Next
                            Next
                        End If

                        For j = 0 To dataLen - 1
                            showDataGridView1.Rows(rowsNum).Cells(j).Value = Hex(dataValBuf(j)).PadLeft(2, "0")
                        Next


                        Array.Copy(dataValBuf, 0, asciiDataValBuf, 0, dataLen)
                        For j = 0 To dataLen - 1
                            If asciiDataValBuf(j) < &H20 OrElse asciiDataValBuf(j) > &H7E Then
                                asciiDataValBuf(j) = &H2E
                            End If
                        Next
                        showDataGridView1.Rows(rowsNum).Cells(16).Value = System.Text.Encoding.ASCII.GetString(asciiDataValBuf)

                    End If
                Else
                    MsgBox("文件的校验和不对")
                End If
            End If
        Next

    End Sub

    Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
        SEARCH_TIMEROUT = Val(TextBox1.Text)
    End Sub

    Private Sub Cbo_BusType_SelectedIndexChanged(sender As Object, e As EventArgs) Handles Cbo_BusType.SelectedIndexChanged
        UpdateDevType()
    End Sub

    Private Sub TextBox2_TextChanged(sender As Object, e As EventArgs) Handles TextBox2.TextChanged
        g_UpgradeTimerout = Val(TextBox2.Text)
    End Sub

    Private Sub NumericUpDown3_ValueChanged(sender As Object, e As EventArgs) Handles NumericUpDown3.ValueChanged
        g_maxRetry = NumericUpDown3.Value
    End Sub

    Private Sub Btn_Help_Click(sender As Object, e As EventArgs)
        Dim dlg As New Help
        dlg.ShowDialog()
    End Sub

    Private Sub btn_Reload_Click(sender As Object, e As EventArgs) Handles btn_Reload.Click
        Dim tmpRet As Boolean = LoadDataFromFile(Tb_HexFilePath.Text)
        Dim checkSum(3) As Byte
        Dim tmpStrCks As String = ""

        Dim tmpIdx As UInt32 = 0
        Dim tmpLineCnt As UInt32 = 1
        Dim tmpStrBuffer As String = ""

        Dim tmpHexValidLenght As UInt32 = g_LoaderEndAddr - g_HexStartAdd_For_C1

        If tmpRet Then

            MsgBox("Hex 装载成功！" & vbCrLf &
                   "起始偏移地址 ：" & g_HexStartAdd_For_C1 & vbCrLf &
                   "    结束地址 ：" & g_LoaderEndAddr & vbCrLf &
                   "    文件长度 ：" & tmpHexValidLenght & vbCrLf
                   )
            'CalcCRC16
            CalcCheckSum(g_LoaderDataBuffer, g_LoaderEndAddr, checkSum)
            tmpStrCks = HexByte2Str(checkSum(3))
            tmpStrCks = tmpStrCks & " " & HexByte2Str(checkSum(2))
            tmpStrCks = tmpStrCks & " " & HexByte2Str(checkSum(1))
            tmpStrCks = tmpStrCks & " " & HexByte2Str(checkSum(0))

            lab_HexLastModifyDate.Text = IO.File.GetLastWriteTime(Tb_HexFilePath.Text)
            lab_HexDataLenght.Text = Format(tmpHexValidLenght, "###,###") & " Bytes"
            lab_HexDataCks.Text = tmpStrCks
            Me.Text = "Massduino Nano485 Loader(" & Application.ProductVersion & ")" & " - " & Tb_HexFilePath.Text
        Else
            lab_HexDataLenght.Text = ""
            lab_HexDataCks.Text = ""
        End If


    End Sub

    Private Function HexByte2Str(hexByte As Byte) As String
        If hexByte < 16 Then
            Return "0" & Hex(hexByte)
        Else
            Return Hex(hexByte)
        End If
    End Function

    Private Sub Form1_Resize(sender As Object, e As EventArgs) Handles Me.Resize
        'Me.Width = 720

    End Sub

    Private Sub RichTextBox1_TextChanged(sender As Object, e As EventArgs)
        If RichTextBox1.TextLength >= 65535 Then RichTextBox1.Clear()
    End Sub

    Private Sub Form1_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
        'V3.0 2020-10-3 Momno:在搜索和升级时关闭窗口，先关闭搜索和升级进程并关闭串口，避免报错
        g_SearchStart = False
        g_UpgradeFlag = False
        If Port.IsOpen = True Then Port.Close()

        'My.Settings.Reload()
        My.Settings.chk_UpgradeAllSelect = Chk_UpgradeAllSel.Checked
        My.Settings.CommPortNumber = Cbo_Port.Text
        My.Settings.CustomeProtocol_JumpToBootloader = tb_CustomeProtocol_JumpToBl.Text
        My.Settings.CustomeProtocol_Serach = tb_CustomeProtocol_Serach.Text
        My.Settings.HexFilePatch = Tb_HexFilePath.Text
        My.Settings.SerachAddStart = num_AddrStart.Value
        My.Settings.SerachAddEnd = num_AddrEnd.Value
        My.Settings.SerachTimeout = TextBox1.Text
        My.Settings.UpgradeTimeout = TextBox2.Text
        My.Settings.BootloaderTimeout = Tb_Timeout.Text
        My.Settings.CommBaudRate = Cbo_Baud.Text
        My.Settings.Opt_ProtocolMode = Opt_ProtocolMode
        My.Settings.BusType = Cbo_BusType.Text
        'My.Settings.C1PackageLenght = tb_C1PackageLen.Text
        My.Settings.Windows_High = Me.Height
        My.Settings.Save()
    End Sub

    Private Sub opt_ProtocolHexMode_CheckedChanged(sender As Object, e As EventArgs) Handles opt_ProtocolHexMode.CheckedChanged
        Opt_ProtocolMode = OPT_PROTOCOL_MODE.HEX_MODE
    End Sub

    Private Sub opt_ProtocolTextMode_CheckedChanged(sender As Object, e As EventArgs) Handles opt_ProtocolTextMode.CheckedChanged
        Opt_ProtocolMode = OPT_PROTOCOL_MODE.TEXT_MODE
    End Sub

    Private Sub Chk_BackupUpgrade_CheckedChanged(sender As Object, e As EventArgs) Handles Chk_BackupUpgrade.CheckedChanged
        If Chk_BackupUpgrade.Checked = True Then
            Chk_LogicFile.Enabled = False
        Else
            Chk_LogicFile.Enabled = True
        End If

        Tb_HexFilePath.Text = Nothing   '重新选择文件
    End Sub

    Private Sub Chk_LogicFile_CheckedChanged(sender As Object, e As EventArgs) Handles Chk_LogicFile.CheckedChanged
        If Chk_LogicFile.Checked = True Then
            Chk_BackupUpgrade.Enabled = False
        Else
            Chk_BackupUpgrade.Enabled = True
        End If

        Tb_HexFilePath.Text = Nothing   '重新选择文件
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        Dim nstrbuf As String = "0C 21 00 00 DE 32 00 00 CE 32 00 00 84 21 00 00 D6 32 00 00 94 32 00 00 84 21 00 00 C6 32 00 00 BE 32 00 00 84 21 00 00 84 21 00 00 84 21 00 00 84 21 00 00 84 21 00 00 84 21 00 00 84 21 00 00 B6 32 00 00 AE 32 00 00 A6 32 00 00 9E 32 00 00 84 21 00 00 84 21 00 00 84 21 00 00 84 21 00 00 84 21 00 00 84 21 00 00 84 21 00 00 84 21 00 00 84 21 00 00 84 21 00 00 84 21 00 00 96 32 00 00 64 41 00 00 68 29 00 00 60 2A 00 00 C8 2A 00 00 30 2B 00 00 84 21 00 00 D8 2C 00 00 78 30 00 00 A8 30 00 00 0C 2D 00 00 84 21 00 00 84 21 00 00 8C 2D 00 00 FC 2D 00 00 50 2E 00 00 B8 2E 00 00 84 21 00 00 EE 32 00 00 84 21 00 00 0C 2F 00 00 F4 2F 00 00 D8 30 00 00 20 31 00 00 7C 31 00 00 E6 32 00 00 5C 36 00 00 DC 31 00 00 84 21 00 00 10 32 00 00 4C 32 00 00 84 21 00 00 84 21 00 00 05 00 AA 55 00 00 00 00 00 00 00 00 00 30 00 31 00 32 00 33 00 34 00 35 00 36 00 37 5B 10 02 C0 21 64 00 C0 22 60 A8 3A 02 C0 20 64 38 10 00 32 40 B1 F8 10 9F 6F CF 5F 04 33 8D 5F 00 35 03 24 A0 B4 18 65 FD 0B 14 10 C1 7B 03 6C 03 6C 8F EA 13 00 13 10 00 78 03 6C 03 6C 03 6C 03 6C 03 6C 90 10 43 6D A0 B4 00 C0 21 60 2F 10 01 C0 2B 64 2E 10 00 30 20 B0 2E 10 01 C0 2B 64 2D 10 00 30 20 B0 FA 07 F9 07 00 00 00 20 00 00 90 EF 00 E0 F8 0F 00 20 D8 22 00 00 60 21 00 00 3C 33 00 00 00 30 00 20 FF FF 00 00 FF 0F 00 00 EE EE 00 00 EE 0E 00 00 21 14 20 B8 7F 6C 40 60 20 81 21 41 C4 63 20 98 01 14 3C 78 C3 14 44 74 40 3A 1F 0C 43 6D 03 6D 03 36 18 69 40 3C 1A 0C 20 A5 00 2A 40 3A 15 0C 00 25 17 6D 03 36 18 69 40 3C 10 0C 20 A5 00 2A 40 3A 0B 0C 00 25 17 6D 03 36 18 69 "
        Dim li = StrToHexData(nstrbuf)
        Dim sintbuf() As Byte = li.ToArray
        Dim crcBuf(4096) As Byte
        Dim crcBufLen As UInt32
        CalcCRC16(sintbuf, sintbuf.Length, crcBuf, crcBufLen)
    End Sub

    Public Function StrToHexData(HexFilePath As String) As List(Of Byte)
        Dim result As List(Of Byte) = New List(Of Byte)
        Dim strbuf() As String = HexFilePath.Trim.Split(" ")
        Dim index As Integer =0
        For Each str As String In strbuf
            str = str.Trim
            index = (HexStrToInt(str(0)) * 16) + HexStrToInt(str(1))
            result.Add(CByte(index))
        Next
        Return result


    End Function

    Public Function HexStrToInt(onestr As String) As Integer
        Dim result As Integer = 0
        If Integer.TryParse(onestr, result) Then
            Return result
        Else
            Select Case onestr.Trim .ToUpper
                Case "A"
                    Return 10
                Case "B"
                    Return 11
                Case "C"
                    Return 12
                Case "D"
                    Return 13
                Case "E"
                    Return 14
                Case "F"
                    Return 15
                Case Else
                    Return 0
            End Select
        End If
        Return result
    End Function

End Class
