前言
MODBUS协议是一种工业领域的串行通信协议,由 Modicon(现隶属于施耐德电气)于 1979 年开发,主要用于工业自动化系统中控制器与远程设备之间的通信。它具有结构简单、开源免费、跨平台兼容的特点,是目前常用的应用层通信协议。
MODBUS协议是OSI模型第 7 层上的应用层报文传输协议,通信方式分为主从通信,主机发送协议指令,从机收到指令之后产生应答,如果没有应答,主机会再次发送协议指令,直到超时,即停止发送。
这里对MODUBUS协议的格式及协议通信框架做一下总结,便于复用。
一、协议格式
MODBUS协议和自定义的协议基本差不多,是一种数据传输格式,也是由起始帧、数据帧、校验帧等组成,只不过Modbus协议更为规范。
MODBUS协议格式:从机地址+功能码+数据+校验码(CRC16)。
MODBUS协议帧格式如下图:

地址域:从机的地址,范围:1~255,1个地址表示一个设备;
功能码:有03,04,06,0x10等功能码,03/04:表示读数据功能码,06:表示写单个寄存器数据,0x10:表示写多个寄存器数据;
数据:主机发送协议中数据表示寄存器,从机应答协议中的数据表示上报的数据;
校验码:对地址域、功能码、数据这些进行CRC16校验,校验值低位在前,高位在后。
二、举例
2.1 03H,04H命令
------读输入寄存器。
主机请求数据:

从机返回数据:

2.2 06命令
------ 写单个寄存器
主机下发数据:

从机返回数据:

三、代码交互框架
3.1 主机发送读数据框架
主机发送数据设计为一个状态机,每个状态计时时间为150ms。
主机发送请求读数据之后,等待接收应答数据,当超过150ms没有收到应答数据时,计数变量加1,连续加到4时,判断为接收数据超时,通信异常标志位置1。
代码实现如下:
void pri_mppt_send(void)
{
static uint8_t su8MpptState = 1;
switch(su8MpptState)
{
case 1:
PS_MpptComm_Send_Soc();
HAL_UART_Transmit(&huart4,(uint8_t*)&g_su8MpptSendCmd, sizeof(g_su8MpptSendCmd),100);
MODBUS_CLR_BIT(SysTimHandle.g_su16SystemTimFlag,SYSTEM_150MS_FLAG);
SysTimHandle.g_su16SysTim150ms = 0;
// pri_mppt_clr_tim_2s(); //调试
su8MpptState = 2;
break;
case 2:
if(MODBUS_GET_BIT(SysTimHandle.g_su16SystemTimFlag,SYSTEM_150MS_FLAG))
// if(MODBUS_GET_BIT(SysTimHandle.g_su16SystemTimFlag,SYSTEM_2S_FLAG)) //调试
{
g_su8MpptSendCnt++;
if(g_su8MpptSendCnt >= 4) //通信超时计数14:2.1s超时
{
su8MpptState = 3;
}
else
{
su8MpptState = 1;
}
}
break;
case 3: //MPPT通信异常,故障码E40
MpptFltFlag.Bit.g_ubPVCommFault = 1;
SysIsFlt.g_sbMpptIsFlt = 1;
su8MpptState = 1;
g_su8MpptSendCnt = 0;
break;
default:
break;
}
}
3.2 主机接收应答数据框架
主机收到应答数据之后,首先判断是否接收完成?然后再判断接收的数据长度,接收数据的地址,接收数据的功能码,全都没问题,则进行CRC16位数据校验,校验也没问题,最后就可以处理数据。
代码实现如下:
//返回数据
void pri_mppt_resp(void)
{
if(MpptDataHandle.g_sbUartRxOkFlag)
{
MpptDataHandle.g_sbUartRxOkFlag = 0;
if((MpptDataHandle.g_su8RxCnt != 12) && (MpptDataHandle.g_su8RxBuf[0] != 1) && (MpptDataHandle.g_su8RxBuf[1] != 3) && MpptDataHandle.g_su8RxBuf[2] != 6)
{
MpptDataHandle.g_su8RxCnt = 0;
return;
}
PS_AlgorithmCalc_Crc_Check(MpptDataHandle.g_su8RxBuf,MpptDataHandle.g_su8RxCnt-2);
if(su16CrcDate == (MpptDataHandle.g_su8RxBuf[MpptDataHandle.g_su8RxCnt-1]<<8)+MpptDataHandle.g_su8RxBuf[MpptDataHandle.g_su8RxCnt-2])
{
g_su8MpptSendCnt = 0;
MpptFltFlag.Bit.g_ubPVCommFault = 0;
SysIsFlt.g_sbMpptIsFlt = 0;
......
MpptDataHandle.g_su8RxCnt = 0;
memset(MpptDataHandle.g_su8RxBuf, 0, sizeof(MpptDataHandle.g_su8RxBuf));
}
else
{
MpptDataHandle.g_su8RxCnt = 0;
}
}
}
四、总结
MODBUS协议交互应用起始蛮简单的,使用过一次熟悉之后,后面基本都是复制粘贴,代码都可以拿来复用。
有几个点需要注意下:
-发送数据超时,数据不能一直发送,要设计超时机制;
-接收数据判断,根据协议格式逐个判断数据的正确性,这样更严谨。