找回密码
 注册

QQ登录

只需一步,快速开始

搜索

S7-200 modbus库MBUS_MSG和MBUSM1部分代码注释

[复制链接]
coolice 发表于 2020-4-25 16:11:09 | 显示全部楼层 |阅读模式
这些代码摘自西门子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地址经常写作5或6位字符的形式,有时候开头的0没有写出。
//下面的地址是当S7-200为modbus从站时S7-200内部的点和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主站访问数据类型的功能代码,后四位字符指定了指定数据类型的正确数据的地址。

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的数据的地址,注意:这里给定的modbus数据地址是双字32位而不是单字16位,表明modbus数据地址范围可能会超过//65535,后面的分析也表明了确实可以超过,而且达到了40多万,但只是对某些数据类型而言

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之间则访问的数据则数据类型为离散量输出。
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.
//如果是读数据,则使用modbus功能码1,如果是写1个数据,则使用功能码5,如果是写多个位则使用功能码15
NOTE:    The boadcast address (0) cannot be used if this is a read request.
//主要广播地址(指从站的地址为0时)不能用于读数据。
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    //读单个离散量输出数据,则使用功能码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    //数据地址减1,使得出现在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直接则数据类型为离散量输入
The only thing we can do with discrete inputs is to read them via modbus function 2.
//对于离散量输入唯一要做的就是通过功能码2去读取数据

LDD>   AC0, 10000                  // 地址范围判断
AD<    AC0, 20000                  //   ...
AB=    #RW:LB2, mRead:0            //    (RW is read)    //读取离散量输入数据时使用功能码2
MOVB   2, mModbusFunction:VB255    //   FunctionNumber = 2
-D     +10001, AC0                 //地址减去10001,使得取消掉地址中包含的功能码含义,并且使得出现在modbus总线上的地址从0开始
                                                //注意:虽然modbus地址是按读取数据类型的不同而分段的,
                                                //但是在提取出功能码后地址却都是一个范围   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之间,则数据类型为模拟量输入
The only thing we can do with analog inputs is to read them via modbus function 4.
//对离散量输入数据唯一可以做的事情就是用功能码4去读取数据。



LDD>   AC0, 30000                  //判断地址范围
AD<    AC0, 40000                  //   ...
AB=    #RW:LB2, mRead:0            //  读取模拟量输入数据时使用功能码4
MOVB   4, mModbusFunction:VB255    //   FunctionNumber = 4
-D     +30001, AC0              //地址减去30001,使得取消掉地址中包含的功能码含义,并且使得出现在modbus总线上的地址从0开始
                                                //注意:虽然modbus地址是按读取数据类型的不同而分段的,
                                                //但是在提取出功能码后地址却都是一个范围   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之间,则数据类型为保持寄存器
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地址剔除掉功能码后的地址数据)的保持寄存器,这将允许"高级"用户使用全部地址范围而对"正常"用户来说仍然是简单的。



LDD>   AC0, 40000            //两个地址范围的检查
AD<    AC0, 50000                  
-D     +40001, AC0    //地址减去40001,使得取消掉地址中包含的功能码含义,并且使得出现在modbus总线上的地址从0开始
                                                //注意:虽然modbus地址是按读取数据类型的不同而分段的,
                                                //但是在提取出功能码后地址却都是一个范围   0-9999

AENO
LDD>   AC0, 400000
AD<=   AC0, 465536
-D     +400001, AC0    //地址减去40001,使得取消掉地址中包含的功能码含义,并且使得出现在modbus总线上的地址从0开始
                                                //注意:虽然modbus地址是按读取数据类型的不同而分段的,
                                                //但是在提取出功能码后地址却都是一个范围   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,则显示错误并且取消掉当前操作,如果是写数据(功能码为5,6,15,16)时则设置1个标志以便当我们发送结束后就终止当前消息

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数据的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    外循坏,对所有字节进行计算
XORB   *#ptr:*LD4, AC0             //   XOR byte with current CRC LSByte

FOR    AC1, +1, +8 //内循坏,对单个字节进行计算,每个字节共8位,这里没有使用常用的
               //算法,而是对每个字节的每个位逐一计算,常规算法是对每个字节对应的结果保存在一个数组里,然
               //后将该字节的数值做为数组下标索引而直接从数组中取得结果,这样能减少计算时间,提高计算速度

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 //修改数据指针,以便对下一个字节进行处理
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
您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|手机版|小黑屋|ELEOK |网站地图

GMT+8, 2024-12-23 02:27

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表