01 MODBUS RTU

目录

一、概念

二、功能码0x01-读取线圈中的数据

三、功能码0x03读保持寄存器的数据

四、功能码0x06向一个寄存器中写入数据

五、功能码0x10-多个寄存器写入数据 

六、ModBus异常功能码

七、ModBus异常错误码表 

八、主机接收从机所有数据合并函数


一、概念

        modbus是一个公开免费的协议,广泛应用于工业控制领域(PLC和仪器,PLC和PLC,PLC和上位机,PLC和触摸屏等等,其中PLC是可控制逻辑单元)

        他有两种物理接口(硬件协议),一个是串口(RS232,RS485,RS422),一个是以太网。串口主要用于modbus RTU或者是modbus ascii模式,而以太网主要用于modbus tcp协议。

        一般的通信方式是:主机广播或者单播发送指令,从机分析请求,并且给主机应答(如果出错就返回异常功能码)。从机只能响应主机,不能主动发送数据

1.1 数据类型

1.2 功能码

 

二、功能码0x01-读取线圈中的数据

主机发送,读线圈数据(注意:数据以16进制格式发送)

从机地址功能码寄存器起始地址高字节寄存器起始地址低字节寄存器数量高字节寄存器数量低字节CRC校验高字节CRC校验低字节

从机响应 (注意:接收数据也是16进制)

从机地址功能码返回数据长度(一个字节,最多是255个数据)数据CRC校验高字节CRC校验低字节

举例:

主机:01 01 00 00 00 08 3D CC

从机:01 01 01 21 91 90

三、功能码0x03读保持寄存器的数据

 typedef struct 
 {
 	//作为从机时使用
  	 u8  myadd;        //本设备从机地址
 	u8  rcbuf[100];   //modbus接受缓冲区
 	u8  timout;       //modbus数据持续时间
 	u8  recount;      //modbus端口接收到的数据个数
 	u8  timrun;       //modbus定时器是否计时标志
 	u8  reflag;       //modbus一帧数据接受完成标志位
 	u8  sendbuf[100]; //modbus接发送缓冲区
 	
 	//作为主机添加部分
 	u8 Host_Txbuf[8];	//modbus发送数组	
 	u8 slave_add;		//要匹配的从机设备地址(做主机实验时使用)
 	u8 Host_send_flag;//主机设备发送数据完毕标志位
 	int Host_Sendtime;//发送完一帧数据后时间计数
 	u8 Host_time_flag;//发送时间到标志位,=1表示到发送数据时间了
 	u8 Host_End;//接收数据后处理完毕
 }MODBUS;
 //参数1从机地址,参数2起始地址,参数3寄存器个数
 void Host_send03(uint8_t slave,uint16_t StartAddr,uint16_t num)
 {
 	int j;
 	uint16_t crc;//计算的CRC校验位
 	modbus.slave_add=slave;//这是先把从机地址存储下来,后面接收数据处理时会用到

 	modbus.Host_Txbuf[0]=slave;//这是要匹配的从机地址
 	modbus.Host_Txbuf[1]=0x03;//功能码
 	modbus.Host_Txbuf[2]=StartAddr/256;//起始地址高位
 	modbus.Host_Txbuf[3]=StartAddr%256;//起始地址低位
 	modbus.Host_Txbuf[4]=num/256;//寄存器个数高位
 	modbus.Host_Txbuf[5]=num%256;//寄存器个数低位
 	crc=Modbus_CRC16(&modbus.Host_Txbuf[0],6); //获取CRC校验位
 	modbus.Host_Txbuf[6]=crc/256;//CRC校验高位
 	modbus.Host_Txbuf[7]=crc%256;//CRC校验低位
 	
 	//开始发送数据
 	RS485_TX_ENABLE;//使能485控制端(启动发送)  
 	for(j=0;j<i;j++)
 	{
 	 Modbus_Send_Byte(modbus.sendbuf[j]);
 	}
 	RS485_RX_ENABLE;//失能485控制端(改为接收)
 }
 //主机接收从机的消息进行处理功能码0x03
 void HOST_receive03()
 {
 	u16 crc,rccrc;//计算crc和接收到的crc
 
   if(modbus.reflag == 0)  //如果接收未完成则返回空
 	{
 	   return;
 	}
 	
 	//(数组中除了最后两位CRC校验位其余全算)
 	crc = Modbus_CRC16(&modbus.rcbuf[0],modbus.recount-2); //获取CRC校验位
 	rccrc = modbus.rcbuf[modbus.recount-2]*256+modbus.rcbuf[modbus.recount-1];//计算读取的CRC校验位
 	
 	if(crc == rccrc) //CRC检验成功 开始分析包
 	{	
 	   if(modbus.rcbuf[0] == modbus.slave_add)  // 检查地址是是对应从机发过来的
 		 {
 			 if(modbus.rcbuf[1]==3)//功能码时03
             {
 	            int i;
 	            int count=(int)modbus.rcbuf[2];//这是数据个数
 	            printf("从机返回 %d 个寄存器数据:\r\n",count/2);
 	            for(i=0;i<count;i=i+2)
 	            {
 		            printf("data%d= %d\r\n",i+1,(int)modbus.rcbuf[4+i]+((int)modbus.rcbuf[3+i])*256);
 	            }
             }
         }

 	}
 		 
}	
 	 modbus.recount = 0;//接收计数清零
    modbus.reflag = 0; //接收标志清零
 }

