標題: S7-200 modbus庫MBUS_MSG和MBUSM1部分代碼注釋 [打印本頁]

作者: 51hei小林    時間: 2016-9-24 20:19
標題: S7-200 modbus庫MBUS_MSG和MBUSM1部分代碼注釋
S7-200 modbus庫MBUS_MSG和MBUSM1部分代碼注釋
這些代碼摘自西門子S7-200自帶的modbus庫 MBUS_MSG和MBUSM1。



The Addr input is based on Modbus addresses.

Modbus addresses are normally written as 5 or 6 character values.  Sometimes the leading zero is not present.
//Modbus地址經(jīng)常寫作5或6位字符的形式,有時候開頭的0沒有寫出。
//下面的地址是當S7-200為modbus從站時S7-200內(nèi)部的點和modbus地址的對應關系。


000001 - 00xxxx are discrete outputs mapped to Q0.0 - Q15.7
//000001 - 00xxxx 表示離散量輸出映射到 Q0.0 - Q15.7
010001 - 01xxxx are discrete inputs mapped to I0.0 - I15.7
//010001 - 01xxxx 表示離散量輸入映射到 I0.0 - I15.7
030001 - 03xxxx are input registers
040001 - 04xxxx are output (holding) registers

The first two characters determine which function the master uses to access the data type.  The last four characters of the address select the proper value within the data type.
//modubs地址的前兩個字符(如果地址形式為6位)或前一個字符(如果地址形式為5位)決定了modbus主站訪問數(shù)據(jù)類型的功能代碼,后四位字符指定了指定數(shù)據(jù)類型的正確數(shù)據(jù)的地址。

Addresses 00xxxx utilize functions 1, 5 and 15.    //地址 00xxxx 使用功能碼1, 5 and 15.
Addresses 01xxxx utilize function 2.                    //地址 01xxxx 使用功能碼 2.
Addresses 03xxxx utilize function 4.                    //地址 03xxxx 使用功能碼 4.
Addresses 04xxxx utilize functions 3, 6 and 16.    //地址 04xxxx 使用功能碼 3, 6 and 16.


Function 5 is a single bit write and function 15 is a multiple bit write. //功能碼 5 表示寫單個位,而功能碼15 則是寫多個位.
Function 6 is a single word write and function 16 is a multiple word write.//功能碼6表示寫一個字,而功能碼16表示寫多個字

