跳到主要内容

RS485 Modbus 从机通信

简介:

下文介绍了如何用 ShineBlink C1 或 C2 开发板作为设备端(Modbus从机)来和上位机 (Modbus 主机) 来通信,并在 ShineBlink 设备端实现了 0x03 功能码(读取多个保持寄存器)和 0x05 功能码(写单个线圈)的程序代码。

一、实现环境

ShineBlink C1 或 C2 开发板设备作为Modbus从机通过RS485总线和上位机通信,我们在电脑上运行知名的Modbus Poll调试软件作为上位机来模拟Modbus主机,Modbus Poll软件可以到其官网上下载。

二、接线图

layout2

三、Modbus从机设备介绍

ShineBlink C1 或 C2 开发板设备作为Modbus网络中的其中一个节点有如下特性:

**串口属性:**19200、N、8、1

**设备地址:**21(0x15)

设备支持的Modbus功能码:

  • 0x05 写单个线圈

  • 0x03 读取多个保持寄存器

功能介绍:

  • 0x05,上位机通过向设备发送0x05功能码,对线圈地址为0x0000的线圈写入值0xFF00时,设备开始运行,

    对线圈地址为0x0000的线圈写入值0x0000时,设备停止运行。

  • 0x03,上位机通过向设备发送0x03功能码,读取保持寄存器起始地址为0x0000的9个保持寄存器(每个保持寄存器值为16bit无符号数据),每个寄存器对应的数据如下:

    寄存器1寄存器2寄存器3寄存器4寄存器5寄存器6寄存器7寄存器8寄存器9
    Pm25HchoTvocMeshTemp1Temp2WindRunFault

    三、Modbus通信实现代码实例

以下代码不仅实现了03和05功能码,并实现了将各种异常情况回复给Modbus主机。

--程序中用到的全局变量定义
Pm25Percent = 0
HchoPercent = 0
TvocPercent = 0
MeshPercent = 0.0
Temprature1 = 0.00
Temprature2 = 0.00
Wind485DisSpeed = 0
DevIsRunning = 0 --控制设备运行或停止
FaultCode = 0 --故障代码

--ModBus通信函数定义
function ModbusProcess()
local sdata = {}

