此篇博客将按照Freemodbus的运行流程,对各个函数进行解析(无主机RTU模式),
我将尽我所能解释的尽量清楚,如有错误或者语义模糊处还请在评论区指出,谢谢。
文章目录
1、eMBInit(…)
首先我们来看mb.h里的说明注释
eMBErrorCode
eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity );
/* ----------------------- 函数原型------------------------------*/
-
\简要说明:初始化modbus协议栈
-
该函数初始化ASCII或RTU模块,
-
并调用porting layer的init函数来配置硬件部分。
-
请注意,直到eMBEnable()被调用之前,
-
Receiver一直是禁用的,没有Modbus帧被处理。
-
\参数1:eMode 选择 ASCII or RTU 模式.
-
\参数2:ucSlaveAddress 从机地址
-
\参数3:ucPort 端口。例如windows上的COM1。这个值跟平台有关系,一些端口干脆忽略它。(我也忽略了,直接填0)
-
\参数4:ulBaudRate 波特率
-
\参数5:eParity 校验方式
-
\返回值:没错返回 MB_ENOERR.
-
没错然后,该协议处于禁用状态,并准备通过调用eMBEnable()激活。
-
Otherwise one of the following error codes
-
is returned:
-
- eMBErrorCode::MB_EINVAL 从机地址无效,有效的地址范围1 - 247
-
- eMBErrorCode::MB_EPORTERR porting layer返回错误
小结:从上面可以看出eMBInit(…)函数就是做一些初始化的
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_RTU_ENABLED > 0
case MB_RTU:点击跳转函数指针定义
pvMBFrameStartCur = eMBRTUStart;
pvMBFrameStopCur = eMBRTUStop;
peMBFrameSendCur = eMBRTUSend;
peMBFrameReceiveCur = eMBRTUReceive;
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
pxMBFrameCBByteReceived = xMBRTUReceiveFSM;
pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;
pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;
eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );
break;点击跳转eMBRTUInit函数
#endif
#if MB_ASCII_ENABLED > 0
case MB_ASCII:
pvMBFrameStartCur = eMBASCIIStart;
pvMBFrameStopCur = eMBASCIIStop;
peMBFrameSendCur = eMBASCIISend;
peMBFrameReceiveCur = eMBASCIIReceive;
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
pxMBFrameCBByteReceived = xMBASCIIReceiveFSM;
pxMBFrameCBTransmitterEmpty = xMBASCIITransmitFSM;
pxMBPortCBTimerExpired = xMBASCIITimerT1SExpired;
eStatus = eMBASCIIInit( ucMBAddress, ucPort, ulBaudRate, eParity );
break;
#endif
default:
eStatus = MB_EINVAL;
}
/* port dependent event module initalization failed. */
if( eStatus == MB_ENOERR ) 如果前面的初始化没错,进行接下来的任务
{
if( !xMBPortEventInit( ) ) 注意,这里是!点击跳转xMBPortEventInit函数
{
eStatus = MB_EPORTERR; 端口事件模块初始化失败。
}
else
{
eMBCurrentMode = eMode;
eMBState = STATE_DISABLED;一切正常就会来到这里
}
}
}
return eStatus;
}
1.1 定义函数返回值类型eMBErrorCode
/*! \ingroup modbus
* \brief Errorcodes used by all function in the protocol stack.
*/
typedef enum
{
MB_ENOERR, /*!< no error. */
MB_ENOREG, /*!< illegal register address. */
MB_EINVAL, /*!< illegal argument. */
MB_EPORTERR, /*!< porting layer error. */
MB_ENORES, /*!< insufficient resources. */
MB_EIO, /*!< I/O error. */
MB_EILLSTATE, /*!< protocol stack in illegal state. */
MB_ETIMEDOUT /*!< timeout error occurred. */
} eMBErrorCode;
1.2 函数指针的定义
//eMBInit( )要初始化的函数指针. 根据模式(RTU or ASCII)来赋予不同的函数地址 定义在里面
static peMBFrameSend peMBFrameSendCur;
static pvMBFrameStart pvMBFrameStartCur;
static pvMBFrameStop pvMBFrameStopCur;
static peMBFrameReceive peMBFrameReceiveCur;
static pvMBFrameClose pvMBFrameCloseCur;
/* Callback functions required by the porting layer. They are called when
* an external event has happend which includes a timeout or the reception
* or transmission of a character.
*/
BOOL( *pxMBFrameCBByteReceived ) ( void );//函数指针
BOOL( *pxMBFrameCBTransmitterEmpty ) ( void );//函数指针
BOOL( *pxMBPortCBTimerExpired ) ( void );//函数指针
BOOL( *pxMBFrameCBReceiveFSMCur ) ( void );//函数指针
BOOL( *pxMBFrameCBTransmitFSMCur ) ( void );//函数指针
/*********************************mbframe.h*************************************/
//定义了函数指针
typedef void ( *pvMBFrameStart ) ( void );
typedef void ( *pvMBFrameStop ) ( void );
typedef eMBErrorCode( *peMBFrameReceive ) ( UCHAR * pucRcvAddress,
UCHAR ** pucFrame,
USHORT * pusLength );
typedef eMBErrorCode( *peMBFrameSend ) ( UCHAR slaveAddress,
const UCHAR * pucFrame,
USHORT usLength );
typedef void( *pvMBFrameClose ) ( void );
函数指针详情参考【】
1.3 eMBRTUInit(…)函数
在mbrtu.c中。此函数完成的时对于串口和传输数据要用的定时器的初始化,同时这下面调用的这两个函数也是在移植Freemodbus协议到不同的板子的过程中必须要进行修改的部分。
/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBRTUInit( UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
{
eMBErrorCode eStatus = MB_ENOERR;
ULONG usTimerT35_50us;
( void )ucSlaveAddress;
ENTER_CRITICAL_SECTION( );
/* Modbus RTU uses 8 Databits. */
if( xMBPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE )
{
eStatus = MB_EPORTERR;
}
else
{
/* If baudrate > 19200 then we should use the fixed timer values
* t35 = 1750us. Otherwise t35 must be 3.5 times the character time.
*/
if( ulBaudRate > 19200 )
{
usTimerT35_50us = 35; /* 1800us. */
}
else
{
/* The timer reload value for a character is given by:
*
* ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )
* = 11 * Ticks_per_1s / Baudrate
* = 220000 / Baudrate
* The reload for t3.5 is 1.5 times this value and similary
* for t3.5.
*/
usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate );
}
if( xMBPortTimersInit( ( USHORT ) usTimerT35_50us ) != TRUE )
{
eStatus = MB_EPORTERR;
}
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
1.3.1 xMBPortSerialInit(…)
1.3.1 xMBPortTimersInit(…)
这关这两个函数的详细介绍请看【】
关于modbus协议数据传输间隔3.5T的详细解释请看【】
1.4 xMBPortEventInit( )函数
BOOL
xMBPortEventInit( void )
{
xEventInQueue = FALSE;
return TRUE;
}
1.5 小结
做为在main()调用的第一个初始化函数,具体功能可总结为如下
eMBInit(RTU,从机地址,端口,波特率,校验方式)
{
验证从机地址是否正确
设置RTU模式并对函数指针进行映射
调用eMBRTUInit( 从机地址, 端口, 波特率, 校验)
{
初始化串口xMBPortSerialInit( ucPort, ulBaudRate, 8, eParity )
初始化定时器xMBPortTimersInit( ( USHORT ) usTimerT35_50us )
}
初始化事件函数xMBPortEventInit( )
}
2. eMBEnable()
首先我们来看mb.h里的说明注释
eMBErrorCode eMBEnable( void );
- 简要说明:使能Modbus协议栈.
- 此函数启动处理消息帧
- 只有协议栈在disabled状态下才可以enable
- 返回值:如果协议栈现在处于enabled状态,则返回 MB_ENOERR.
- 如果不在 disabled state 则返回 MB_EILLSTATE.
*/
eMBErrorCode
eMBEnable( void )
{
eMBErrorCode eStatus = MB_ENOERR;
if( eMBState == STATE_DISABLED ) //先前一定要是 Disbaled状态
{
/* Activate the protocol stack. */
pvMBFrameStartCur( );// pvMBFrameStartCur = eMBRTUStart;函数指针 ,不太懂
eMBState = STATE_ENABLED;
}
else
{
eStatus = MB_EILLSTATE;
}
return eStatus;
}
函数指针赋值过了,直接调用
2.1 eMBRTUStart()函数
void
eMBRTUStart( void )
{
ENTER_CRITICAL_SECTION( );
/* Initially the receiver is in the state STATE_RX_INIT. we start
* the timer and if no character is received within t3.5 we change
* to STATE_RX_IDLE. This makes sure that we delay startup of the
* modbus protocol stack until the bus is free.
*/
eRcvState = STATE_RX_INIT;
vMBPortSerialEnable( TRUE, FALSE );
vMBPortTimersEnable( );
EXIT_CRITICAL_SECTION( );
}
2.1.1 vMBPortSerialEnable
详情请看【】
2.1.2 vMBPortTimersEnable( );
详情请看【】
2.2 小结
eMBErrorCode
eMBEnable( void )
{
判断状态为进制,则调用eMBRTUStart()
{
状态设为RX初始化
启动串口接收功能
使能定时器
}
状态设置为使能
}
未完待续