一、modbus介绍
modbus是电气领域常用的通讯协议,关于modbus协议的介绍资料很多,这次的重点不是modbus协议具体介绍,这里我就不重复赘述,推荐看下面这个文章,讲解的很清晰。
https://blog.csdn.net/xiaoluoshan/article/details/73233955
modbus是一个一对多的协议,即可以一主机,多从机。所有的通讯必须由主机发起,从机根据主机的命令,做出对应的响应,返回有效数据给到主机。比如主机想要读取从机1的保持寄存的数据,那主机必须先发送读取读取指令,这个指令必须包括从机1的地址,而这条指令只有从机1才能解析,做出响应,这一点其实和iic协议很相似。但是要注意的是,只要是挂载在总线上的从机设备,都能共享总线上的指令,只是不能做出对应的响应,这一点很关键。
现在我们知道了modbus所有通讯都必须由主机发起,这样不难发现modbus缺陷:从机和从机之间无法直接建立通讯,都必须通过主机来实现。
二、使用监听的方式实现从机和从机的通讯
前段时间,因为项目需要,需要利用modbus协议来实现从机和从机之间的通讯。其中pc上位机来做主机,通过RS485协议总线,与多台从机设备进行通讯,使用的通讯协议就是modbus。其大致框架如下:
其中pc上位机会不断下发读保持寄存器的指令到各个从机,从机返回响应。原本的方案是pc将设备1的数据读上来,再下发到设备2。但是pc的上位机程序已经固定,下发指令固定,不能进行修改,这个方案就放弃了。于是就想出下面的方案,即使用设备2监听总线上的数据,发现是设备1返回到pc的指令,就将改指令解释出来,从而获取设备1的数据。
因为pc下发的是读取保持寄存器的指令,从寄存器地址0开始,一次读取读取二十个。
下发的报文如下:
01 03 00 00 00 14 45 C5
设备地址1返回的报文
01 03 28 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 8A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3A 37
我们要解析的就是地址1返回的报文。
上面说过,所有的设备都能共享总线上的指令,即设备1对pc做出的响应,其实设备2也能接收到这个指令,只是modbus协议本身不会对它进行解释。我们要做的就是自己编写代码,接收并解析这条指令,从中获得我们想要的数据。
可以看到这两条报文开始两个直接都是设备地址和功能码,第三个直接就区分开了。我们可以对照modbus 03指令的功能码:
我们只需要对第三字节进行判断。如果第三个字节是返回的字节数,那对应的就是03指令的响应,即设备1对pc做出的响应,其后面就包含有效数据。再根据modbus格式,将数据解析出来即可。
void monitorData(void)
{
if(UartToPC_Obj.deviceAddr == 2) //监听pc通讯
{
//这里开始就是对指令进行区分
if( UartToPC_Obj.rxBuffer[0] == 0x01) //设备地址
{
/*监听保持寄存器*/
if((UartToPC_Obj.rxBuffer[1] == 0X03) && ((UartToPC_Obj.rxBuffer[2] >> 1) == 20) ))
{
UartToDD_Obj.holdRegisters[0][27] = UartToPC_Obj.rxBuffer[3 + 2 * 2] << 8 | UartToPC_Obj.rxBuffer[4 + 2 * 2];
}
}
}
}
这行代码就是获取modbus响应指令中的第二个寄存器的数据:
UartToPC_Obj.rxBuffer[3 + 2 * 2] << 8 | UartToPC_Obj.rxBuffer[4 + 2 * 2];
当然这只是个简单的提取,其实代码中还加入了很多有限数据的筛选判断,进一步提高提取数据的正确率,这就要结合具体的项目进行分析。
三、使用监听方法的缺点
因为挂载在总线的设备很多,其中有很多相似的数据,很难区分开,目前使用的利用读取寄存器数量的方式也存在明显的缺陷,如果遇到恰好也是读一样数量的寄存器,就得加入其它筛选机制。目前也没有想到更好的区分方式,有好的方法,可以留言分享。