none
crc16程式碼有問題,請前輩們幫忙看哪有問題 RRS feed

  • 問題

  • 各位前輩

    我在網路上抓到CRC16的程式碼

    不過它好像是用VB6.0寫的

    不過我現在用的是VB2005express

    語法上好像不完全相容(2005顯示語法錯誤處以紅字表示)

    想請教大家,不知要如何修改才能使用

     

    範例如下

    计算法就是依据crc校验码的产生原理来设计程序。其优点是模块代码少,修改灵活,可移植性好。其缺点为计算量大。为了便于理解,这里假定了三位数据,而多项式码为a001(hex)。

      在窗体上放置一命令按钮command1,并添加如下代码:


      private sub command1_click()

       dim crc() as byte

       dim d() as byte ’待传输数据

       redim d(2) as byte 錯誤 1 無法再使用 'ReDim' 陳述式來宣告陣列變數。 
       d(0) = 123

       d(1) = 112

       d(2) = 135

       crc = crc16(d) ’调用crc16计算函数 錯誤 2 型別 'String' 的值無法轉換成 '1-維陣列屬於 Byte'。 
       ’crc(0)为高位

       ’crc(1)为低位

      end sub

      注意:在数据传输时crc的低位可能在前,而高位在后。


      function crc16(data() as byte) as string

       dim crc16lo as byte, crc16hi as byte   ’crc寄存器

       dim cl as byte, ch as byte        ’多项式码&ha001

       dim savehi as byte, savelo as byte

       dim i as integer

       dim flag as integer

       crc16lo = &hff

       crc16hi = &hff

       cl = &h1

       ch = &ha0

       for i = 0 to ubound(data)

        crc16lo = crc16lo xor data(i) ’每一个数据与crc寄存器进行异或

        for flag = 0 to 7

         savehi = crc16hi

         savelo = crc16lo

         crc16hi = crc16hi \ 2      ’高位右移一位

         crc16lo = crc16lo \ 2      ’低位右移一位

         if ((savehi and &h1) = &h1) then ’如果高位字节最后一位为1

          crc16lo = crc16lo or &h80   ’则低位字节右移后前面补1

         end if              ’否则自动补0

         if ((savelo and &h1) = &h1) then ’如果lsb为1,则与多项式码进行异或

          crc16hi = crc16hi xor ch

          crc16lo = crc16lo xor cl

         end if

        next flag

       next i

       dim returndata(1) as byte

       returndata(0) = crc16hi       ’crc高位

       returndata(1) = crc16lo       ’crc低位

       crc16 = returndata 錯誤 3 型別 '1-維陣列屬於 Byte' 的值無法轉換成 'String'

    end function

    請大家幫忙解法我的問題

    謝謝


      

    2007年3月5日 上午 07:09

解答

  • Dear Sir

    Modbus crc 之前小弟也有搜尋過,下面是小弟目前正在用的code您可以參考看看

        ''MODBUS CRC
        Public Function GetCrc16(ByRef Data() As Byte) As Boolean
            If UBound(Data) < 8 Then
                Return False
                Exit Function
            End If
            Dim cCRC As Integer = &HFFFF
            Dim cIntTmp As Integer = 0
            For intCnt As Integer = 0 To UBound(Data) - 2
                cCRC = cCRC Xor Data(intCnt)
                For intBit As Integer = 0 To 7
                    cIntTmp = cCRC Mod 2
                    cCRC = cCRC \ 2
                    If cIntTmp = 1 Then
                        cCRC = cCRC Xor &HA001
                    End If
                Next intBit
            Next intCnt
            Data(UBound(Data) - 1) = cCRC Mod 256
            Data(UBound(Data)) = cCRC \ 256
            Return True
        End Function

    2007年3月5日 上午 07:15
    版主
  • Dear Sir

    主要是我現在不知道你的通訊協定是甚麼,這個你要去看文件;如果是String的話大部份來說就是傳送ASCII Code,你可以先宣告一個空陣列A,之後利用Encoding.ASCII.Getbyte來將字串資料轉換成byte陣列並填入A陣列中,之後將byte陣列(A)傳入getcrc16中來取得檢查碼並且組合成完整的封包之後再將封包傳送去測試看看。

    2007年3月5日 上午 10:20
    版主

