MODBUS-RTU协议真不难!用四个核心功能码(01/03/05/06)带你玩转工业通讯(附代码)

在工业控制领域,MODBUS-RTU堪称最通用、最久经考验的通讯协议之一。它就像设备之间的普通话,让不同厂商的仪表、PLC和控制器能够顺畅交流。

今天,我将抛开复杂的理论,直接通过最常用的四个功能码:01030506,结合我在PIC单片机上的实战代码,带你彻底掌握MODBUS-RTU的精髓。

一、 MODBUS-RTU协议极简导读

你可以把MODBUS通讯想象成一场有序的问答

主机(Master 问:“1号从站,你的线圈状态什么情况?

从站(Slave 答:“1号从站回复,我的线圈状态是XXXX

协议帧格式(重中之重):
无论问与答,都遵循以下结构,这是分析和调试所有问题的根本。

从站地址

功能码

数据

CRC校验

1 Byte

1 Byte

N Bytes

2 Bytes

从站地址0为广播地址,1-255为设备地址。

功能码:告诉从站要做什么事。

数据:请求或回复的具体内容。

CRC校验:确保数据在传输过程中没有出错(低8位在前,高8位在后)。

二、 四大核心功能码实战详解

1. 功能码 01Read Coils- 读取线圈状态

功能:读取一组开关量输出(DO)的状态,比如继电器的吸合/释放

主机请求帧(例如:读取1号从站,线圈地址0x0000开始的4个线圈状态)

地址

功能码

起始地址高8

起始地址低8

数量高8

数量低8

CRC16

0x01

0x01

0x00

0x00

0x00

0x04

0x31CA

从站正常回复(假设4个线圈状态为:ON, OFF, ON, OFF

地址

功能码

字节数

数据(线圈状态)

CRC16

0x01

0x01

0x01

0x05 (二进制 0000 0101)

0xE1C8

注意:数据0x05的二进制为0000 0101,从最低位开始看,分别对应线圈1ON2OFF3ON4OFF。若返回的线圈数不是 8 的倍数,则在最后的数据字节中的剩余位至字节的最高位全部填零,字节数区说明全部数据的字节数。

2. 功能码 05Write Single Coil- 强制单个线圈

功能:强制改变一个开关量输出点的状态,非常常用,比如操作某个继电器吸合或释放。

主机请求帧(强制1号从站地址为0x0002的线圈为ON

地址

功能码

输出地址高8

输出地址低8

输出值高8

输出值低8

CRC16

0x01

0x05

0x00

0x02

0xFF

0x00

0x8C3A

注意:强制ON固定为0xFF00,强制OFF固定为0x0000

从站正常回复原样回复主机发送的指令,表示执行成功。

地址

功能码

输出地址高8

输出地址低8

输出值高8

输出值低8

CRC16

0x01

0x05

0x00

0x02

0xFF

0x00

0x8C3A

3. 功能码 03Read Holding Registers- 读取保持寄存器

功能:读取设备参数、采集到的数据等,是使用最频繁的功能码之一。例如读取温度、压力、流量等。

主机请求帧(读取1号从站,起始地址0x00002个寄存器)

地址

功能码

起始地址高8

起始地址低8

数量高8

数量低8

CRC16

0x01

0x03

0x00

0x00

0x00

0x02

0xC40B

从站正常回复(假设两个寄存器的值分别为0x1234, 0x5678

地址

功能码

字节数

数据1高8

数据1低8

数据2高8

数据2低8

CRC16

0x01

0x03

0x04

0x12

0x34

0x56

0x78

0x1FE1

4. 功能码 06Write Single Register- 预置单个寄存器

功能:修改设备的一个参数,如设定目标温度、压力上限等。

主机请求帧(将1号从站地址为0x0001的寄存器值改为0x55AA

地址

功能码

寄存器地址高8

寄存器地址低8

数据高8

数据低8

CRC16

0x01

0x06

0x00

0x01

0x55

0xAA

0x12E4

从站正常回复:同样原样回复主机指令,表示修改成功。

三、 PIC单片机MODBUS-RTU代码

下面是部分主程序:

#include<pic16f1947.h> //PIC16F1947为例

unsigned char address_485=1;//默认从机地址为1

unsigned char recive[100]; //从机接收数据缓冲区100字节

unsigned int crc_hl; //16位两个字节校验码

void calccrc(uchar crcbuf) //校验码算法子程序

{

       uchar i;

       crc_hl=crc_hl^crcbuf;

       for(i=0;i<8;i++)

       {

              uchar TT;

              TT=crc_hl&1;

              crc_hl=crc_hl>>1;

              crc_hl=crc_hl&0x7fff;

              if(TT==1)

              crc_hl=crc_hl^0xa001;

              crc_hl=crc_hl&0xffff;

       }

}

void main()

{

       unsigned char  i,j;

       //初始化

      

       while(1)

       {

              if(x) // x为判断uart成功通讯接收一包数据的条件

              {

                     crc_hl=0xffff;

                            for(i=0; i<6; i++)

                            {

                                   calccrc(recive[i]);

                            }

                            if((recive[0]== address_485)&&(recive[1]==0x03)&&(recive[6]==(crc_hl&0xff))&&(recive[7]==(crc_hl>>8))) //判断地址、功能码及校验码

                            {

                                   //1、判断寄存器地址及填写要回复的数据

                                   //2、校验码计算

                                   //3、把要回复的数据及校验码发送给主机

                                   //如果是功能码0506直接原样把数据发送给主机,不需要2这两步

                                                }                                 

              }

             

       }

}

MODBUS-RTU物理层通常采用RS-485。一个稳定可靠的RS-485电路是通讯的保障。

下图是我一直使用的RS485接口电路图

具体介绍请参考我的另一篇文章:深入解析RS-232RS-485RS422总线与实战防护电路

掌握01030506这四个核心功能码,你就能解决80%以上的MODBUS-RTU应用场景。协议本身不难,关键在于严谨的实现和对现场干扰的防护

如果你对这篇文章感兴趣或有什么问题,欢迎在评论区留言

后续干货不断,咱们一起在单片机的世界里,共同进步。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值