Modbus協議完全資料與程序解析
switch(Modbus_mode) //通過判斷模式來進行對響應的發送
{
case Modbus_read_coil:
read_coil_proc();
break;
……
default:
return;
break;
}
這樣的做的話,就可以吧解析函數,執行函數和具體的實施函數分開來弄,層次多多少少要清晰一些
下面就是針對01,02,03,04,05,06,15,16幾個功能碼的執行及返回進行說明
在說明各功能函數之前,先說說響應。
上面說的那兩個函數只不過是對一幀的外圍進行解析與判斷,至于具體的參數,還需要功能函數去解析與返回,功能函數要做的事情有3個,1個是參數的解析,2是執行,3是返回響應。
先說響應,響應是有特點的,第一個字節肯定是自己的本機地址,第二個字節肯定是功能碼,最后兩個字節肯定是crc校驗,所以說,在發送緩沖中,基本上4個字節已經定死了
Modbus_send_buf[0] = Modbus_addr;
Modbus_send_buf[1] = Modbus_read_input_reg; //相應的功能碼,每個功能寒暑都不一樣
再經過執行函數最后算crc
modbus_crc = crc16(Modbus_send_buf,temp); //計算發送crc數據
Modbus_send_buf[temp] = modbus_crc >> 8; //計算
temp++;
Modbus_send_buf[temp] = modbus_crc & 0xff; //return num 高位
5.1 01 讀線圈狀態
#define Modbus_read_coil 0x01
其實表面上挺難理解的,啥線圈啥的,但你仔細看看就可以了解,就是讀輸出數字量,如果你寫下位機的話,其實就是控制讀取輸出io,說白了,就是把目前的io輸出狀態返回給主機。這些io連接的可能是繼電器,也可能是一些開關之類的東西,也就是些數字信號。讀數字輸出信號。
計算機發送命令:[設備地址] [命令號01] [起始寄存器地址高8位] [低8位] [讀取的寄存器數高8位] [低8位]
設備響應:[設備地址] [命令號01] [返回的字節個數][數據1][數據2]...[數據n][CRC校驗的低8位] [CRC校驗的高8位]
簡單的說就是返回所有的輸出io的值,放在一個或者幾個字節里,可以用判斷的方法來實現,當然,也可以用與或的方式實現。
if(P1_0 == 1)
{
temp |= (1<<8);
}
else
{
temp &= (1<<8);
}
將temp的值放入第四個緩沖區,當然這根據設備的io口,編程時就已經確定了的。接下來就可以進行crc計算了。最后發送即可。
Modbus_send_buf[3] = temp;
modbus_crc = crc16(Modbus_send_buf,4);
Modbus_send_buf[4] = modbus_crc >> 8;
Modbus_send_buf[5] = modbus_crc & 0xff; //return num 高位
5.2 02 讀只可讀數字量寄存器(輸入狀態)
基本上和01意思差不多,只不過這個功能碼返回的數據是輸入io的數據,和01的區別是01可讀可改,而02只可讀不可改。也就是輸入的狀態。數據不可由設備本身控制。程序方面和01程序一樣。
5.3 03讀可讀寫模擬量寄存器(保持寄存器)
說簡單點就是讀da,da屬于模擬量,也可以輸出,但是以模擬量的方式來進行傳輸的
計算機發送命令:[設備地址] [命令號03] [起始寄存器地址高8位] [低8位] [讀取的寄存器數高8位] [低8位] [CRC校驗的低8位] [CRC校驗的高8位]
設備響應:[設備地址] [命令號03] [返回的字節個數][數據1][數據2]...[數據n][CRC校驗的低8位] [CRC校驗的高8位]
其中返回字節個數,為讀取寄存器數乘2
寫程序時,首先要注意數據個數,temp = Modbus_recevie_buf[5];一般寄存器個數不會超過255,個數取讀取寄存器個數的低八位即可。返回即乘2,temp = temp << 1;,下面要做的就是一個循環for(i = 0;i < temp ; i += 2),把需要的數據放入發送數組。其內容是
Modbus_send_buf[i+3]=(data_v&0xff00)>>8;
Modbus_send_buf[i+4]=data_v&0x0ff;
由于幀的前面3個是地址,功能碼,和返回字節個數,所以循環從第四個數據開始存放。data_v為讀取的數據,在程序中還需要其他語句配合。比如:data_v = updateValue();
循環后就可以進入crc校驗了可以利用返回字節數來確定crc的校驗個數temp = temp + 3;,最后計算發送字節的個數
send_cnt = Modbus_recevie_buf[5]*2 + 5 ; //數據發送個數 數據+地址+命令+返回數據個數+crc低+crc高
最后將數據發送出去即可。
5.4 04讀只可讀模擬量寄存器(輸入寄存器)
和03的區別是04就是讀ad,ad輸入輸入模擬兩,只能讀,不能改,同樣也是以模擬兩的方式來進行傳輸的。其程序 與03類似
5.5 05寫數字量(線圈狀態)
05則是修改io口輸出狀態,數字量輸出。
計算機發送命令:[設備地址] [命令號05] [需下置的寄存器地址高8位] [低8位] [下置的數據高8位] [低8位] [CRC校驗的低8位] [CRC校驗的高8位]
設備響應:若執行成功,則原樣返回
寫程序時,首先確定需要修改的io口,然后根據0xff00或0x0000來置位或清零該數據位。執行完成后,將接收到的數據重新發送即可 Uart0_senddata(Modbus_recevie_buf,8);
5.6 06寫單個模擬量寄存器(保持寄存器)
06為修改設備da數據,模擬量傳輸數據。
計算機發送命令:[設備地址] [命令號06] [需下置的寄存器地址高8位] [低8位] [下置的數據高8位] [低8位] [CRC校驗的低8位] [CRC校驗的高8位]
設備響應:若執行成功,原樣返回即可
5.7 16主機設置寄存器
簡單的說,就是一次設置多個da,以一個偏移量為準,一次設置多個輸出模擬里量
計算機發送命令:[設備地址] [命令號10] [開始地址高8位] [低8位] [寄存器個數高8位] [低8位] [第一個寄存器數據高][第一個寄存器數據低][第二個寄存器數據高][第二個寄存器數據低]……[CRC校驗的低8位] [CRC校驗的高8位]
命令響應:功能碼[0x10],寄存器起始地址高字節,低字節,要寫的寄存器數量的高字節,低字節,CRC校驗低字節,高字節
在程序中,首先要獲取寄存器個數
num = Modbus_recevie_buf[6] - 2;
然后進入循環,一次把寄存器數據提取出來for(i = 0; i < num; i = i + 2)
在循環的內部提取數據temp = (((unsigned int)(Modbus_recevie_buf[i+7])<<8)|(Modbus_recevie_buf[i+8]));
以上就是我在項目中涉及到的一點modbus的通訊的下位機程序,不全,但總體的思路,接收數據并解析,解析后提取數據在設備上加載或采集,然后再按照響應的方式發送回去。
下回改進的方向,1,增加功能碼2,增加宏定義及編譯定義,3增加單片主機的程序,和pc主從機的程序。4,增加ascii的程序,和rtu同時設置。Pc機程序,采用c#號編寫。
主站蜘蛛池模板:
浦北县|
平顺县|
沙雅县|
无极县|
临城县|
新余市|
泾源县|
松江区|
安义县|
怀化市|
铜川市|
红河县|
甘肃省|
隆德县|
锦屏县|
三门峡市|
双峰县|
奉贤区|
尉犁县|
永川市|
新巴尔虎右旗|
永昌县|
如皋市|
庆城县|
天门市|
阿克陶县|
栾川县|
内丘县|
扶沟县|
孟连|
恩平市|
共和县|
桐城市|
丹棱县|
固阳县|
永新县|
太康县|
濮阳县|
常宁市|
泸州市|
临桂县|
評論