Modbus學習總結2

在實際編程過程中需要注意以下一些細節。

1、MODBUS 3.5T是如何計算的?

T = 1000毫秒*(1起始位+數據位+奇偶校驗+停止位)/波特率

如果你的通訊方式是:波特率115200(表示每秒傳輸多少字符),數據位8,無奇偶校驗。那麼你發送一個字符的時間是:T=1000* (1起始位+8數據位+0奇偶校驗+1停止位)/ 115200=0.087ms。

發送端:發送一幀後延時7*T(其中3.5T是停止時間,3.5T是起始時間)再發送第二幀,保證一幀數據裡頭各字節間間隔延時不能超過1.5T。

接收端:接收一個字節,查詢2T時間,是否有接收到下一個字節,有則這幀數據未完,繼續循環接收;沒有則默認這幀已經接收完畢。

2、串口超時時間設置

COMMTIMEOUTS timeout;

//填充timeout結構

GetCommTimeouts(m_h232Port,&timeout);

timeout.ReadIntervalTimeout=100;//兩字符之間最大的延時

timeout.ReadTotalTimeoutConstant=500;

timeout.ReadTotalTimeoutMultiplier=0;//讀取每字符間的超時

timeout.WriteTotalTimeoutConstant=2000;

timeout.WriteTotalTimeoutMultiplier=60;//寫入每字符間的超時

BOOLerror=SetCommTimeouts(m_hPort,&timeout);

ReadIntervalTimeout:兩字符之間最大的延時,當讀取串口數據時,一旦兩個字符傳輸的時間差超過該時間,讀取函數將返回現有的數據。設置為0表示該參數不起作用。

ReadTotalTimeoutMultiplier:讀取每字符間的超時。

ReadTotalTimeoutConstant:一次讀取串口數據的固定超時。所以在一次讀取串口的操作中,其超時為ReadTotalTimeoutMultiplier乘以讀取的字節數再加上 ReadTotalTimeoutConstant。將ReadIntervalTimeout設置為MAXDWORD,並將ReadTotalTimeoutMultiplier 和ReadTotalTimeoutConstant設置為0,表示讀取操作將立即返回存放在輸入緩衝區的字符。

WriteTotalTimeoutMultiplier:寫入每字符間的超時。

WriteTotalTimeoutConstant:一次寫入串口數據的固定超時。所以在一次寫入串口的操作中,其超時為WriteTotalTimeoutMultiplier乘以寫入的字節數再加上 WriteTotalTimeoutConstant。

3、串口編程。

在程序中如果要用到多個串口,而且還要做很多複雜的處理,那麼最好不用MSComm通訊控件,網絡上有很多封裝好的串口類。

C++ class for Win32 serial ports:http://www.naughter.com/serialport.html

CSerialPort:https://github.com/itas109/CSerialPort

4、如何寫穩定的modbus代碼?

需要從穩定性和易讀性來考慮,如果穩定性較差會造成系統控制故障,如果易讀性差就會造成難以維護,這些控制指令之間差別很小,如果一個一個單獨寫命令,非常容易出錯。對應舉措如下:

1)避免每個指令寫一部分代碼,需要統一處理,比如校驗函數,發送函數,接收函數等。通信協議已知,從中可以知道通信的實際數據長度(不包含包頭包尾和校驗的部分),所以可以控制讀寫多少個字節,並且可以知道什麼時候啟動校驗,那些數據參與校驗計算。

2)為指令建立指令列表,這樣後來需要添加功能,就可以直接把指令字加入列表即可。

3)適當抽象。為每個指令的動作寫回調函數,這樣就可以在應用層,用一句簡單的回調函數指針直接操作具體的動作函數,而不是應用邏輯層操作具體的驅動層面的接口。

4)超時,必須有超時機制。通信失敗怎麼處理?通信之後一般線斷了怎麼處理?不能讓系統死等後面的幾個字節發過來。