All Modbus addresses are 1 based, that is, the first holding register is addressed as 040001, but Modbus is zero based on the wire (seems strange, doesn't it?).     
//所有的Modbus地址是從1開始的,第一個保持寄存器的地址是040001,但是在modbus總線上地址卻是從0開始的(是不是很奇怪,為什么?)

We need to check to be sure that the address is not zero (for any given data type).   
//我們需要檢查以確保對任意給定的類型地址不能是0



//下面是部分代碼和代碼的注釋
//取modbus的數(shù)據(jù)的地址,注意:這里給定的modbus數(shù)據(jù)地址是雙字32位而不是單字16位,表明modbus數(shù)據(jù)地址范圍可能會超過//65535,后面的分析也表明了確實可以超過,而且達到了40多萬,但只是對某些數(shù)據(jù)類型而言

LD     SM0.0
MOVD   #Addr:LD3, AC0              // get a copy of Addr



If the Address input is greater than 0 and less than 10000 then the data type is for discrete outputs.  
//如果輸入的地址在0-10000之間則訪問的數(shù)據(jù)則數(shù)據(jù)類型為離散量輸出。
If the request is to read then we will use modbus function 1.  If this is a write and the count is 1 then we will use Modbus function 5.   If this is a multibit write then we must use modbus function 15 for the write.
//如果是讀數(shù)據(jù),則使用modbus功能碼1,如果是寫1個數(shù)據(jù),則使用功能碼5,如果是寫多個位則使用功能碼15
NOTE:    The boadcast address (0) cannot be used if this is a read request.
//主要廣播地址(指從站的地址為0時)不能用于讀數(shù)據(jù)。
NOTE:    There is an override to force the use of function 15 for single bit writes in the case where the slave does not support
     Modbus Function 5.  
//注意:如果從站不支持寫1個離散量輸出位時的功能碼5,我們將強制使用功能碼15.

LDD>   AC0, 0        //地址范圍比較
AD<    AC0, 10000

LPS
AB=    #RW:LB2, mRead:0
MOVB   1, mModbusFunction:VB255    //讀單個離散量輸出數(shù)據(jù),則使用功能碼1
LRD
AB=    #RW:LB2, mWrite:1    //寫單個離散量輸出則使用功能碼5
LPS
AW=    #Count:LW7, 1
MOVB   5, mModbusFunction:VB255
LPP
LDW>   #Count:LW7, 1            //如果是寫多個離散量輸出或設定了強制性使用功能碼15,則使用功能碼15
O      mModbusForceMulti:V251.0
ALD
MOVB   15, mModbusFunction:VB255

LPP
DECD   AC0    //數(shù)據(jù)地址減1,使得出現(xiàn)在modbus總線上的地址是從0開始
JMP    10



If the Address input is greater than 10000 and less than 20000 then the data type is for discrete inputs.
//如果地址在10000-20000直接則數(shù)據(jù)類型為離散量輸入
The only thing we can do with discrete inputs is to read them via modbus function 2.
//對于離散量輸入唯一要做的就是通過功能碼2去讀取數(shù)據(jù)

LDD>   AC0, 10000                  // 地址范圍判斷
AD<    AC0, 20000                  //   ...
AB=    #RW:LB2, mRead:0            //    (RW is read)    //讀取離散量輸入數(shù)據(jù)時使用功能碼2
MOVB   2, mModbusFunction:VB255    //   FunctionNumber = 2
-D     +10001, AC0                 //地址減去10001,使得取消掉地址中包含的功能碼含義,并且使得出現(xiàn)在modbus總線上的地址從0開始
                                                //注意:雖然modbus地址是按讀取數(shù)據(jù)類型的不同而分段的,
                                                //但是在提取出功能碼后地址卻都是一個范圍   0-9999

JMP    10                          //     ...
                                   //   continue


If the Address input is greater than 30000 and less than 40000 then the data type is for analog inputs.
//如果地址范圍在30000-40000之間,則數(shù)據(jù)類型為模擬量輸入
The only thing we can do with analog inputs is to read them via modbus function 4.
//對離散量輸入數(shù)據(jù)唯一可以做的事情就是用功能碼4去讀取數(shù)據(jù)。



LDD>   AC0, 30000                  //判斷地址范圍
AD<    AC0, 40000                  //   ...
AB=    #RW:LB2, mRead:0            //  讀取模擬量輸入數(shù)據(jù)時使用功能碼4
MOVB   4, mModbusFunction:VB255    //   FunctionNumber = 4
-D     +30001, AC0              //地址減去30001,使得取消掉地址中包含的功能碼含義,并且使得出現(xiàn)在modbus總線上的地址從0開始
                                                //注意:雖然modbus地址是按讀取數(shù)據(jù)類型的不同而分段的,
                                                //但是在提取出功能碼后地址卻都是一個范圍   0-9999

JMP    10                          //     ...
                                   //   continue




If the Address input is greater than 40000 and less than 50000 then the data type is for holding registers.  
  //如果地址范圍在40000-50000之間,則數(shù)據(jù)類型為保持寄存器
If the request is to read then we will use modbus function 3.  If this is a write and the count is 1 then we will use Modbus function 6.   If this is a multibit write then we must use modbus function 16 for the write.
//如果是讀取保持寄存器,則使用功能碼3,如果是寫1個保持寄存器則使用功能碼6,如果是寫多個保持寄存器則使用功能碼16
NOTE:    There is an override to force the use of function 16 for single word writes in the case where the slave does not support
     Modbus Function 6.  
//注意:如果從站不支持寫1個保持寄存器的功能碼6,我們將強制使用功能碼16.
NOTE:    We have added a check for the address range for 400,001 to 465,536 so that users can address holding register
    numbers greater than 9999.  This allows use of the full address range for the "advanced" users but still keeps it
    simple for the "normal" users.
//我們增加了對地址范圍400001-465536的檢查,以便用戶能夠訪問地址大于9999(modbus地址剔除掉功能碼后的地址數(shù)據(jù))的保持寄存器,這將允許"高級"用戶使用全部地址范圍而對"正常"用戶來說仍然是簡單的。



LDD>   AC0, 40000            //兩個地址范圍的檢查
AD<    AC0, 50000                  
-D     +40001, AC0    //地址減去40001,使得取消掉地址中包含的功能碼含義,并且使得出現(xiàn)在modbus總線上的地址從0開始
                                                //注意:雖然modbus地址是按讀取數(shù)據(jù)類型的不同而分段的,
                                                //但是在提取出功能碼后地址卻都是一個范圍   0-9999

AENO
LDD>   AC0, 400000
AD<=   AC0, 465536
-D     +400001, AC0    //地址減去40001,使得取消掉地址中包含的功能碼含義,并且使得出現(xiàn)在modbus總線上的地址從0開始
                                                //注意:雖然modbus地址是按讀取數(shù)據(jù)類型的不同而分段的,
                                                //但是在提取出功能碼后地址卻都是一個范圍   0-9999

AENO
OLD
LPS
AB=    #RW:LB2, mRead:0    //讀取保持寄存器時使用功能碼3
MOVB   3, mModbusFunction:VB255
LRD
AB=    #RW:LB2, mWrite:1    //寫單個保持寄存器時使用功能碼6
LPS
AW=    #Count:LW7, 1
MOVB   6, mModbusFunction:VB255

LPP
LDW>   #Count:LW7, 1    //寫多個保持寄存器或強制使用功能碼時使用功能碼16
O      mModbusForceMulti:V251.0
ALD
MOVB   16, mModbusFunction:VB255
LPP
JMP    10



Address error...
//modbus地址檢查出錯處理...
The address is outside of the expected ranges (we did not match any of the range checks above) so show an error and return.  We will also reach here if there was, for example, a write to discrete inputs or analog inputs.  In either case there is an error so abort the message.



LD     SM0.0    //程序如果能運行到這里,表明modbus地址都不在指定的標準地址范圍呢,則表示地址出錯
MOVB   mRequestError:4, AC0        // show request error
JMP    250                         // goto error return



If this is a broadcast (address = 0) and the function is a read function (1, 2, 3 and 4) then show an error and abort.  If this is a write request (functions 5, 6, 15 and 16) then set a flag so that we can terminate the message after it has transmitted.
//如果是modbus廣播,則對于讀功能1,2,3,4,則顯示錯誤并且取消掉當前操作,如果是寫數(shù)據(jù)(功能碼為5,6,15,16)時則設置1個標志以便當我們發(fā)送結束后就終止當前消息

LDB=   #Slave:LB1, 0               // if (slave address == 0)
LPS
AB<=   mModbusFunction:VB255, 4    //   if (function <= 4)
MOVB   mRequestError:4, AC0        //     error = request error
JMP    250                         //     return
LPP
S      mModbusBroadcast:V250.1, 1  //   set the broadcast flag





//下面是西門子S7-200的modbus庫MBUSM1,用于計算modbus數(shù)據(jù)的crc校驗碼,不再給出解釋,照著crc算法即可看懂,只要注意其中的兩個循壞即可。
/////////////////////////CRC///////////////////////////

LD     SM0.0
MOVW   16#FFFF, AC0                // initialize the CRC 初始化crc的值為16#FFFF
BTI    mModbusBufr:VB0, #count:LW2 // get the byte count from the buffer
MOVD   &mModbusBufr:&VB0, #ptr:LD4 // get buffer address for CRC check
INCD   #ptr:LD4                    // point to first message byte

FOR    AC2, +1, #count:LW2         // for all bytes in msg    外循壞,對所有字節(jié)進行計算
XORB   *#ptr:*LD4, AC0             //   XOR byte with current CRC LSByte

FOR    AC1, +1, +8 //內(nèi)循壞,對單個字節(jié)進行計算,每個字節(jié)共8位,這里沒有使用常用的
               //算法,而是對每個字節(jié)的每個位逐一計算,常規(guī)算法是對每個字節(jié)對應的結果保存在一個數(shù)組里,然
               //后將該字節(jié)的數(shù)值做為數(shù)組下標索引而直接從數(shù)組中取得結果,這樣能減少計算時間,提高計算速度

SRW    AC0, 1                      //     shift the CRC
LD     SM1.1                       //     if (LSBit was 1)
XORW   16#A001, AC0                //       XOR the CRC polynomial
NEXT                               //   next bit

LD     SM0.0
INCD   #ptr:LD4                    //   point to the next message byte //修改數(shù)據(jù)指針,以便對下一個字節(jié)進行處理
NEXT                               // next message byte



LD     SM0.0                       // when the CRC is complete...
SWAP   AC0                         // swap CRC bytes
MOVW   AC0, #crc:LW0               // write the output
MOVW   AC0, *#ptr:*LD4             // write CRC into buffer











歡迎光臨 (http://www.torrancerestoration.com/bbs/) Powered by Discuz! X3.1