Modbus-RTU协议 & FreeModbus的移植

Modbus-RTU协议 & FreeModbus的移植

1. Modbus-RTU协议

modbus-rtu分为主机(Master)和从机(Slaver)。

主机就是用来读写从机数据的:
它通过发送指令来读写从机的数据;之后,接收从机的返回信息,以评估指令的执行情况。

从机就是用来存储数据的:
只有对应的从机收到了我需要执行动作的指令,才会执行相应的操作,并将执行结果返回给主机。不同的指令,不同的执行情况,返回的信息都不相同。

以上内容概括了Modbus-RTU的通信过程。

Modbus是异步半双工协议,与I2C类似,同一时间只能由单独一端(主机或从机)占用总线

四种数据类型

虽然概括来讲,Modbus-RTU协议就是主机读写各从机数据
那么,主机读写的都是什么类型的数据呢?

Modbus-RTU协议规定,从机有四种数据类型,分为两个大类

第一类是单位为 bit的数据:

线圈 Coils;每个coils对应着一个bit,所有的coils都是可读可写的

离散输入 Discrete input;它也代表着1个bit,只不过它是只读的,这是它和coils的区别。

第二类是单位为word(2个字节)的数据:

保持寄存器 Holding register;可读可写

输入寄存器 Input register;只读
从上面来看:带有“register”字样的数据类型,是以word为单位的,否则是以bit为单位的
带有“input”字样的,是只读类型

Modbus-RTU的指令

既然已经明确了读写的数据类型,那么具体操作是怎样的呢?或者说发送、返回指令的格式?
这一部分网上的内容众说纷纭。为了保证协议符合标准,我们最好参考官方Modbus协议规范
(虽说是英文文档,但四级水平足矣^_^

文档中非常详细地定义了各种指令的具体格式。借此即可确定下来各个指令的详细内容,进而
使得从机执行特定的操作。下面仅举一例——怎样令主机读取001号从机的第5-第14线圈的内容?

想令主机读取1号从机,地址码即为0x01;0x01就是发送指令的首个字节。

第二个字节是功能码。现在使用读取从机线圈的功能,功能码即为0x01

第3、4字节是起始地址(2个字节的地址)。我们从5号线圈开始,即起始地址是0x0004(每个从机的线圈编号是从1开始的,但对应地址是从0开始,即地址比编号-1)。因此0x000x04是发送指令的第3、4字节。

第5、6字节是读取的线圈数,这里是10个,即0x000A。因此0x000x0A分别是发送指令的第5、6字节。

第7、8字节是CRC校验码1,根据前面所有字节的内容,通过计算,可得CRC校验码为0xFDCC;
因此0xFD0xCC是发送指令的最后2个字节。

至此可确定主机发送指令的全部内容:

01010004000AFDCC
从机地址功能码起始地址H起始地址L数量H数量LCRC校验HCRC校验L

由此,主机通过总线将此指令发给所有的从机
指令的收发建议使用串口完成。且每帧(即每个指令)之间至少要有3.5Byte传输时间的间隔(即传输28个bit所需的时间长度),用于区分不同的帧。这个间隔是标准协议的规定,我们需要遵守。

现在假设每个从机都已经完整无误地收到了上面的指令,那么所有的从机都会执行这个命令吗?然后它们都会向主机返回各自的执行情况吗?

不,从机会将指令的地址码0x04和自身的地址码匹配。只有两者相同时,或者指令中的地址码是广播地址0x00时,从机才会响应指令。

对于地址码是0x00的广播指令,由于所有的从机都会执行这一命令,因此它们都不会向主机返回任何信息,避免出现错误。所以广播指令适合写从机,而不适合读从机。

如果从机正确地执行了读线圈的命令,将返回怎样的结果呢?或者说返回帧是怎样的呢?

按照官方标准,返回帧的首字节是从机地址,即0x01
第2字节是功能码,即0x01

第3字节是读取的字节数。由于读取了10个线圈,且按协议标准,不足1字节,按1字节计算,不足的部分补零。因此,字节数是2,即0x02

第4、5字节是读取的具体内容。假设001号从机的0-15线圈的内容是0010 0101 0110 1011。

现暂定从机以uint8_t数组的形式存储线圈的内容,即缓存区的内容为Coils_Buf[0] = 0xA4, Coils_Buf[1] = 0xD6(这里要注意缓存区每个字节的低二进制位对应线圈的低地址,高二进制位对应高地址;不理解的话可以将缓存区的内容展开为二进制位,和线圈的内容对比一下)。

现在要将第4-第13个二进制位在返回帧中以2个字节显示,即0110 1010(地址11 - 4的线圈内容),000000 01(高6位的补零&地址是13 - 12的线圈内容)。因此,
第4、第5字节分别是0x6A0X01

最后2个字节是CRC校验位。与之前一样的方法,可得此帧的CRC校验字节是0x560x9C

至此,返回帧的全部内容已被确定:

0101026A02569C
从机地址功能码字节数内容L内容HCRC校验HCRC校验L

从机会在数据读取完毕后将此帧向主机返回,主机由此读到了从机线圈的内容。至此,01H指令运行完毕。

2. FreeModbus的移植

什么是FreeModbus这里不再赘述。

本文基于Github上的FreeModbus
程序中使用了RT-Thread操作系统

移植过程时将Modbus协议栈复制到自己的工程模板下即可,后序的操作可以参照其附带的说明。
在收发指令时,最好通过串口+DMA的方式一次性地收发整个帧,而不是原程序中逐个字节地收发。否则所发送的帧容易被理解成多个单字节的帧,导致通信失败。

#endif


  1. 这里的CRC校验码是对前面所有字节进行特定的计算而得出的,用于检测数据是否无失真地传输。从机收到指令时,会对指令再次进行CRC计算。一旦两次计算的结果不同,说明指令在传输过程中发生了错误。 ↩︎

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32是一种非常受欢迎的微控制器,广泛应用于各种嵌入式系统。在FreeRTOS系统下使用STM32的串口DMA接收方式对接FreeModbus-RTU协议栈,可以实现高效的数据传输,提高系统性能。下面分别从串口DMA和FreeModbus-RTU两部分介绍。 串口DMA是一种直接内存访问技术,能够实现数据的高效传输,极大地提高了系统性能。在STM32中,串口DMA接收方式可以实现在接收数据时不需要CPU干预,将接收到的数据直接存储到指定的内存区域中。这种方式可以大大降低CPU的负载,提高系统的并发处理能力。 FreeModbus-RTU是一个广泛应用于工控系统的通信协议栈,具有易于移植、高效、可靠等优点。通过STM32的串口DMA接收方式对接FreeModbus-RTU协议栈,可以实现快速、高效的通信。具体实现过程中,需要根据FreeModbus-RTU协议的规则进行数据包的解析和封装。在串口DMA接收到数据后,可以通过设置相关的中断来触发数据的解析和封装。 需要注意的是,在接收数据时,由于数据包的长度是不确定的,因此需要设置合适的缓冲区大小。同时,在封装数据包时,需要按照FreeModbus-RTU协议的规则进行封装,并且需要考虑到异步通信时数据包的压缩问题,以提高通信效率。 综上所述,通过STM32的串口DMA接收方式对接FreeModbus-RTU协议栈,可以实现高效、可靠的通信,并且可以在保证系统性能的同时提高通信效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值