日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

rs485与modbus协议

發(fā)布時間:2023/12/14 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 rs485与modbus协议 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

????????

目錄

一、主從模式

二、modbus協(xié)議

1、modbus的兩種數(shù)據(jù)幀格式

2、modbus在rs485上的實現(xiàn)

?三、總結(jié)


????????在rs485的通信方式中,485總線上可以掛載多個設備,但是485是一種半雙工的通信方式(在一個時間段只能與一個設備通信),如果不對掛載的節(jié)點設備加以限制,會引起通信紊亂的問題。為了解決數(shù)據(jù)傳輸紊亂的問題,我們得對485通信加以規(guī)則限制,來保證通信的穩(wěn)定性和可靠性。舉個例子來說明rs485與modbus的關系我們把城市A看做4主機,城市B看做從機;如果兩個城市之間要想進行經(jīng)濟往來(數(shù)據(jù)傳輸),那么第一件事就是修路,而這里的公路就是rs485總線;既然公路修好了,就可以通車了,但如果我們不對路上的車輛加以限制的話,那發(fā)生交通事故的可能性就會很大,所以我們制定了一套交通規(guī)則去限制車輛的行駛,而這個"交通規(guī)則"就是modbus。

rs485總線示意圖

一、主從模式

????????485是一種半雙工通信方式(設備發(fā)送數(shù)據(jù)的時候不能接收數(shù)據(jù)),主從模式時為了解決485與多個設備通信出現(xiàn)通信數(shù)據(jù)紊亂的問題。在主從模式里,有這樣幾個規(guī)定:

????????1、系統(tǒng)中的主機和所有從機在上電之后都處于監(jiān)聽狀態(tài)。

????????2、從機不可以主動向主機發(fā)送數(shù)據(jù),同一時刻只能有一個從機與主機通信。

????????3、任何一次通信(數(shù)據(jù)交換)都由主機發(fā)起。主機發(fā)送數(shù)據(jù)請求之后就轉(zhuǎn)為接收數(shù)據(jù)狀態(tài)。主機按照預先約定好的格式發(fā)出尋址數(shù)據(jù)幀。

? ? ? ? 在約定好主從模式后,485通信主機和多從機的通信就可以互不干擾,485就可以選擇任意一個節(jié)點設備進行通信,但我們還得考慮一個問題,主機和節(jié)點設備該用怎么的數(shù)據(jù)幀來傳輸數(shù)據(jù)呢?這里可以有兩種方式:1.自定義數(shù)據(jù)幀格式;2.使用現(xiàn)有的通信協(xié)議(modbus)。很顯然自定義的數(shù)據(jù)幀格式的兼容性不強,只能用于自己設計的485通信設備,而modbus有更強的兼容性。

二、modbus協(xié)議

modbus是一種串行,modbus協(xié)議目前存在用于串口、以太網(wǎng)、485以及Tcp/IP等,modbus是一個請求/應答協(xié)議,并且提供功能碼規(guī)定的服務。modbus功能碼是 modbus請求/應答 PDU(協(xié)議數(shù)據(jù)單元)的元素。本文件的作用是描述 modbus事務處理框架內(nèi)使用的功能碼。

1、modbus的兩種數(shù)據(jù)幀格式

1?RTU 傳輸模式:當設備使用 RTU (Remote Terminal Unit) 模式在 Modbus 串行鏈路通信, 報文中每個 8 位字節(jié)含有兩個 4 位十六進制字符。這種模式的主要優(yōu)點是較高的數(shù)據(jù)密度,在相同的波特率下比 ASCII 模式有更高的吞吐率。每個報文必須以連續(xù)的字符流傳送。

Modbus 報文 RTU 幀:由發(fā)送設備將 Modbus 報文構(gòu)造為帶有已知起始和結(jié)束標記的幀。這使設備可以在報文的開始接收新幀,并且知道何時報文結(jié)束。不完整的報文必須能夠被檢測到而錯誤標志必須作為結(jié)果被設置。在 RTU 模式,報文幀由時長至少為 3.5 個字符時間的空閑間隔區(qū)分。在后續(xù)的部分,這個時間區(qū)間被稱作 t3.5

?注:整個報文幀必須以連續(xù)的字符流發(fā)送。(定時器定時)如果兩個字符之間的空閑間隔大于 1.5 個字符時間,則報文幀被認為不完整應該被接收節(jié)點丟棄。對于波特率大于 19200 Bps 的情形,應該使用 2 個定時的固定值:建議的字符間超時時間(t1.5)為750μs,幀間的超時時間 (t1.5) 為1.750ms。

2?ASCII 傳輸模式:由發(fā)送設備將 Modbus 報文構(gòu)造為帶有已知起始和結(jié)束標記的幀。這使設備可以在報文的開始接收新幀,并且知道何時報文結(jié)束。不完整的報文必須能夠被檢測到而錯誤標志必須作為結(jié)果被設置。ASCII詳情見modbus手冊。

2、modbus在rs485上的實現(xiàn)

