#include "stdlib.h"
#include "string.h"
#include "port.h"
#include "mb.h"
#include "mbconfig.h"
#include "mbframe.h"
#include "mbproto.h"
#include "mbfunc.h"
#include "mbport.h"
#if MB_SLAVE_RTU_ENABLED == 1
#include "mbrtu.h"
#endif
#if MB_SLAVE_ASCII_ENABLED == 1
#include "mbascii.h"
#endif
#if MB_SLAVE_TCP_ENABLED == 1
#include "mbtcp.h"
#endif
#ifndef MB_PORT_HAS_CLOSE
#define MB_PORT_HAS_CLOSE 0
#endif
static UCHAR ucMBAddress; //Modbus从机地址
static eMBMode eMBCurrentMode; //Modbus模式,eMBMode之一
static enum
{
STATE_ENABLED,
STATE_DISABLED,
STATE_NOT_INITIALIZED
} eMBState = STATE_NOT_INITIALIZED;
static peMBFrameSend peMBFrameSendCur; //指向 eMBRTUSend()
static pvMBFrameStart pvMBFrameStartCur; //指向 eMBRTUStart()
static pvMBFrameStop pvMBFrameStopCur; //指向 eMBRTUStop()
static peMBFrameReceive peMBFrameReceiveCur;//指向 eMBRTUReceive()
static pvMBFrameClose pvMBFrameCloseCur; //指向 vMBPortClose()
BOOL( *pxMBFrameCBByteReceived ) ( void ); //指向 xMBRTUReceiveFSM() 串口接收回调状态机
BOOL( *pxMBFrameCBTransmitterEmpty ) ( void ); //指向 xMBRTUTransmitFSM() 串口发送回调状态机
BOOL( *pxMBPortCBTimerExpired ) ( void ); //指向 pxMBPortCBTimerExpired() 定时器中断回调
BOOL( *pxMBFrameCBReceiveFSMCur ) ( void ); // 未使用
BOOL( *pxMBFrameCBTransmitFSMCur ) ( void ); // 未使用
static xMBFunctionHandler xFuncHandlers[MB_FUNC_HANDLERS_MAX] = {
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED > 0
{MB_FUNC_OTHER_REPORT_SLAVEID, eMBFuncReportSlaveID},
#endif
#if MB_FUNC_READ_INPUT_ENABLED > 0
{MB_FUNC_READ_INPUT_REGISTER, eMBFuncReadInputRegister},
#endif
#if MB_FUNC_READ_HOLDING_ENABLED > 0
{MB_FUNC_READ_HOLDING_REGISTER, eMBFuncReadHoldingRegister},
#endif
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0
{MB_FUNC_WRITE_MULTIPLE_REGISTERS, eMBFuncWriteMultipleHoldingRegister},
#endif
#if MB_FUNC_WRITE_HOLDING_ENABLED > 0
{MB_FUNC_WRITE_REGISTER, eMBFuncWriteHoldingRegister},
#endif
#if MB_FUNC_READWRITE_HOLDING_ENABLED > 0
{MB_FUNC_READWRITE_MULTIPLE_REGISTERS, eMBFuncReadWriteMultipleHoldingRegister},
#endif
#if MB_FUNC_READ_COILS_ENABLED > 0
{MB_FUNC_READ_COILS, eMBFuncReadCoils},
#endif
#if MB_FUNC_WRITE_COIL_ENABLED > 0
{MB_FUNC_WRITE_SINGLE_COIL, eMBFuncWriteCoil},
#endif
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
{MB_FUNC_WRITE_MULTIPLE_COILS, eMBFuncWriteMultipleCoils},
#endif
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED > 0
{MB_FUNC_READ_DISCRETE_INPUTS, eMBFuncReadDiscreteInputs},
#endif
};
eMBErrorCode
eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
{
eMBErrorCode eStatus = MB_ENOERR;
// 检查地址设置是否正确
if( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) ||
( ucSlaveAddress < MB_ADDRESS_MIN )
|| ( ucSlaveAddress > MB_ADDRESS_MAX ) )
{
// 非法参数
eStatus = MB_EINVAL;
}
else
{
// 地址复制给静态全局变量
ucMBAddress = ucSlaveAddress;
switch ( eMode )
{
#if MB_SLAVE_RTU_ENABLED > 0
case MB_RTU:
// RTU模式下函数指针赋值
pvMBFrameStartCur = eMBRTUStart;
pvMBFrameStopCur = eMBRTUStop;
peMBFrameSendCur = eMBRTUSend;
peMBFrameReceiveCur = eMBRTUReceive;
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
pxMBFrameCBByteReceived = xMBRTUReceiveFSM;
pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;
pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;
// RTU模式初始化,包括串口、定时器初始化
eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );
break;
#endif
#if MB_SLAVE_ASCII_ENABLED > 0
case MB_ASCII:
// ASCII模式下函数指针赋值
pvMBFrameStartCur = eMBASCIIStart;
pvMBFrameStopCur = eMBASCIIStop;
peMBFrameSendCur = eMBASCIISend;
peMBFrameReceiveCur = eMBASCIIReceive;
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
pxMBFrameCBByteReceived = xMBASCIIReceiveFSM;
pxMBFrameCBTransmitterEmpty = xMBASCIITransmitFSM;
pxMBPortCBTimerExpired = xMBASCIITimerT1SExpired;
// ASCII模式初始化,包括串口、定时器初始化
eStatus = eMBASCIIInit( ucMBAddress, ucPort, ulBaudRate, eParity );
break;
#endif
default:
eStatus = MB_EINVAL;
break;
}
if( eStatus == MB_ENOERR )
{
// 事件标志组初始化
if( !xMBPortEventInit( ) )
{
// 失败
eStatus = MB_EPORTERR;
}
else
{
eMBCurrentMode = eMode; // 记录Modbus工作模式
eMBState = STATE_DISABLED; // 工作状态设置 DISABLED
}
}
}
return eStatus;
}
#if MB_SLAVE_TCP_ENABLED > 0
eMBErrorCode
eMBTCPInit( USHORT ucTCPPort )
{
eMBErrorCode eStatus = MB_ENOERR;
if( ( eStatus = eMBTCPDoInit( ucTCPPort ) ) != MB_ENOERR )
{
eMBState = STATE_DISABLED;
}
else if( !xMBPortEventInit( ) )
{
eStatus = MB_EPORTERR;
}
else
{
pvMBFrameStartCur = eMBTCPStart;
pvMBFrameStopCur = eMBTCPStop;
peMBFrameReceiveCur = eMBTCPReceive;
peMBFrameSendCur = eMBTCPSend;
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBTCPPortClose : NULL;
ucMBAddress = MB_TCP_PSEUDO_ADDRESS;
eMBCurrentMode = MB_TCP;
eMBState = STATE_DISABLED;
}
return eStatus;
}
#endif
eMBErrorCode
eMBRegisterCB( UCHAR ucFunctionCode, pxMBFunctionHandler pxHandler )
{
int i;
eMBErrorCode eStatus;
if( ( 0 < ucFunctionCode ) && ( ucFunctionCode <= 127 ) )
{
ENTER_CRITICAL_SECTION( );
if( pxHandler != NULL )
{
//注册[功能码][功能函数]至 xFuncHandlers
for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
{
if( ( xFuncHandlers[i].pxHandler == NULL ) ||
( xFuncHandlers[i].pxHandler == pxHandler ) )
{
xFuncHandlers[i].ucFunctionCode = ucFunctionCode;
xFuncHandlers[i].pxHandler = pxHandler;
break;
}
}
eStatus = ( i != MB_FUNC_HANDLERS_MAX ) ? MB_ENOERR : MB_ENORES;
}
else
{
//移除[功能码][功能函数]从 xFuncHandlers
for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
{
if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode )
{
xFuncHandlers[i].ucFunctionCode = 0;
xFuncHandlers[i].pxHandler = NULL;
break;
}
}
eStatus = MB_ENOERR;
}
EXIT_CRITICAL_SECTION( );
}
else
{
eStatus = MB_EINVAL;
}
return eStatus;
}
eMBErrorCode
eMBClose( void )
{
eMBErrorCode eStatus = MB_ENOERR;
if( eMBState == STATE_DISABLED )
{
if( pvMBFrameCloseCur != NULL )
{
pvMBFrameCloseCur( );
}
}
else
{
eStatus = MB_EILLSTATE;
}
return eStatus;
}
eMBErrorCode
eMBEnable( void )
{
eMBErrorCode eStatus = MB_ENOERR;
if( eMBState == STATE_DISABLED )
{
//eMBRTUStart 启动协议栈
pvMBFrameStartCur( );
eMBState = STATE_ENABLED;
}
else
{
eStatus = MB_EILLSTATE;
}
return eStatus;
}
eMBErrorCode
eMBDisable( void )
{
eMBErrorCode eStatus;
if( eMBState == STATE_ENABLED )
{
//eMBRTUStop 禁止串口收发,停止定时器
pvMBFrameStopCur( );
eMBState = STATE_DISABLED;
eStatus = MB_ENOERR;
}
else if( eMBState == STATE_DISABLED )
{
eStatus = MB_ENOERR;
}
else
{
eStatus = MB_EILLSTATE;
}
return eStatus;
}
eMBErrorCode eMBPoll( void )
{
static UCHAR *ucMBFrame;
static UCHAR ucRcvAddress;
static UCHAR ucFunctionCode;
static USHORT usLength;
static eMBException eException;
int i;
eMBErrorCode eStatus = MB_ENOERR;
eMBEventType eEvent;
if( eMBState != STATE_ENABLED )
{
//非法状态
return MB_EILLSTATE;
}
// 无限等待事件发生并分发处理
if( xMBPortEventGet( &eEvent ) == TRUE )
{
switch ( eEvent )
{
// 协议栈就绪,读者可在此处增加记录协议栈就绪状态标志
case EV_READY:
break;
// 收到一帧数据
case EV_FRAME_RECEIVED:
// eMBRTUReceive,提取有效数据至ucRcvAddress,ucMBFrame,usLength
eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength );
if( eStatus == MB_ENOERR )
{
//检查地址是否正确
if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) )
{
//抛出 EV_EXECUTE 事件
( void )xMBPortEventPost( EV_EXECUTE );
}
}
break;
// 收到正确的一帧数据后执行响应功能函数
case EV_EXECUTE:
ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];//功能码
eException = MB_EX_ILLEGAL_FUNCTION; //异常码
for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
{
// 查询功能码对应的功能函数
if( xFuncHandlers[i].ucFunctionCode == 0 )
{
break;
}
else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode )
{
// 调用功能函数
eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength );
break;
}
}
// 是否广播
if( ucRcvAddress != MB_ADDRESS_BROADCAST )
{
if( eException != MB_EX_NONE )
{
// 发生错误,功能码高位置1
usLength = 0;
ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR );
ucMBFrame[usLength++] = eException;
}
//eMBRTUSend 发送响应帧给主机
eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength );
}
break;
case EV_FRAME_SENT:
break;
}
}
return MB_ENOERR;
}
Modbus资料整理
笔者将Modbus 主机、从机协议完整资料整理如下:
| 文件 | 说明 |
|---|---|
| freemodbus-v1.6.zip | FreeModbusV1.6源码 |
| FreeModbusMasterSlave(Rev1.0.0).zip | 笔者移植好测试通过STM32+FreeRTOS+FreeModbus Master Slave代码 |
| ModbusSlave 7.0和ModbusPoll_7.0软件注册码.zip | FreeModbus Master Slave调试工具 |
| FreeModbus V1.6 主机使用说明.md | FreeModbus 主机使用说明文档 |
| FreeModbus 从机流程图.vsd | FreeModbus 从机流程图 |
| FreeModbus 主机流程图.vsd | FreeModbus 主机流程图 |
| Modbus应用协议.doc | 笔者整理的 FreeModbus 协议文档 |
| FreeModbus Datasheet | 笔者搜集 FreeModbus 官方协议文档 |
资料链接:FreeModbus资源传送门



