提示:文章寫完后,目錄可以自動(dòng)生成,如何生成可參考右邊的幫助文檔
前言
本章節(jié)是博主學(xué)習(xí)Modbus TCP所寫的一篇文章
一、Modbus TCP是什么?
1.1.1 Modbus TCP介紹
Modbus TCP是Modbus的一個(gè)分支,主要用到網(wǎng)口通信的,在工業(yè)上也用的很頻繁,我們公司最近的項(xiàng)目就是用的W5500+modbus TCP來和PC端的上位機(jī)通信和交互的。雖然這個(gè)項(xiàng)目不是我在寫代碼,但是我都學(xué)了Modbus RTU了,那我也抽空學(xué)習(xí)一下Modbus TCP吧。本次的Modbus TCP不是博主寫的,是一個(gè)大佬開源的一個(gè)W5500寫的一個(gè)Mobus TCP代碼,我在這個(gè)基礎(chǔ)上自己寫了主機(jī)讀從機(jī)多個(gè)數(shù)據(jù),和主機(jī)寫從機(jī)寄存器的函數(shù)。這個(gè)項(xiàng)目搞了一天,主要是一直ping不通,最后ping通了。
Modbus TCP 相較于 Mobus RTU是沒有CRC16校驗(yàn)位的,而且還多了4個(gè)字節(jié)的事務(wù)協(xié)議。
1.1.2 報(bào)文格式
0x03 主機(jī)讀從機(jī)寄存器
/*******************************************************************************
Modbus TCP
主機(jī)保持讀從機(jī) 實(shí)物協(xié)議 字節(jié)長(zhǎng)度 從機(jī)地址 功能碼 起始地址 讀取線圈個(gè)數(shù)
主機(jī) 05 95 00 00 00 06 01 03 00 00 00 01
實(shí)物協(xié)議 字節(jié)長(zhǎng)度 從機(jī)地址 功能碼 字節(jié)個(gè)數(shù) 上傳數(shù)據(jù)
從機(jī) 05 95 00 00 00 05 01 03 02 00 04
0x03 讀保持寄存器
********************************************************************************/
0x06 主機(jī)寫從機(jī)寄存器
/*******************************************************************************
Modbus TCP
主機(jī)寫單一從機(jī) 實(shí)物協(xié)議 字節(jié)長(zhǎng)度 從機(jī)地址 功能碼 寄存器地址 下發(fā)數(shù)據(jù)
主機(jī) 31 C9 00 00 00 06 01 06 00 00 00 20
實(shí)物協(xié)議 字節(jié)長(zhǎng)度 從機(jī)地址 功能碼 寄存器地址 下發(fā)數(shù)據(jù)
從機(jī) 31 C9 00 00 00 06 01 06 00 00 00 20
0x06 寫單一寄存器
********************************************************************************/
總體來說還是很簡(jiǎn)單的
二、W5500配置
工程源碼:添加鏈接描述,大家記得給博主點(diǎn)贊哈。
首先是配置
然后是查看你的IP地址、網(wǎng)關(guān)、子網(wǎng)掩碼等
Win+R彈出運(yùn)行框,然后輸入cmd進(jìn)入到
然后是 ipconfig/all可以查看IP地址等,這里我用的以太網(wǎng).
然后可以自己靜態(tài)分配地址 IPv4
然后就是修改代碼
/*******************************************************************************
* 函數(shù)名 : Load_Net_Parameters
* 描述 : 裝載網(wǎng)絡(luò)參數(shù)
* 輸入 : 無
* 輸出 : 無
* 返回值 : 無
* 說明 : 網(wǎng)關(guān)、掩碼、物理地址、本機(jī)IP地址、端口號(hào)、目的IP地址、目的端口號(hào)、端口工作模式
*******************************************************************************/
void Load_Net_Parameters(void)
{
Gateway_IP[0] = 192;//加載網(wǎng)關(guān)參數(shù)
Gateway_IP[1] = 168;
Gateway_IP[2] = 1;
Gateway_IP[3] = 1;
Sub_Mask[0]=255;//加載子網(wǎng)掩碼
Sub_Mask[1]=255;
Sub_Mask[2]=255;
Sub_Mask[3]=0;
Phy_Addr[0]=0x08;//加載物理地址 ,這個(gè)可以和主機(jī)的不一樣
Phy_Addr[1]=0x97;
Phy_Addr[2]=0x98;
Phy_Addr[3]=0xBE;
Phy_Addr[4]=0x2F;
Phy_Addr[5]=0x7F;
IP_Addr[0]=192;//加載本機(jī)IP地址
IP_Addr[1]=168;
IP_Addr[2]=1;
IP_Addr[3]=17;
S0_Port[0] = 0x13;//加載端口0的端口號(hào)5000
S0_Port[1] = 0x88;
S0_Mode=TCP_SERVER;//加載端口0的工作模式,TCP服務(wù)端模式
}
然后我們?cè)赾md里面ping一下看通不通
輸入 ping 自己的IP,我這里是 ping 192.168.1.17,下面是Ping成功的情況。
這個(gè)時(shí)候我們就可以用Modbus調(diào)試了。
三.讀保持寄存器&&寫單一寄存器函數(shù)
1.代碼展示
/*******************************************************************************
* 函數(shù)名 : Process_Socket_Keep_Read_nData
* 描述 : W5500接收并發(fā)送接收到的多個(gè)數(shù)據(jù)
* 輸入 : s:端口號(hào)
* 輸出 : 無
* 返回值 : 無
* 說明 : 本過程先調(diào)用S_rx_process()從W5500的端口接收數(shù)據(jù)緩沖區(qū)讀取數(shù)據(jù),
* 然后將讀取的數(shù)據(jù)從Rx_Buffer拷貝到Temp_Buffer緩沖區(qū)進(jìn)行處理。
* 處理完畢,將數(shù)據(jù)從Temp_Buffer拷貝到Tx_Buffer緩沖區(qū)。調(diào)用S_tx_process()
* 發(fā)送數(shù)據(jù)。注意這里是發(fā)送多個(gè)數(shù)據(jù)(0x03),寫一個(gè)數(shù)據(jù)是(0x06)
*******************************************************************************/
void Process_Socket_Keep_Read_nData(SOCKET s,uint8_t* Tx_Buff_datas){
uint16_t size;
uint8_t Tx_Buffer_CP[128];
uint8_t p=0;
size=Read_SOCK_Data_Buffer(s, Rx_Buffer);
/*******************************************************************************
Modbus TCP
主機(jī)保持讀從機(jī) 實(shí)物協(xié)議 字節(jié)長(zhǎng)度 從機(jī)地址 功能碼 起始地址 讀取線圈個(gè)數(shù)
主機(jī) 05 95 00 00 00 06 01 03 00 00 00 01
實(shí)物協(xié)議 字節(jié)長(zhǎng)度 從機(jī)地址 功能碼 字節(jié)個(gè)數(shù) 上傳數(shù)據(jù)
從機(jī) 05 95 00 00 00 05 01 03 02 00 04
0x03 讀保持寄存器
********************************************************************************/
if(Rx_Buffer[6]==0x01){//判讀是否是該從機(jī)寄存器地址
if(Rx_Buffer[7]==0x03){//主機(jī)讀取從機(jī)保持讀寄存器
// printf("PC receive Data(0x03)=");
// for(int i=0;i<size;i++){
// printf("0x%X,",Rx_Buffer[i]);
// }
// printf("\r\n");
//判斷發(fā)送的字節(jié)數(shù)量
uint16_t Begin_addr=Rx_Buffer[8]<<8;//讀取起始地址高八位
Begin_addr=Rx_Buffer[9]; //讀取起始地址低八位
uint16_t End_addr=Rx_Buffer[10]<<8;//讀取結(jié)束地址高八位
End_addr=Rx_Buffer[11];//讀取結(jié)束地址低八位
uint16_t RX_SUM=End_addr-Begin_addr;//主機(jī)讀取多少個(gè)數(shù)據(jù)
uint16_t RX_SUM_size=RX_SUM*2;
//printf("PULL READ Data=%d\r\n",RX_SUM_size);
//判斷事務(wù)協(xié)議后的字節(jié)數(shù)量
uint16_t start_size=3+RX_SUM_size;
//實(shí)物協(xié)議碼
Tx_Buffer_CP[0]=Rx_Buffer[0];
Tx_Buffer_CP[1]=Rx_Buffer[1];
Tx_Buffer_CP[2]=Rx_Buffer[2];
Tx_Buffer_CP[3]=Rx_Buffer[3];
//下發(fā)長(zhǎng)度
Tx_Buffer_CP[4]=start_size>>8;
Tx_Buffer_CP[5]=start_size;
//從機(jī)地址
Tx_Buffer_CP[6]=Rx_Buffer[6];
//功能碼
Tx_Buffer_CP[7]=Rx_Buffer[7];
//發(fā)送的字節(jié)長(zhǎng)度
Tx_Buffer_CP[8]=RX_SUM_size;
p=9;
for(int j=0;j<RX_SUM_size;j++){
if((p+j)%2==1){ //填充高8位
Tx_Buffer_CP[p+j]=Tx_Buff_datas[j];
}else{ //填充低8位
Tx_Buffer_CP[p+j]=Tx_Buff_datas[j];
}
}
memcpy(Tx_Buffer, Tx_Buffer_CP, p+RX_SUM_size);
//printf("size=%d\r\n",p+RX_SUM_size);
//發(fā)送響應(yīng)報(bào)文
Write_SOCK_Data_Buffer(0, Tx_Buffer, p+RX_SUM_size);
}else if(Rx_Buffer[7]==0x06){//主機(jī)寫從機(jī)單一寄存器
/*******************************************************************************
Modbus TCP
主機(jī)寫單一從機(jī) 實(shí)物協(xié)議 字節(jié)長(zhǎng)度 從機(jī)地址 功能碼 寄存器地址 下發(fā)數(shù)據(jù)
主機(jī) 31 C9 00 00 00 06 01 06 00 00 00 20
實(shí)物協(xié)議 字節(jié)長(zhǎng)度 從機(jī)地址 功能碼 寄存器地址 下發(fā)數(shù)據(jù)
從機(jī) 31 C9 00 00 00 06 01 06 00 00 00 20
0x06 寫單一寄存器
********************************************************************************/
//寫地址
uint16_t Write_addr=Rx_Buffer[8]>>8;
Write_addr=Rx_Buffer[9];
//寫數(shù)據(jù)
uint16_t Write_data=Rx_Buffer[10]>>8;
Write_data=Rx_Buffer[11];
printf("Write_addr=0x%x,Write_data=0x%x\r\n",Write_addr,Write_data);
printf("PC receive Data(0x06)=");
for(int w=0;w<size;w++){
printf("0x%X,",Rx_Buffer[w]);
}
printf("\r\n");
for(int i=0;i<size;i++){
Tx_Buffer[i]=Rx_Buffer[i];
}
printf("0x06_data_size=%d\r\n",size);
Write_SOCK_Data_Buffer(0, Tx_Buffer, size);
if(Write_addr==0x04){
if(Write_data==0x01){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET);//控制LED燈
}else if(Write_data==0x02){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET);
}
}
}
}else{
printf("Not the address, but=0x%X,\r\n",Rx_Buffer[6]);
}
}
這里就介紹完了,最后效果展示
2.參考資料
W5500+STM32F103C8T6進(jìn)行TCP通信(modbus)_junseven164的博客-CSDN博客文章來源:http://www.zghlxwxcb.cn/news/detail-616176.html
總結(jié)
Modbus TCP使用還是非常好的,之前博主沒有學(xué)Modbus的時(shí)候一直用的JSON格式來打包數(shù)據(jù)給上位機(jī)?,F(xiàn)在掌握兩種數(shù)據(jù)格式,美滋滋文章來源地址http://www.zghlxwxcb.cn/news/detail-616176.html
到了這里,關(guān)于[STM32F103C8T6]W5500+Modbus TCP(HAL庫)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!