关于MODBUS协议和RS485硬件电路的介绍


前言

Modbus 是属于工业上常用的通讯协议,包括RTU、ASCII、TCP,其中MODBUS-RTU协议最常用,比较简单,在32上很容易实现。就目前来说,作者在实际应用中只接触到了这种,所以以下的讲解也是根据MODBUS-RTU协议来说的,下面我会附上介绍及代码供有需要的伙伴们参考。


一、RS485

1.介绍

要想了解MODBUS协议,需要先接触一下RS485硬件电路。
首先了解什么是单工通讯,单工通讯是指数据只能朝着一个方向传输的通讯方式。而半双工通讯则是指对于通讯两端,不能同时对对方发送数据,必须错开时间段发送。 RS-485的通讯线只有2条,且这两条通讯线在一次传输中都需要用到,因此485只可实现半双工通讯。
485通讯实际上是把MCU出来的TTL电平通过硬件层的一个转换器芯片进行转换,这个芯片通常是MAX485芯片。
把MCU出来的一条的TTL信号经过芯片转换为两根线(线A、线B)上的信号。当MCU给转换器输入低TTL电平时,转换器会使得B的电压比A的电压高,反之,A的电压比B的电压高(通过AB线之间的电压差来判断是高低电平)

在这里插入图片描述

2.两者之间的联系

RS485实现半双工通讯,会遇到一个问题,MCU1向MCU2发数据时,并不知道线上是否正传来MCU2数据,因为没有其他线可用来判断对方的收发状态,那么可能也会导致数据冲突。因此,RS-485要实现半双工通讯,就需要上层的软件协议加以规约,也就是做到”不能你想发数据就发数据”。可以理解,软件层协议就好像交通规则,它能让数据有序传输。而RS-485对应的软件层协议最多使用的是MODBUS协议,只有软件和硬件相互配合,才能让数据有序的传输。可以这样进行比喻,RS485相当于一条马路,可以让好多车经过,但是为了不出现交通事故,就需要制定一下交通规则,而MODBUS协议就是针对于在RS485这条马路上保证车辆正常行驶的交通规则。网上有许多关于RS485协议的说法,其实是不准确的,RS485只是硬件电路,需要上层的软件协议来支持,而软件协议就是MODBUS协议,这就是我所理解的两者之间的联系。

二、通信格式

1.MODBUS协议

接触了RS485之后,现在可以了解MODBUS协议。RS-485上的软件层协议ModBus主要依赖于主从模式。主从模式是指在半双工通讯方式上,2个或者2个以上的设备组成的通讯系统中,支持多从机的方式:
(1) 至少且只有一个主机,其他的都是从机
(2) 不管任何时候,从机都不能主动向主机发送数据
(3) 主机具有访问从机的权限,从机不可以主动访问从机,任何一次数据交换,都要由主机发起
(4)不管是主机还是从机,系统一旦上电,都要把自己置于接收状态(或者称为监听状态)
主从机的数据交互,需要:
a. 主机将自己转为发送状态
b. 主机按照预先约定的格式发出寻址数据帧。

数据帧是由我们买的适用于MODBUS协议的模块所提供的,不同的模块对应的功能码不太一样,但是格式都是一样的,功能码有许多,这里我不再进行介绍,具体格式如下:例如我使用功能码04,进行数据查询


主机发送命令帧
在这里插入图片描述
不同的模块对应不同的功能码,CRC高字节和CRC低字节也是通过CRC校验的算法求解出来的。

说明:从机地址是厂家生产的模块就给定的,不过有修改地址的功能码,寄存器地址是厂家规定好的地址,寄存器数量是你需要采集几项数据,例如我这里需要采集温度和湿度两项数据,会用到两个寄存器,所以寄存器数量为02,CRC是计算出来的,下面会给出代码。

从机响应的数据帧

在这里插入图片描述
说明:这里写了两个数据,每个数据两个字节,所以字节数是04,数据1和数据2就是支持MODBUS协议的模块所给你的主机返回的数据,CRC校验会根据前面的数据得出校验值。

这里给出CRC校验的算法,CRC校验也分为好几种,由于MODBUS协议规定的格式最后面校验值时两个字节,所以用CRC16算法。

CRC校验代码如下:

	#ifdef _CRC16
	u8 _flash chCRCHTalbe[]={
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
	0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
	0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40};
	 
	u8 _flash chCRCLTalbe[] = {
	0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
	0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
	0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
	0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
	0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
	0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
	0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
	0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
	0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
	0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
	0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
	0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
	0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
	0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
	0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
	0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
	0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
	0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
	0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
	0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
	0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
	0x41, 0x81, 0x80, 0x40};
	 
	u16 GetCRC16(u8* pchMsg, u16 wDataLen)
	{
	    u8 chCRCHi = 0xFF; // 高CRC字节初始化
	    u8 chCRCLo = 0xFF; // 低CRC字节初始化
	    u16 wIndex;         // CRC循环中的索引
	     
	    while (wDataLen--)
	    {
	         // 计算CRC
	         wIndex = chCRCLo ^ *pchMsg++ ;
	         chCRCLo = chCRCHi ^ chCRCHTalbe[wIndex];
	         chCRCHi = chCRCLTalbe[wIndex] ;
	    }   
	    return ((chCRCHi << 8) | chCRCLo) ;
	}

说明:直接调用此函数就行 u16 GetCRC16(u8* pchMsg, u16 wDataLen)

2.通信代码

此处通信以串口3为例,功能码为04,对数据进行查询,发送代码如下:

	void send()
	{
	  u8 array[8];
	  
	  array[0]=0x01;
	  array[1]=0x04;
	  array[2]=0x00;
	  array[3]=0x01;
	  array[4]=0x00;
	  array[5]=0x02;
	  array[6]=0x20;
	  array[7]=0x0b;
	  RS485_Send_Data(array,8);//发送MODBUS指令
	
	}
	void RS485_Send_Data(u8 *buf,u8 len)
	{
	     u8 t;
		 RS485_ON;//开始发送数据的时候把两个引脚拉高,发完之后再拉低
		 delay_ms(1);
		 for(t=0;t<len;t++)
		 {
		   USART_SendData(USART3,buf[t]);
		   while(USART_GetFlagStatus(USART3,USART_FLAG_TC)!=SET);//分号不能去掉,要不然发送数据不成功
		   USART_ClearFlag(USART3,USART_FLAG_TC);
		 }
		 delay_ms(1);
		 RS485_OFF;
	}

剩下的就是当接收到数据之后,对模块返回的数据进行处理,其中包括对数据的CRC校验是否正确,判断是哪一条指令或者你想实现更多的操作都可以添加进去。

总结

到此关于MODBUS协议和RS485硬件电路的介绍就结束了,其中关于协议里面的功能码网上有好多,具体实现的功能也都有介绍,可以根据上面的格式来。关于RS485实际的硬件电路网上也有现成的,我这里设计的时候也是用的网上的,这里只对电路介绍了一下,要是想深入了解还是需要亲自去查相关电路设计。好了,最后附图一张:
在这里插入图片描述

  • 10
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暖阳 (*◎v◎*) 

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值