本篇按照FreeModbus协议栈的工作流程,对源代码进行总结解析;FreeModbus协议栈作为从机,等待主机传送的数据,当从机接收到一帧完整的报文后,对报文进行解析,然后响应主机,发送报文给主机,实现主机和从机之间的通信;
1:demo.c中三个函数,完成协议栈的准备工作;
eMBInit()函数:(mb.c)
/*函数功能:
*1:实现RTU模式和ASCALL模式的协议栈初始化;
*2:完成协议栈核心函数指针的赋值,包括Modbus协议栈的使能和禁止、报文的接收和响应、3.5T定时器中断回调函数、串口发送和接收中断回调函数;
*3:eMBRTUInit完成RTU模式下串口和3.5T定时器的初始化,需用户自己移植;
*4:设置Modbus协议栈的模式eMBCurrentMode为MB_RTU,设置Modbus协议栈状态eMBState为STATE_DISABLED;
*/
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; /*使能modbus协议栈*/
pvMBFrameStopCur = eMBRTUStop; /*禁用modbus协议栈*/
peMBFrameSendCur = eMBRTUSend; /*modbus从机响应函数*/
peMBFrameReceiveCur = eMBRTUReceive; /*modbus报文接收函数*/
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
//接收状态机
pxMBFrameCBByteReceived = xMBRTUReceiveFSM; /*串口接收中断最终调用此函数接收数据*/
//发送状态机
pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM; /*串口发送中断最终调用此函数发送数据*/
//报文到达间隔检查
pxMBPortCBTimerExpired = xMBRTUTimerT35Expired; /*定时器中断函数最终调用次函数完成定时器中断*/
//初始化RTU
eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );
break;
#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;
}
//
if( eStatus == MB_ENOERR )
{
if( !xMBPortEventInit() )
{
/* port dependent event module initalization failed. */
eStatus = MB_EPORTERR;
}
else
{
//设定当前状态
eMBCurrentMode = eMode; //设定RTU模式
eMBState = STATE_DISABLED; //modbus协议栈初始化状态,在此初始化为禁止
}
}
}
return eStatus;
}
eMBEnable()函数:(mb.c)
/*函数功能
*1:设置Modbus协议栈工作状态eMBState为STATE_ENABLED;
*2:调用pvMBFrameStartCur()函数激活协议栈
*/
eMBErrorCode
eMBEnable( void )
{
eMBErrorCode eStatus = MB_ENOERR;
if( eMBState == STATE_DISABLED )
{
/* Activate the protocol stack. */
pvMBFrameStartCur( ); /*pvMBFrameStartCur = eMBRTUStart;调用eMBRTUStart函数*/
eMBState = STATE_ENABLED;
}
else
{
eStatus = MB_EILLSTATE;
}
return eStatus;
}
eMBRTUStart()函数:(mbrtu.c)
/*函数功能
*1:设置接收状态机eRcvState为STATE_RX_INIT;
*2:使能串口接收,禁止串口发送,作为从机,等待主机传送的数据;
*3:开启定时器,3.5T时间后定时器发生第一次中断,此时eRcvState为STATE_RX_INIT,上报初始化完成事件,然后设置eRcvState为空闲STATE_RX_IDLE;
*4:每次进入3.5T定时器中断,定时器被禁止,等待串口有字节接收后,才使能定时器;
*/
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( );
}
eMBPoll()函数:(mb.c)
/*函数功能:
*1:检查协议栈状态是否使能,eMBState初值为STATE_NOT_INITIALIZED,在eMBInit()函数中被赋值为STATE_DISABLED,在eMBEnable函数中被赋值为STATE_ENABLE;
*2:轮询EV_FRAME_RECEIVED事件发生,若EV_FRAME_RECEIVED事件发生,接收一帧报文数据,上报EV_EXECUTE事件,解析一帧报文,响应(发送)一帧数据给主机;
*/
eMBErrorCode
eMBPoll( void )
{
static UCHAR *ucMBFrame; //接收和发送报文数据缓存区
static UCHAR ucRcvAddress; //modbus从机地址
static UCHAR ucFunctionCode; //功能码
static USHORT usLength; //报文长度
static eMBException eException; //错误码响应枚举
int i;
eMBErrorCode eStatus = MB_ENOERR; //modbus协议栈错误码
eMBEventType eEvent; //事件标志枚举
/* Check if the protocol stack is ready. */
if( eMBState != STATE_ENABLED ) //检查协议栈是否使能
{
return MB_EILLSTATE; //协议栈未使能,返回协议栈无效错误码
}
/* Check if there is a event av