主機用的是stm32f407,從機用的stm32f103。

主機的modbus實現(xiàn)

//主機向從機發(fā)送數(shù)據(jù)交換請求后,接收到從機的數(shù)據(jù)時,主機沒有做CRC校驗 #include "modbus.h" #include "sp3485.h" #include "CRC16.h"extern u8 flag;溫度、濕度、熱釋電、光照、煙霧、有毒氣體 //uint16_t ModBus_Buf[6] = {0,0,0,0,0,0};//從機數(shù)據(jù) //**************************功能碼****************************************************** //功能碼請求PDU void ModBus_Send03Req(uint8_t DeviceID,uint16_t StartAddr,uint16_t Count) {uint8_t Send_Buf[16] = "\0";uint16_t Temp_crc = 0;Send_Buf[0] = DeviceID;Send_Buf[1] = 03;Send_Buf[2] = (StartAddr>>8);Send_Buf[3] = (StartAddr&0XFF);Send_Buf[4] = (Count>>8);Send_Buf[5] = (Count&0XFF);Temp_crc = CRC16_Fanction(Send_Buf,6);Send_Buf[6] = (Temp_crc&0XFF);Send_Buf[7] = (Temp_crc>>8);RS485_Send_Data(Send_Buf,8); }/* open led1 ModBus_Send05Req(1,6,0x01); open led2 ModBus_Send05Req(1,6,0x10); open ledall ModBus_Send05Req(1,6,0x11); open beep ModBus_Send05Req(1,7,1); close beep ModBus_Send05Req(1,7,0);*/void ModBus_Send06Req(uint8_t DeviceID,uint16_t InputAddr,uint16_t OutVlaue) {uint8_t Send_Buf[16] = "\0";uint16_t Temp_crc = 0;Send_Buf[0] = DeviceID;Send_Buf[1] = 06;Send_Buf[2] = (InputAddr>>8);Send_Buf[3] = (InputAddr&0XFF);Send_Buf[4] = (OutVlaue>>8);Send_Buf[5] = (OutVlaue&0XFF);Temp_crc = CRC16_Fanction(Send_Buf,6);Send_Buf[6] = (Temp_crc&0XFF);Send_Buf[7] = (Temp_crc>>8);RS485_Send_Data(Send_Buf,8); }

從機的modbus實現(xiàn)

#include "modbus.h" #include "rs485.h" #include <string.h> #include <stdio.h> #include "CRC16.h" #include "oled.h" #include "led.h"//溫度、濕度、熱釋電、光照、煙霧、有毒氣體、led 、beep uint16_t ModBus_Buf[8] = {0,0,0,0,0,0,0,0}; u16 Slave_ID = 0;//modbus中斷 void TIM4_IRQHandler(void) {if(TIM_GetITStatus(TIM4,TIM_IT_Update) == SET){TIM_ClearITPendingBit(TIM4,TIM_IT_Update);RS485_DataBuf.Flag_over = 1;//一幀接收完成TIM_Cmd(TIM4,DISABLE);//關閉定時器} } /* Modbus數(shù)據(jù)幀格式(3.5t=3.5個字符時間間隔)起始位 + 設備地址 + 功能代碼 + 數(shù)據(jù) + CRC校驗 + 結(jié)束符3.5t 8Bit 8Bit n*8Bit 16Bit 3.5t */ void Modbus_Init(u16 id, u32 brr) {u32 time_arr = 0;Slave_ID = id;time_arr = (44000000/brr);//至少t3.5,我們使用4個字符間隔(單位us)ModBus_TIM4_Config(time_arr); } //modbos定時器,定時周期為4個字符時間 void ModBus_TIM4_Config(uint32_t arr) {TIM_TimeBaseInitTypeDef Base_InitStructure = {0};NVIC_InitTypeDef NVIC_InitStructure = {0};//時鐘使能RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);//配置定時器://配置基本定時器:時鐘預分頻、ARR(LOAD)、CNT(VAL)、計數(shù)方式、分頻因子Base_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//分頻因子Base_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上計數(shù)Base_InitStructure.TIM_Period = arr-1;//重裝載ARRBase_InitStructure.TIM_Prescaler = 72-1;//預分頻psc,usTIM_TimeBaseInit(TIM4,&Base_InitStructure);TIM_SetCounter(TIM4,0); //計數(shù)器清零TIM_Cmd(TIM4,DISABLE);TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);//打開定時器更新中斷NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);//定時器使能TIM_Cmd(TIM4,DISABLE); } //ModBus 獲取數(shù)據(jù)并解析(接收到的數(shù)據(jù)) void ModBus_GetData(void) {u16 CRC_Get = 0,CRC_Temp = 0;if(RS485_DataBuf.Flag_over == 1){//如果接收到的數(shù)據(jù)幀里面有數(shù)據(jù)if(RS485_DataBuf.Buf_count > 4)//設備地址 + 功能代碼 + CRC校驗 = 4Bit{//獲取數(shù)據(jù)最后兩位的CRC校驗碼CRC_Get = (RS485_DataBuf.Get_buf[RS485_DataBuf.Buf_count-1]<<8)|(RS485_DataBuf.Get_buf[RS485_DataBuf.Buf_count-2]);//驗證接收到數(shù)據(jù)的CRC校驗碼CRC_Temp = CRC16_Fanction(RS485_DataBuf.Get_buf,RS485_DataBuf.Buf_count-2);if(CRC_Get == CRC_Temp)//CRC校驗通過,數(shù)據(jù)無錯誤{//判斷是不是給我的數(shù)據(jù)if(RS485_DataBuf.Get_buf[0] == 0X00)ModBus_Rsp41(); //廣播else if(RS485_DataBuf.Get_buf[0] == Slave_ID){switch(RS485_DataBuf.Get_buf[1]){case 0X03:ModBus_Rsp03();break;//讀保持寄存器case 0x06:ModBus_Rsp06();break;//寫單個線圈default:break;}}elseprintf("not my data\r\n");}}//復位接收緩沖區(qū):為了接收下一組數(shù)據(jù)RS485_DataBuf.Flag_over = 0;RS485_DataBuf.Buf_count = 0;memset(RS485_DataBuf.Get_buf,0,256);} } //**************************功能碼******************************************************// /* 功能碼0x03 resPDU:功能碼(1B)+起始地址(2B)+寄存器數(shù)量(2B) rspPUD:功能碼(1B)+字節(jié)數(shù)(1B)2*N +寄存器值(N*2B) */ void ModBus_Rsp03(void) {uint8_t Send_Buf[256] = "\0";uint16_t i = 0,Temp_addr=0;uint16_t Temp_crc = 0;Send_Buf[0] = Slave_ID; //設備地址Send_Buf[1] = 0x03; //功能代碼Send_Buf[2] = ((RS485_DataBuf.Get_buf[4]<<8)|(RS485_DataBuf.Get_buf[5]))*2; //字節(jié)數(shù)//res的請求地址Temp_crc = (RS485_DataBuf.Get_buf[2]<<8)|(RS485_DataBuf.Get_buf[3]);for(i=0; i<Send_Buf[2]/2; i++){ //溫度、濕度、光照、煙霧、燈、按鍵、蜂鳴器Send_Buf[3+2*i] = (ModBus_Buf[Temp_addr+i]>>8);Send_Buf[4+2*i] = (ModBus_Buf[Temp_addr+i]&0XFF);}Temp_crc = CRC16_Fanction(Send_Buf,3+Send_Buf[2]);Send_Buf[3+2*i] = (Temp_crc&0XFF);Send_Buf[4+2*i] = (Temp_crc>>8);RS485_Send_Data(Send_Buf,5+Send_Buf[2]); } /* 功能碼0x06 resPDU:功能碼(1B)+起始地址(2B)+寄存器數(shù)量(2B) rspPUD:功能碼(1B)+起始地址(2B)+寄存器數(shù)量(2B)*/ void ModBus_Rsp06(void) {uint8_t Send_Buf[256] = "\0";uint16_t i = 0;u16 index = 0,value = 0;for(i=0; i<16; i++){Send_Buf[i] = RS485_DataBuf.Get_buf[i];}index = Send_Buf[2]<<8 | Send_Buf[3];value = Send_Buf[4]<<8 | Send_Buf[5];if(index == 0x06)//led{if(value == 0x11){LED1_H;LED2_H;}else if(value == 0x01){LED2_H;LED1_L;}else if(value == 0x10){LED2_L;LED1_H;}else{LED1_L;LED2_L;} }if(index == 0x07)//beep{if(value == 0x01)BEEP_ON;else BEEP_OFF;}RS485_Send_Data(Send_Buf,8); } //廣播回應函數(shù) void ModBus_Rsp41(void) {uint8_t Send_Buf[256] = "\0";uint16_t Temp_crc = 0;Slave_ID = RS485_DataBuf.Get_buf[2];Send_Buf[0] = Slave_ID;//自己設備IDSend_Buf[1] = 0X41;Temp_crc = CRC16_Fanction(Send_Buf,2);Send_Buf[2] = (Temp_crc&0XFF);Send_Buf[3] = (Temp_crc>>8);RS485_Send_Data(Send_Buf,4); }

?三、總結(jié)

????????1、rs485總線是一種半雙工通信總線,在同一時間只能接受或發(fā)送,可以掛載多個節(jié)點設備。

????????2、為了解決主機與從機通信的穩(wěn)定性和可靠性,我們規(guī)定了主從模式,同一時刻只能一主一從進行通信。但主機和從機用什么樣的數(shù)據(jù)幀進行通信還沒有確定。

? ? ? ? 3、modbus規(guī)定了主機與從機通信的數(shù)據(jù)格式,相對于自定義的數(shù)據(jù)格式,modbus的兼容性更強。

總結(jié)

以上是生活随笔為你收集整理的rs485与modbus协议的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。