所有回覆

  • Dear Sir

    Modbus crc 之前小弟也有搜尋過,下面是小弟目前正在用的code您可以參考看看

        ''MODBUS CRC
        Public Function GetCrc16(ByRef Data() As Byte) As Boolean
            If UBound(Data) < 8 Then
                Return False
                Exit Function
            End If
            Dim cCRC As Integer = &HFFFF
            Dim cIntTmp As Integer = 0
            For intCnt As Integer = 0 To UBound(Data) - 2
                cCRC = cCRC Xor Data(intCnt)
                For intBit As Integer = 0 To 7
                    cIntTmp = cCRC Mod 2
                    cCRC = cCRC \ 2
                    If cIntTmp = 1 Then
                        cCRC = cCRC Xor &HA001
                    End If
                Next intBit
            Next intCnt
            Data(UBound(Data) - 1) = cCRC Mod 256
            Data(UBound(Data)) = cCRC \ 256
            Return True
        End Function

    2007年3月5日 上午 07:15
    版主
  • Bauann 您好,

     

    感謝您最近幾天為小弟的問題做回覆

    不暪您說,這是小弟第一次接觸vb

    只能看書找網路硬湊程式,

    小弟目前要寫的程式是只要將modbus所需的address(位置)function(/)data address(指令位置)data value(讀取資料數)

    填上後,

    透過rs232,儀器會將資料回送回來,

    您的程式碼上將getCRC16()()中的值宣告為byte

    但我的程式在傳送button的程式碼中(程式碼如下)

    Private Sub btnsend_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnsend.Click

            Dim outcmd As String

            Dim retstr As String

            retstr = RS232.ReadExisting

            Select Case txtfunc.Text.Trim

                Case "03"

                    outcmd = txtaddr.Text.Trim + txtfunc.Text.Trim + txtdataaddr.Text.Trim

                    outcmd = outcmd + GetCrc16(outcmd)

                    RS232.Write(outcmd)

            End Select

    Outcmd是字串(string)….格式和byte不符

    請問這個問題要如何解決

     

    謝謝

    2007年3月5日 上午 08:05
  • Dear Sir

    主要是我現在不知道你的通訊協定是甚麼,這個你要去看文件;如果是String的話大部份來說就是傳送ASCII Code,你可以先宣告一個空陣列A,之後利用Encoding.ASCII.Getbyte來將字串資料轉換成byte陣列並填入A陣列中,之後將byte陣列(A)傳入getcrc16中來取得檢查碼並且組合成完整的封包之後再將封包傳送去測試看看。

    2007年3月5日 上午 10:20
    版主
  • Dear Bauann

    再次謝謝你的回覆,

    不好意思,因為我是新手,很多術語不是很了解

    我的通訊協定應該是Modbus RTU Mode

    以下是datasheet Funtion03(03hex)Read Holding Registers的格式

    Query

    Example of a request to read 0....1(register 40001 to 40002)from slave device1

    Field Name                              RTU(hex)

    Header                                     None

    Slave address                         01

    Function                                   03

    Starting Address HI                00

    Starting Address LO               00

    No of registers HI                    00

    No of registers LO                   02

    Error Check LO                       C4

    Error Check HI                         0B

    Trailer                                        None

    Total Bytes                                8

     

    Respones                            

    Field Name                          RTU(hex)                         

    Header                                  None

    Slave address                     01

    Function                               03

    Byte Count                           04

    Data HI                                 00

    Data LO                               06

    Data HI                                 00

    Data LO                               05

    Error Check LO                  DA

    Error Check HI                    31

    Trailer                                  None

    Total Bytes                          8

    不知這樣的資料是否能讓您更了解我的問題

    因為我實在不清楚要如何來撰寫此程式

    讓VB2005express能和儀器溝通

    2007年3月6日 上午 01:06
  • Dear Sir

    以function "03"來說,您應該宣告一個byte陣列,長度為8,例如下面這樣

            Dim B(7) As Byte
            B(0) = 1
            B(1) = 3
            B(2) = 0
            B(3) = 0
            B(4) = 0
            B(5) = 2
            GetCrc16(B)

    這樣就可以得到要傳送出去的byte陣列,另外Getcrc16的程式要改一下,一開頭的 If UBound(Data) < 8 改成 If UBound(Data) < 7 ;Response的部份就是送出之後Device會回應給你的資料。

    2007年3月6日 上午 01:32
    版主
  • Dear Bauann

    謝謝您的回覆,

    想再進一步請教,

    我的介面設計如下

    Address                        Function                    Dataaddress                      Datavalue

    address/function/dataaddress/datavalue都是要讓使用者輸入數值

    而且剛才我看了datasheet.....RTU(hex)

    指的是不是它認16進位,

    之前你有提到如果是ASCII編碼,可利用encording.ASCII.Getbyte來將ASCII轉換成位元組,

    那如果Datasheet中寫的,要以16進位輸入,再轉成位元組的話要如何撰寫

    還是說我可以讓使用者在輸入時以十進位輸入(使用上比較直覺)

    然後我在程式中再將使用者輸入的十進位轉成16進位,

    讓程式和儀器能溝通

    ps.你說到CRC16的程式

    一開頭的 If UBound(Data) < 8 改成 If UBound(Data) < 7一開頭的 If UBound(Data) < 8 改成 If UBound(Data) < 7

    請問為何要改,我想知道原因,以後如果有其他情況發生,能知道何時要改

    如何改

    再次麻煩你解答

    2007年3月6日 上午 01:56
  • Dear Sir

    改 if UBound(Data)<7 主要這段是我自己加的,因為我在用的時候封包UBound長度一定超過8,所以小於8的我視為異常,而現在你在用的這個UBound是7,所以要修改一下。

    16進位或是10進位都可以,主要都是"人"看到的不一樣,你填到byte陣列之後就是0~255的資料在裡面;所以你看你要給使用者填入10進位或16進位都可以,如果是10進制程式就會像是 B(2) = 10 ,如果是16進制就會是 B(2) = cint("&H" & Textbox1.text) ,類似這樣

    2007年3月6日 上午 02:19
    版主
  • Dear Bauann

    謝謝你的回覆,

    我大概了解你的意思

    我會先就之前所說的來試寫

    到時有問題再麻煩你幫忙了

    2007年3月6日 上午 02:26
  • Dear Bauaan

    不好意思,又要請教您一些問題

    我寫了傳送按鈕的程式加上您的crc16程式,可以請你幫忙check嗎

    Private Sub btnsend_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnsend.Click
            Dim dataout(7) As Byte
            dataout(0) = txtaddr.Text
            dataout(1) = txtfunc.Text
            dataout(2) = txtdataaddrHI.Text
            dataout(3) = txtdataaddrLO.Text
            dataout(4) = txtdatavalHI.Text
            dataout(5) = txtdatavalLO.Text
            GetCrc16(dataout)
            RS232.Write(dataout(7))
        End Sub
        Public Function GetCrc16(ByRef Data() As Byte) As Boolean
            If UBound(Data) < 7 Then
                Return False
                Exit Function
            End If
            Dim cCRC As Integer = &HFFFF
            Dim cIntTmp As Integer = 0
            For intCnt As Integer = 0 To UBound(Data) - 2
                cCRC = cCRC Xor Data(intCnt)
                For intBit As Integer = 0 To 7
                    cIntTmp = cCRC Mod 2
                    cCRC = cCRC \ 2
                    If cIntTmp = 1 Then
                        cCRC = cCRC Xor &HA001
                    End If
                Next intBit
            Next intCnt
            Data(UBound(Data) - 1) = cCRC Mod 256
            Data(UBound(Data)) = cCRC \ 256
            Return True
        End Function

    另外,我要如何在接收的欄位(txtreceive)看到儀器傳回來的數值

    程式碼如何寫

    我在書上看到write方法的位元組陣資料輸出/入格式

    write(buffer as byte(),offset as integer, count as integer)

    read(buffer as byte(),offset as integer, count as integer)

    這兩個是什麼意思

    和我要寫輸出/入的程式有關嗎

    2007年3月6日 上午 06:34
  • Dear Sir

    你是用SerialPort嗎?是的話可以參考

    http://msdn2.microsoft.com/zh-tw/library/ms143549(VS.80).aspx

    例如

            Dim intReturn As Integer = 0
            Dim Recvbyte As Byte()
            ReDim Recvbyte(7)
            intReturn = SerialPort1.Read(Recvbyte, 0, 8)

    Read方法的船回值表示這一次動作總共成功的收到幾個byte,而

    read(buffer as byte(),offset as integer, count as integer)

    buffer是用來放接收進來資料的陣列,offect是要從陣列的哪一個位置開始放資料,count是說總共要收幾個byte。

    而接收的話就是利用Read相關的方法將資料接收進來,接收進來的也是byte陣列看你怎麼表示出來給"人"看了

    例如

            For i As Integer = 0 To Recvbyte.Length - 1
                TextBox1.Text = TextBox1.Text & Hex(Recvbyte) & ","
            Next

    2007年3月6日 下午 03:01
    版主
  • Dear Bauann

    我的程式還是有問題,想請你再幫忙看看

    傳送的按鈕程式

    Private Sub btnsend_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnsend.Click
            Dim dataout(7) As Byte
            dataout(0) = 0
            dataout(1) = 3
            dataout(2) = 0
            dataout(3) = 0
            dataout(4) = 0
            dataout(5) = 1
            GetCrc16(dataout)
            RS232.Write(dataout, 0, 8)
        End Sub

    在之前所提到datasheet中

    我的指令是依十六進位來決定

    例如小數點顯示的指令為0000

    也就是dataout(2)和dataout(3)所要填入的數值

    不過當我keyin dataout(2)=00和dataout(3)=00時

    程式會自動改為dataout(2)=0和dataout(3)=0

    是不是有可能因為它沒辦法得到正確的指令

    所以程式還是沒辦法和儀器溝通

    2.接收按鈕的程式

    Private Sub btnreceive_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnreceive.Click
            Dim Inbyte() As Byte, Readcount As Integer
            ReDim Inbyte(7)
            Readcount = RS232.Read(Inbyte, 0, 8)
            For i As Integer = 0 To Inbyte.Length - 1
                txtreceive.Text = txtreceive.Text & Hex(Inbyte) & ","
            Next
        End Sub

    想請問的是redim的用意是什麼

    txtreceive.text&Hex(Inbyte)是什麼意思

    我在執行時

    最後一行的程式txtreceive.Text = txtreceive.Text & Hex(Inbyte) & ","
    出現錯誤訊息"引數"Number"無法轉換為型別"Byte()"

     

    麻煩你代為解答

    謝謝


     

    2007年3月12日 下午 01:51
  • Dear Sir

    有一些部分您要注意一下,

    1.) Dataout是一個byte陣列,00跟0在byte陣列中是同樣的東西,所以沒有差別

    2.) Read之前最好先Delay一段時間,讓你的Device有時間可以處理/回應

    3.) Redim 是重新定義陣列的大小,因為預期會接收到8個byte所以Redim為Inbyte(7),同時也有初始化陣列的用意

    4.) & 關鍵字是在Visual basic中用來連接字串用的,例如 "123" & "456" 就會等於 "123456"

    5.) For i As Integer = 0 To Inbyte.Length - 1
                txtreceive.Text = txtreceive.Text & Hex(Inbyte(i)) & ","
         Next

        Hex是把數值轉成16進位的表示方式,例如 Hex(10) = "A",這段只是把接收進來的資料轉為16進制顯示出來而已

    2007年3月12日 下午 02:31
    版主
  • Dear Bauann

    謝謝你的回覆,

    想再請教你有關

    2): Read之前最好先Delay一段時間,讓你的Device有時間可以處理/回應

    請問這個delay程式是直接寫在我的接收按鈕的程式中嗎

    這該如何寫??

     

    2007年3月13日 上午 01:08
  • Dear Sir

    最簡單的是呼叫WIndows API Sleep去暫停程式的執行動作

        ''宣告方式
        Public Declare Auto Sub Sleep Lib "Kernel32.dll" (ByVal dwMilliseconds As Integer)
        ''呼叫範例,暫停50ms
        Sleep(50)

    2007年3月13日 上午 01:21
    版主
  • Dear Bauann

    非常感謝你的回覆,

    我的程式已經可以和儀器溝通了

    想再請教我希望在textreceive的地方

    只顯示部分矩陣的值,如Inbyte(4)和Inbyte(5)

    應該如處理?

    另外在我的儀器中可選擇小數點的位置

    如選擇小數點第一位時,

    儀器會顯示123.4(以十進位顯示)

    如選擇小數點第二位時

    儀器會顯示12.34(以十進位顯示)

    我的所有顯示數值會和小數點的設定有關連,

    這部分應如何撰寫

     

    謝謝你的回覆

     

     

    2007年3月14日 上午 04:02
  • ? ,把迴圈的範圍改掉就可以了

         For i As Integer = 0 To Inbyte.Length - 1
                txtreceive.Text = txtreceive.Text & Hex(Inbyte(i)) & ","
         Next

    或者你只固定要Inbyte(4)跟Inbyte(5)可以這樣用

            Dim strTmp As String
            strTmp = Hex(Inbyte(4)) & Hex(Inbyte(5))

    小數點的話,看你轉換出來要點在甚麼地方,主要你要知道現在是設定成1位或是2位,可以用SubString去切字串

            ''譬如strTmp = "1234"
            strTmp = strTmp.Substring(0, 3) & "." & strTmp.Substring(3, 1)
            ''結果 strTmp = "123.4"

    2007年3月14日 上午 04:14
    版主
  • Dear Bauann

    1

    請教一個問題,

    我的程式在第一次傳送/接收時,資料是無誤的

    但當我再按第二次及而後傳送/接收時,資料是錯誤的

    可否請你幫忙看看程式上是否有問題

    傳送程式

    Private Sub btnsend_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnsend.Click
            Dim dataout(7) As Byte
            dataout(0) = CType(txtaddr.Text, Byte)
            dataout(1) = CType(txtfunc.Text, Byte)
            dataout(2) = CType(txtdataaddrHI.Text, Byte)
            dataout(3) = CType(txtdataaddrLO.Text, Byte)
            dataout(4) = CType(txtdatavalHI.Text, Byte)
            dataout(5) = CType(txtdatavalLO.Text, Byte)
            GetCrc16(dataout)
            RS232.Write(dataout, 0, 8)
        End Sub

    接收程式

    Private Sub btnreceive_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnreceive.Click
            Dim Inbyte() As Byte, Readcount As Integer
            ReDim Inbyte(7)
            Readcount = RS232.Read(Inbyte, 0, 8)
            txtreceive.Text = txtreceive.Text & Hex(Inbyte(3)) & txtreceive.Text & Hex(Inbyte(4))

        End Sub

    第一次傳送/接收,顯示03

    第二次傳送/接收,顯示0033

    第三次傳送/接收,顯示00300333

    第四次傳送/接收,顯示0030033003003333

    每多按一次的傳送/接收,

    數值的數目是以2的次方增加,但數值的排列並不規則

    不知這樣的問題是否是程式有誤

    2

    另外想請教的是在字串的連接上有+和&兩種方式

    txtreceive.Text = txtreceive.Text & Hex(Inbyte(3)) & txtreceive.Text & Hex(Inbyte(4))

    txtreceive.Text = txtreceive.Text & Hex(Inbyte(3)) + txtreceive.Text & Hex(Inbyte(4))

    這兩種方式是否有差異

    2007年3月15日 上午 01:55
  • 1. 在 txtreceive.Text = txtreceive.Text & Hex(Inbyte(3)) & txtreceive.Text & Hex(Inbyte(4)) 之前,先把txtreceive.Text = "" ,清掉原本的東西

    2. 兩種都可以,但是用 & 比較好,因為有時VB會幫你偵測、轉換型態可能會發生預料外的錯誤,例如說下面的程式碼你可以比較看看不同的地方

            Dim int As Integer
            Dim str1, str2 As String
            int = 12
            str1 = "12"
            str2 = "34"
            MessageBox.Show("12" + "34")
            MessageBox.Show(str1 + str2)
            MessageBox.Show(int + str2)
            MessageBox.Show(int & str2)

    2007年3月15日 上午 02:52
    版主
  • Dear Bauann

    抱歉,我沒把問題寫清楚

    因為我必須留下第一次的接收資料

    第二次接收將它放在第二行

    以此類推

    另外,請問

    vbNewline

    vbNullchar

    vbTab

    vbBack

    vbCr

    vbLf

    各代表什麼意思

    2007年3月15日 上午 05:19
  • 1. 你要這樣做的話要另外用一個字串變數去暫存,最後再來組合,不然會把TextBox搞亂

    2.看一下下面的網頁,你要的都在上面,有問題的話再說

    http://msdn2.microsoft.com/zh-tw/library/c157t28f(VS.80).aspx

    2007年3月15日 上午 07:00
    版主