--查询是否收到Modbus主机发来的消息
flag, data = LIB_Uart1Recv()
if flag == 1 then
--判断消息是不是发给本机,是本机的才理会
if data[1] == PI[2] then --PI[2], Modbus本机地址(1-247)
--判断Modbus功能码
if data[2] == 0x05 then -- 0x05 写单个线圈
--这里定义线圈地址为0x0000的线圈为开机/关机控制信号
if data[3] == 0x00 and data[4] == 0x00 then
if data[5] == 0xff and data[6] == 0x00 then --ON
DevIsRunning = 1 --置1开机全局变量
elseif data[5] == 0x00 and data[6] == 0x00 then --OFF
DevIsRunning = 0 --置0开机全局变量
else
--这里需回复非法数据03异常消息(非法数据值),读者可自行完成
end
--回复OK,把收到的数据原封不动回传
LIB_Uart1BlockSend(data)
else
--回复异常消息(非法数据地址)
sdata[1] = data[1] --本机地址
sdata[2] = data[2]+0x80 --异常的时候功能码加0x80
sdata[3] = 0x02 --异常码0x02表示设备不支持此数据地址
CRC = LIB_CrcCalculate("CRC16_MODBUS", sdata)
sdata[4] = CRC & 0x00ff --低位在前
sdata[5] = CRC >> 8 --高位在后
LIB_Uart1BlockSend(sdata)
end
elseif data[2] == 0x03 then --0x03 读多个保持寄存器
--这里定义起始地址为0x0000的这些寄存器存放传感器数据,且读取的寄存器个数必须是9个
if data[3] == 0x00 and data[4] == 0x00 and data[5] == 0x00 and data[6] == 0x09 then
sdata[1] = data[1] --本机地址
sdata[2] = data[2] --功能码
sdata[3] = 18 --数据域字节数: 9个寄存器一共18字节
sdata[4] = Pm25Percent >> 8
sdata[5] = Pm25Percent & 0x00ff
sdata[6] = HchoPercent >> 8
sdata[7] = HchoPercent & 0x00ff
sdata[8] = TvocPercent >> 8
sdata[9] = TvocPercent & 0x00ff
sdata[10] = math.floor(MeshPercent) >> 8
sdata[11] = math.floor(MeshPercent) & 0x00ff
sdata[12] = math.floor(Temprature1) >> 8
sdata[13] = math.floor(Temprature1) & 0x00ff
sdata[14] = math.floor(Temprature2) >> 8
sdata[15] = math.floor(Temprature2) & 0x00ff
sdata[16] = Wind485DisSpeed >> 8
sdata[17] = Wind485DisSpeed & 0x00ff
sdata[18] = DevIsRunning >> 8
sdata[19] = DevIsRunning & 0x00ff
sdata[20] = FaultCode >> 8
sdata[21] = FaultCode & 0x00ff
CRC = LIB_CrcCalculate("CRC16_MODBUS", sdata)
sdata[22] = CRC & 0x00ff --低位在前
sdata[23] = CRC >> 8 --高位在后
--回复传感器数据
LIB_Uart1BlockSend(sdata)
else
--回复异常消息(非法数据地址)
sdata[1] = data[1] --本机地址
sdata[2] = data[2]+0x80 --异常的时候功能码加0x80
sdata[3] = 0x02 --异常码0x02表示设备不支持此数据地址
CRC = LIB_CrcCalculate("CRC16_MODBUS", sdata)
sdata[4] = CRC & 0x00ff --低位在前
sdata[5] = CRC >> 8 --高位在后
LIB_Uart1BlockSend(sdata)
end
else
--回复异常消息(非法功能码)
sdata[1] = data[1] --本机地址
sdata[2] = data[2]+0x80 --异常的时候功能码加0x80
sdata[3] = 0x01 --异常码0x01表示设备不支持此功能码
CRC = LIB_CrcCalculate("CRC16_MODBUS", sdata)
sdata[4] = CRC & 0x00ff --低位在前
sdata[5] = CRC >> 8 --高位在后
LIB_Uart1BlockSend(sdata)
end
end
end
end

--配置Uart1作为485接口,初始默认波特率19200,并且D6作为自动收发切换引脚
LIB_Uart1Rs485Config("BAUDRATE_19200","D6")

--开始大循环
while(GC(1) == true)
do
--Modbus通信处理
ModbusProcess()
end

四、Modbus Poll 上位机实验流程

(1)配置0x03读取保持寄存器功能(Setup)

setup

(2)建立Modbus串行通信连接(Connectiong)

connect

(3)建立连接后的数据通信

1. 上位机每秒钟自动下发0x03指令读取设备的起始地址为0x0000的9个保持寄存器的值

03result

通信数据日志:

03log

上面设备回复的23个字节数据举例说明:

例如:15 03 12 00 06 00 00 00 01 00 00 00 16 00 16 00 00 00 00 00 00 D5 16

数据结尾的CRC算法采用Modbus Crc16

本机地址:0x15
功能码:0x03
字节数:18字节(9个16bit无符号寄存器)
寄存器1:0x0006 表示pm25=6
寄存器2:0x0000 表示hch0=0
寄存器3:0x0001 表示tvoc=1
寄存器4:0x0000 表示mesh=6
寄存器5:0x0016 表示temprature1=21度
寄存器6:0x0016 表示temprature2=21度
寄存器7:0x0000 表示windspeed=0
寄存器8:0x0000 表示running=0
寄存器9:0x0000 表示faultcode=0
CRC_L:0xD5
CRC_H:0x16

2. 上位机下发开机命令(功能码0x05,向线圈地址为0x0000处写单个线圈值0xFF00)

05 write single coil on

通信数据日志:

05log

3. 上位机下发停机命令(功能码0x05, 向线圈地址为0x0000处写单个线圈值0x0000)

上位机发送: 15 05 00 00 00 00 CE DE 设备应回复: 15 05 00 00 00 00 CE DE