5)接收超時機制,不能依靠數傳輸的字節個數來停止接收來和區分幀間隔,因為可能通信就是斷掉了,所以要按照協議,串口通信情況下3~5個T空閒就認為一幀結束。也可以通過協議事先計算出接收數據的總時間(字符數*(字符時間+字符間間隔時間)),使用該使用作為等待超時時間,但最長超時時間不能超過5T時間。

6)響應超時機制,Modbus是請求/應答式通信,那麼主機就需要知道到底多久從機才會應答,主機等待從機應答的最長時間就是從機的最大回復間隔,超過這個時間後從機即使已經完成計算也不能回覆,因為此時主機可能已經開始給其他從機發送數據了。

7)如果通信都是由你發起的,那麼無論此次通信是完成還是超時或失敗,都應該關閉通信端口。

8)如果是線程中操作串口,可以提升線程優先級,讓操作系統在一定程度上提高串口讀寫性能。

9)ModbusRTU設備參數設置如下:

(1)內部屬性:單擊“查看設備內部屬性” ,點擊按鈕進入內部屬性

(2)最小採集週期:組態軟件對設備進行操作的時間週期, 單位為 ms, 默認為100ms,根據採集數據量的大小,設置值可適當調整

(3)設備地址:必須和實際設備的地址相一致,範圍為0-255,默認值為 0。

(4)通訊等待時間:通訊數據接收等待時間,默認設置為 200ms,根據採集數據量的大小,設置值可適當調整。

(5)快速採集次數:對選擇了快速採集的通道進行快採的頻率(已不使用,為與老驅動兼容,故保留,無需設置) 。

(6)16位整數解碼順序:調整字元件的解碼順序,對於Modicon PLC 及標準 PLC設備,使用默認值即可。

16 位整數解碼順序 舉例:0x0001

0―12 表示字元件高低字節不顛倒(默認值) 表示 1

1―21 表示字元件高低字節顛倒 表示 256

(7)32位整數解碼順序:調整雙字元件的解碼順序,對於Modicon PLC,請設置為“2-3412”順序解碼。

32 位整數解碼順序 舉例: 0x0000 0001

0―1234 表示雙字元件不做處理直接解碼(默認值) 表示 1

1―2143 表示雙字元件高低字不顛倒,但字內高低字節顛倒 表示256

2—3412表示雙字元件高低字顛倒,但字內高低字節不顛倒 表示65536

3—4321表示雙字元件內4個字節全部顛倒 表示 16777216

(8)32位浮點數解碼順序:調整雙字元件的解碼順序,對於Modicon PLC,請設置為“2-3412”順序解碼。

32 位浮點數解碼順序 舉例:0x3F80 0000

0―1234 表示雙字元件不做處理直接解碼(默認值) 表示 1.0

1―2143 表示雙字元件高低字不顛倒,但字內高低字節顛倒 表示-5.78564e-039

2—3412表示雙字元件高低字顛倒,但字內高低字節不顛倒 表示2.27795e-041

3—4321表示雙字元件內4 個字節全部顛倒 表示 4.60060e-041

(9)校驗方式: 選擇LRC校驗值的組合方式, 對於 Modicon PLC及標準 PLC 設備,

使用默認設置即可。

0—LH[低字節,高字節]:校驗結果為2 個字節,低字節在前,高字節在後。

1—HL[高字節,低字節]:校驗結果為2 個字節,高字節在前,低字節在後。默認值。

(10)分塊採集方式:驅動採集數據分塊的方式,對於Modicon PLC及標準 PLC設備,使用默認設置可以提高採集效率。

0— 按最大長度分塊:採集分塊按最大塊長處理,對地址不連續但地址相近的多個分塊,分為一塊一次性讀取,以優化採集效率。

1— 按連續地址分塊:採集分塊按地址連續性處理,對地址不連續的多個分塊,每次只採集連續地址,不做優化處理。

例如:有4區寄存器地址分別為 1~5,7,9~12的數據需採集,如果選擇“0-按最大長度分塊” ,則兩塊可優化為地址1~12的數據打包1次完成採集;如果選擇“1-按連續地址分塊” ,則需要採集 3 次。

