code

2016-07-23  本文已影响77人  恰冯同学年少
/************************************************************
*参数:
*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;
}
上一篇下一篇

猜你喜欢

热点阅读