|
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位字符的形式,有時(shí)候開頭的0沒有寫出。
//下面的地址是當(dāng)S7-200為modbus從站時(shí)S7-200內(nèi)部的點(diǎn)和modbus地址的對應(yīng)關(guān)系。
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地址的前兩個(gè)字符(如果地址形式為6位)或前一個(gè)字符(如果地址形式為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 表示寫單個(gè)位,而功能碼15 則是寫多個(gè)位.
Function 6 is a single word write and function 16 is a multiple word write.//功能碼6表示寫一個(gè)字,而功能碼16表示寫多個(gè)字
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開始的,第一個(gè)保持寄存器的地址是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,后面的分析也表明了確實(shí)可以超過,而且達(dá)到了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個(gè)數(shù)據(jù),則使用功能碼5,如果是寫多個(gè)位則使用功能碼15
NOTE: The boadcast address (0) cannot be used if this is a read request.
//主要廣播地址(指從站的地址為0時(shí))不能用于讀數(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個(gè)離散量輸出位時(shí)的功能碼5,我們將強(qiáng)制使用功能碼15.
LDD> AC0, 0 //地址范圍比較
AD< AC0, 10000
LPS
AB= #RW:LB2, mRead:0
MOVB 1, mModbusFunction:VB255 //讀單個(gè)離散量輸出數(shù)據(jù),則使用功能碼1
LRD
AB= #RW:LB2, mWrite:1 //寫單個(gè)離散量輸出則使用功能碼5
LPS
AW= #Count:LW7, 1
MOVB 5, mModbusFunction:VB255
LPP
LDW> #Count:LW7, 1 //如果是寫多個(gè)離散量輸出或設(shè)定了強(qiáng)制性使用功能碼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ù)時(shí)使用功能碼2
MOVB 2, mModbusFunction:VB255 // FunctionNumber = 2
-D +10001, AC0 //地址減去10001,使得取消掉地址中包含的功能碼含義,并且使得出現(xiàn)在modbus總線上的地址從0開始
//注意:雖然modbus地址是按讀取數(shù)據(jù)類型的不同而分段的,
//但是在提取出功能碼后地址卻都是一個(gè)范圍 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ù)時(shí)使用功能碼4
MOVB 4, mModbusFunction:VB255 // FunctionNumber = 4
-D +30001, AC0 //地址減去30001,使得取消掉地址中包含的功能碼含義,并且使得出現(xiàn)在modbus總線上的地址從0開始
//注意:雖然modbus地址是按讀取數(shù)據(jù)類型的不同而分段的,
//但是在提取出功能碼后地址卻都是一個(gè)范圍 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個(gè)保持寄存器則使用功能碼6,如果是寫多個(gè)保持寄存器則使用功能碼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個(gè)保持寄存器的功能碼6,我們將強(qiáng)制使用功能碼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 //兩個(gè)地址范圍的檢查
AD< AC0, 50000
-D +40001, AC0 //地址減去40001,使得取消掉地址中包含的功能碼含義,并且使得出現(xiàn)在modbus總線上的地址從0開始
//注意:雖然modbus地址是按讀取數(shù)據(jù)類型的不同而分段的,
//但是在提取出功能碼后地址卻都是一個(gè)范圍 0-9999
AENO
LDD> AC0, 400000
AD<= AC0, 465536
-D +400001, AC0 //地址減去40001,使得取消掉地址中包含的功能碼含義,并且使得出現(xiàn)在modbus總線上的地址從0開始
//注意:雖然modbus地址是按讀取數(shù)據(jù)類型的不同而分段的,
//但是在提取出功能碼后地址卻都是一個(gè)范圍 0-9999
AENO
OLD
LPS
AB= #RW:LB2, mRead:0 //讀取保持寄存器時(shí)使用功能碼3
MOVB 3, mModbusFunction:VB255
LRD
AB= #RW:LB2, mWrite:1 //寫單個(gè)保持寄存器時(shí)使用功能碼6
LPS
AW= #Count:LW7, 1
MOVB 6, mModbusFunction:VB255
LPP
LDW> #Count:LW7, 1 //寫多個(gè)保持寄存器或強(qiáng)制使用功能碼時(shí)使用功能碼16
O mModbusForceMulti:V251.0
ALD
MOVB 16, mModbusFunction:VB255
LPP
JMP 10
Address error...
//modbus地址檢查出錯(cuò)處理...
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 //程序如果能運(yùn)行到這里,表明modbus地址都不在指定的標(biāo)準(zhǔn)地址范圍呢,則表示地址出錯(cuò)
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,則顯示錯(cuò)誤并且取消掉當(dāng)前操作,如果是寫數(shù)據(jù)(功能碼為5,6,15,16)時(shí)則設(shè)置1個(gè)標(biāo)志以便當(dāng)我們發(fā)送結(jié)束后就終止當(dāng)前消息
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,用于計(jì)算modbus數(shù)據(jù)的crc校驗(yàn)碼,不再給出解釋,照著crc算法即可看懂,只要注意其中的兩個(gè)循壞即可。
/////////////////////////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é)進(jìn)行計(jì)算
XORB *#ptr:*LD4, AC0 // XOR byte with current CRC LSByte
FOR AC1, +1, +8 //內(nèi)循壞,對單個(gè)字節(jié)進(jìn)行計(jì)算,每個(gè)字節(jié)共8位,這里沒有使用常用的
//算法,而是對每個(gè)字節(jié)的每個(gè)位逐一計(jì)算,常規(guī)算法是對每個(gè)字節(jié)對應(yīng)的結(jié)果保存在一個(gè)數(shù)組里,然
//后將該字節(jié)的數(shù)值做為數(shù)組下標(biāo)索引而直接從數(shù)組中取得結(jié)果,這樣能減少計(jì)算時(shí)間,提高計(jì)算速度
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ù)指針,以便對下一個(gè)字節(jié)進(jìn)行處理
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
|
|