四、功能码0x04读输入寄存器数据

数据的请求和响应格式   没有加上设备地址和CRC校验

五、功能码0x06写单个线圈寄存器

        写单个线圈寄存器。FF00H 值请求线圈处于 ON 状态,0000H 值请求线圈处于 OFF 状态。

主机发送:从机地址 0x01 功能码 0x05 寄存器地址高字节 0x00 寄存器地址低字节 0x00 数据高字节0xFF    数据低字节 0x00 CRC 校验高字节  CRC 校验低字节

从机接收:接收内容同发送

六、功能码0x06向一个寄存器中写入数据

 void Host_send06(uint8_t slave,uint16_t Addr,uint16_t data)
 {
 	uint16_t crc,j;//计算的CRC校验位

 	modbus.slave_add=slave;//从机地址赋值一下,后期有用

 	modbus.Host_Txbuf[0]=slave;//这是要匹配的从机地址
 	modbus.Host_Txbuf[1]=0x06;//功能码
 	modbus.Host_Txbuf[2]=Addr/256;//写入寄存器地址高位
 	modbus.Host_Txbuf[3]=Addr%256;//写入寄存器低位
 	modbus.Host_Txbuf[4]=data/256;//写入数据高位
 	modbus.Host_Txbuf[5]=data%256;//写入数据低位
 	crc=Modbus_CRC16(&modbus.Host_Txbuf[0],6); //获取CRC校验位
 	modbus.Host_Txbuf[6]=crc/256;//CRC校验高位
 	modbus.Host_Txbuf[7]=crc%256;//CRC校验低位

 	//开始发送数据
 	RS485_TX_ENABLE;//使能485控制端(启动发送)  
 	for(j=0;j<i;j++)
 	{
 	 Modbus_Send_Byte(modbus.sendbuf[j]);
 	}
 	RS485_RX_ENABLE;//失能485控制端(改为接收)
 }
 //从机返回数据
 void Host_receive06()
 {
 	int crc,rccrc;

 	crc = Modbus_CRC16(&modbus.rcbuf[0],6); //获取CRC校验位
 	rccrc = modbus.rcbuf[6]*256+modbus.rcbuf[7];//计算读取的CRC校验位

 	if(crc == rccrc) //CRC检验成功 开始分析包
 	{	
 	   if(modbus.rcbuf[0] == modbus.slave_add)  // 检查地址是是对应从机发过来的
 		 {
 			 if(modbus.rcbuf[1]==6)//功能码时06
 			 {
 						printf("地址为 %d 的从机寄存器 %d 中写入数据 %d \r\n ",(int)modbus.rcbuf[0],(int)modbus.rcbuf[3]+((int)modbus.rcbuf[2])*256,(int)modbus.rcbuf[5]+((int)modbus.rcbuf[4])*256);
 			 }
 		 }
 	}	
 }

 七、功能码0x10-多个寄存器写入数据

        理解加模仿,然后自己写功能吗0x10的代码