(11)4區16 位寫功能碼選擇:寫 4 區單字時功能碼的選擇,這個屬性主要是針對自己製作設備的用戶而設置的,這樣的設備4區單字寫可能只支持 0x10 功能碼,而不支持0x06 功能碼。

0—0x06:單字寫功能碼使用0x06。

1—0x10:單字寫功能碼使用0x10。

注意:

(1). “解碼順序”及“校驗方式”設置:主要是針對非標準 ModbusRTU 協議的不同解碼及校驗順序。當用戶通過本驅動軟件與設備通訊時,如果出現解析數據值不對,或者通訊校驗錯誤(通訊狀態為3),可與廠家諮詢後對以上兩項進行設置。而對於ModiconPLC及支持標準ModbusRTU 的 PLC 及控制器等設備,一般需將“32位整數解碼順序”和“32位浮點數解碼順序”設置為“2-3412” 。 另外,在使用本驅動與“Modbus 串口數據轉發設備”構件通訊時, “解碼順序”及“校驗方式”均需按默認值設置,否則會導致通訊失敗或解析數據錯誤。

(2). “分塊採集方式”設置:主要是針對非標準 ModbusRTU協議設備。當用戶通過本驅動軟件與設備通訊時,如果按默認“0-按最大長度分塊”時,出現讀取連續地址正常,而不連續地址不正常時,可與廠家諮詢,並設置為“1-按連續地址分塊方式”嘗試是否可正常通訊。 而對於 Modicon PLC 及支持標準 ModbusRTU 的 PLC 及控制器等設備,直接使用默認設置即可,這樣可以提高採集效率。

5、lrc和crc校驗算法

//--------------------------------------------------------------------------

// Constants

//--------------------------------------------------------------------------

static const uint16_t modbus_crc_table[256] = {

0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,

0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,

0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,

0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,

0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,

0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,

0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,

0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,

0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,

0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,

0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,

0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,

0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,

0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,

0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,

0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,

0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,

0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,

0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,

0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,

0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,

0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,

0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,

0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,

0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,

0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,

0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,

0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,

0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,

0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,

0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,

0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040

};

//--------------------------------------------------------------------------

// Modbus functions

//--------------------------------------------------------------------------

uint8_t modbus_lrc_calc(uint8_t *data, uint16_t len)

{

uint8_t lrc = 0U;

int i;

for (i = 0U; i < len; i++)

{

lrc += data[i];

}

lrc = (0xFFU - lrc)+1U;

return(lrc);

}

uint16_t modbus_crc_calc(uint8_t *buffer, uint16_t size)

{

uint16_t crc = 0xFFFFU;

uint8_t nTemp;

while (size--)

{

nTemp = *buffer++ ^ crc;

crc >>= 8;

crc ^= modbus_crc_table[(nTemp & 0xFFU)];

}

return(crc);

}

LRC算出來的是16進制的字節值,若發送的數據是字符串形式,不需任何轉換,直接放在字符串最後位置就行了。若發送的數據是數組形式,則需要轉換為ASCII值,即由1字節的16進制轉換為2字節的ASCII值,4D應該轉換為34 44。

如果你的發送指令是:com.output="xxxxx",就是字符串形式;

如果發送指令形式是:com.output=A[],就是數組形式。

但不管是哪種形式,ModBus ASCII模式最終在數據線上的數據都是ASCII值。字符串形式的數據,編譯軟件會自動轉換。

6、調試工具

Modbus Poll是一個Modbus管理模擬器軟件,幫助開發人員進行管理和監控的Mod bus數據區在同一時間和模擬Mod bus協議。支持Modbus RTU / ASCII和Modbus TCP / IP協議。

下載地址:http://www.downcc.com/soft/25945.html

modbus slave是一款功能強大的modbus子設備模擬工具,可以幫助modbus通訊設備開發人員進行modbus通訊協議的模擬和測試,用於模擬、測試、調試modbus通訊設備。

下載地址:http://www.ddooo.com/softdown/70166.htm

7、modbus開源項目

https://github.com/stephane/libmodbus


分享到:


相關文章: