/************************************************************
*参数:
*eMode:Modbus传输模式,RTU/ASCALL/TCP,本文移植最常用的RTU模式
*ucSlaveAddress:Modbus从机地址,范围0-247,最大255,247-255预留给用户
*ucPort:串口号;ulBaudRate:波特率;eParity:奇偶校验位;
*功能:
*初始化RTU和ASCALL传输模式,TCP有单独的初始化函数
************************************************************/
eMBErrorCode
eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
{
eMBErrorCode eStatus = MB_ENOERR; /*错误代码状态标志*/
/* check preconditions */
/*检查从机地址的合法性,若modbus地址为广播地址或者不在1-247范围内,返回错误代码MB_EINVAL*/
if( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) ||
( ucSlaveAddress < MB_ADDRESS_MIN ) || ( ucSlaveAddress > MB_ADDRESS_MAX ) )
{
eStatus = MB_EINVAL;
}
else
{
/*读取modbus从机地址*/
ucMBAddress = ucSlaveAddress;
switch ( eMode )
{
#if (MB_RTU_ENABLED > 0) /*在mbconfig.h中打开RTU模式,#define MB_RTU_ENABLED (1),如若考虑代码量,删除ASCALL和TCP模式代码,只能支持一种模式的传输*/
case MB_RTU:
/*给函数指针赋值,这些函数全部为modbus协议的核心功能函数,需用户自行移植,详细见下文*/
pvMBFrameStartCur = eMBRTUStart;
pvMBFrameStopCur = eMBRTUStop;
peMBFrameSendCur = eMBRTUSend;
peMBFrameReceiveCur = eMBRTUReceive;
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
pxMBFrameCBByteReceived = xMBRTUReceiveFSM;
pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;
pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;
/*串口初始化以及3.5T字节周期设置函数,详解见下文*/
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:
/*若传输协议不为RTU/ASCALL模式,返回错误代码MB_EINVAL*/
eStatus = MB_EINVAL;
}
if( eStatus == MB_ENOERR )
{
if( !xMBPortEventInit( ) ) /*端口层时间状态初始化,设置事件跟新标志xEventInQueue为FALSE,无事件更新*/
{
/* port dependent event module initalization failed. */
eStatus = MB_EPORTERR;
}
else
{
eMBCurrentMode = eMode; /*读取当前modbus协议传输模式*/
eMBState = STATE_DISABLED; /*modbus协议层状态标志,初始化为STATE_NOT_INITIALIZED,初始化成功后,赋值STATE_DISABLED准备使能状态*/
}
}
}
return eStatus;
}
启动modbus从机
/*********************************************************
*FUNC:Modbus从机做好接受的数据的准备,赋eRcvState为STATE_RX_INIT,使能modbus接受,使能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; /*赋值eRcvState为接受初始化*/
vMBPortSerialEnable( TRUE, FALSE ); /*使能从机接受,禁止发送,等待主机发送的数据*/
vMBPortTimersEnable(); /*使能3.5T定时器*/
EXIT_CRITICAL_SECTION( );
}
modbus传输停止函数
/**********************************************************
*modbus传输停止函数,禁止发送和接收,禁止3.5T定时器
**********************************************************/
void
eMBRTUStop( void )
{
ENTER_CRITICAL_SECTION( );
vMBPortSerialEnable( FALSE, FALSE );/*禁止接收和发送*/
vMBPortTimersDisable( ); /*禁止3.5T定时器*/
EXIT_CRITICAL_SECTION( );
}
报文发送函数
eMBErrorCode
eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
{
eMBErrorCode eStatus = MB_ENOERR;
USHORT usCRC16;
ENTER_CRITICAL_SECTION( );
/* Check if the receiver is still in idle state. If not we where to
* slow with processing the received frame and the master sent another
* frame on the network. We have to abort sending the frame.
*/
if( eRcvState == STATE_RX_IDLE )
{
/* First byte before the Modbus-PDU is the slave address. */
/*在协议数据单元前加从机地址*/
pucSndBufferCur = ( UCHAR * ) pucFrame - 1;
usSndBufferCount = 1;
/* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
usSndBufferCount += usLength;
/* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */
usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );
ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );
/* Activate the transmitter. */
eSndState = STATE_TX_XMIT;
xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur ); /*发送一个字节的数据,进入发送中断函数,启动传输*/
pucSndBufferCur++; /* next byte in sendbuffer. */
usSndBufferCount--;
vMBPortSerialEnable( FALSE, TRUE );
}
else
{
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
eMBErrorCode
eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
{
BOOL xFrameReceived = FALSE;
eMBErrorCode eStatus = MB_ENOERR;
ENTER_CRITICAL_SECTION();
assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX );
/* Length and CRC check */
if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN )
&& ( usMBCRC16( ( UCHAR * ) ucRTUBuf, usRcvBufferPos ) == 0 ) )
{
/* Save the address field. All frames are passed to the upper layed
* and the decision if a frame is used is done there.
*/
*pucRcvAddress = ucRTUBuf[MB_SER_PDU_ADDR_OFF];
/* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
* size of address field and CRC checksum.
*/
*pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC );
/* Return the start of the Modbus PDU to the caller. */
*pucFrame = ( UCHAR * ) & ucRTUBuf[MB_SER_PDU_PDU_OFF];
xFrameReceived = TRUE;
}
else
{
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION();
return eStatus;
}
BOOL
xMBRTUReceiveFSM( void )
{
BOOL xTaskNeedSwitch = FALSE;
UCHAR ucByte;
assert( eSndState == STATE_TX_IDLE );
//读串口接收数据,实际上该函数在串口接收中断中被执行
( void )xMBPortSerialGetByte( ( CHAR * ) & ucByte );
//根据不同的状态转移
switch ( eRcvState )
{
/* If we have received a character in the init state we have to
* wait until the frame is finished.
*/
case STATE_RX_INIT:
vMBPortTimersEnable();
break;
/* In the error state we wait until all characters in the
* damaged frame are transmitted.
*/
case STATE_RX_ERROR:
vMBPortTimersEnable();
break;
/* In the idle state we wait for a new character. If a character
* is received the t1.5 and t3.5 timers are started and the
* receiver is in the state STATE_RX_RECEIVCE.
*/
case STATE_RX_IDLE:
//接收到一个数据,保存串口数据,重启定时器
usRcvBufferPos = 0;
ucRTUBuf[usRcvBufferPos++] = ucByte;
//状态转移,数据接收中
eRcvState = STATE_RX_RCV;
/* Enable t3.5 timers. */
//开启定时器,相当于重启定时器
vMBPortTimersEnable();
break;
/* We are currently receiving a frame. Reset the timer after
* every character received. If more than the maximum possible
* number of bytes in a modbus frame is received the frame is
* ignored.
*/
case STATE_RX_RCV:
if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX )
{
ucRTUBuf[usRcvBufferPos++] = ucByte;
}
else
{
eRcvState = STATE_RX_ERROR;
}
//开启定时器,相当于重启定时器
vMBPortTimersEnable();
break;
}
return xTaskNeedSwitch;
}