八、ModBus异常功能码

九、ModBus异常错误码表 

主机发送:01 03 00 00 00 3C CRCH CRCL

 从机响应:01 83 02 CRCH CRCL

此例子就说明了功能码03出了问题,可能是数据地址问题。

十、主机接收从机所有数据合并函数

 void Modbus_receiveAll()
 {
 	u16 crc,rccrc;//crc和接收到的crc

    if(modbus.reflag == 0)  //如果接收未完成则返回空
    {
 	   return;
 	}

 	crc = Modbus_CRC16(&modbus.rcbuf[0],modbus.recount-2); //获取CRC校验位
 	rccrc = modbus.rcbuf[modbus.recount-2]*256+modbus.rcbuf[modbus.recount-1];//计算读取的CRC校验位

 	if(crc == rccrc) //CRC检验成功 开始分析包
 	{	
 	   if(modbus.rcbuf[0] == modbus.myadd)  // 检查地址是否时自己的地址
 		 {
 		   switch(modbus.rcbuf[1])   //分析modbus功能码
 			 {
 				 case 3:      //处理读保存寄存器的数据的代码     break;
                 case 6:      //对应代码                       break;
 				 case 16:     //对应代码                       break;
 			 }
 		 }
 		 else if(modbus.rcbuf[0] == 0) //广播地址不予回应
 		 {
 		    
 		 }	 
 	}	
 	modbus.recount = 0;//接收计数清零
    modbus.reflag = 0; //接收标志清零
 }

十一、Modbus通信失败原因

        当从节点设备向主节点设备发送请求时,从节点希望一个正常响应。从主节点询问中出 现
下列四种可能事件之一:
        1) 如果从节点设备接收到无通信错误的请求,并且可以正常地处理询问,那么从节点设 备
将返回一个正常响应 ;
        2) 如果由于通信错误,从节点没有接收到请求,那么不能返回响应。主节点程序将最 终
处理请求的超时状态 ;
        3) 如果从节点接收到请求,但是检测到一个通信错误(奇偶校验、LRC、CRC、...), 那
么不能返回响应。主节点程序将最终处理请求的超时状态 ;
        4) 如果从节点接收到无通信错误的请求,但不能处理这个请求(例如,如果请求读一 个
不存在的输出或寄存器),从节点将返回一个异常响应,通知用户错误的本质特性; 异常
响应报文有两个与正常响应不同的域:
        功能码域:在正常响应中,从节点利用响应功能码域来应答最初请求的功能码。所有功 能
码的最高有效位(MSB)都为0(它们的值都低于128)。在异常响应中,从节点设置功能 码的
MSB为1。这使得异常响应中的功能码值比正常响应中的功能码值高128。

  • 14
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Modbus RTU是一种常用的串行通信协议,用于在工业自动化领域中,实现设备之间的数据交换。在Modbus RTU协议中,功能用于指示通信从站执行的操作类型。 功能01表示读取线圈状态,主站发起该命令后,从站会返回一组线圈状态数据。线圈状态用二进制位表示,每个位代表一个线圈的状态,0表示关闭,1表示打开。该命令适用于读取数字量输入、开关量输入等状态信息。 执行功能01的命令时,主站会发送以下几个字段的数据给从站: 1. 从站地址:用于指定需要读取线圈状态的从站设备。 2. 功能:用于标识执行读取线圈状态操作的命令。 3. 起始地址:指定了读取线圈状态的起始地址值,从该地址开始连续读取线圈状态数据。 4. 所读取的线圈数量:指示了需要读取的线圈数量。 从站收到主站发送的命令后,会执行相应的操作,并返回包含线圈状态数据的响应报文给主站。响应报文包含以下几个字段的数据: 1. 从站地址:与主站发送的从站地址相同,用于确认响应报文是给哪个主站的。 2. 功能:与主站发送的功能相同,用于确认响应报文是对应读取线圈状态操作的。 3. 字节数:用于表示响应报文中所包含的数据字节数。 4. 线圈状态数据:实际的线圈状态数据,与主站发送的读取线圈数量一致。 功能01Modbus RTU协议中扮演着重要的角色,通过使用该功能,可以实现读取设备的状态信息,便于监测和控制设备的运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值