FreeModbus--完全分析--系列2

eMBPoll的分析

在此循环函数中xMBPortEventGet(&eEvent ) == TRUE先判断是否有事件,无事件发生则不进入状态机!

还记得第二节定时器开始工作了吗?我们先看看该定时器如果超时了会发生什么事件!

在超时中断中我们将会调用pxMBPortCBTimerExpired函数,其中有以下代码:

BOOL
xMBRTUTimerT35Expired( void )
{
    BOOL            xNeedPoll = FALSE;

    switch ( eRcvState )
    {
        /* Timer t35 expired. Startup phase is finished. */
    case STATE_RX_INIT:
        xNeedPoll = xMBPortEventPost( EV_READY );
        break;

        /* A frame was received and t35 expired. Notify the listener that
         * a new frame was received. */
    case STATE_RX_RCV:
        xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED );
        break;

        /* An error occured while receiving the frame. */
    case STATE_RX_ERROR:
        break;

        /* Function called in an illegal state. */
    default:
        assert( ( eRcvState == STATE_RX_INIT ) ||
                ( eRcvState == STATE_RX_RCV ) || ( eRcvState == STATE_RX_ERROR ) );
    }

    vMBPortTimersDisable(  );
    eRcvState = STATE_RX_IDLE;

    return xNeedPoll;
}
上一节分析中全局变量eRcvState =STATE_RX_INIT,因此第二节所说的定时器第一次超时将会发送xNeedPoll =xMBPortEventPost( EV_READY )事件,

然后关闭定时器,全局变量eRcvState =STATE_RX_IDLE。此时,在主循环eMBPoll中将会执行一次EV_READY下的操作,

之后会一直执行eMBPoll,整个协议栈开始运行!

接收数据分析

由于FreeModbus只支持从机模式,因此我们分析一下其在接收到数据后的操作!!!

接收数据

在上三节的操作中,我们可以知道进入eMBPoll循环后,串口中断是开启的。因此在接收到数据的时候,首先响应的应该是串口中断程序。

接收中断中将会调用接收状态机:

BOOL
xMBRTUReceiveFSM( void )
{
    BOOL            xTaskNeedSwitch = FALSE;
    UCHAR           ucByte;

    assert( eSndState == STATE_TX_IDLE );

    /* Always read the character. */
    ( 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;
}
经过第3节的分析,此时全局变量eRcvState =STATE_RX_IDLE。接收状态机开始后,读取UART串口缓存中的数据,

并进入STATE_RX_IDLE分支中存储一次数据后开启超时定时器,进入STATE_RX_RCV分支继续接收后续的数据,

直至定时器超时!为什么要等待超时才能停止接收呢!

        /* A frame was received and t35 expired. Notify the listener that
         * a new frame was received. */
    case STATE_RX_RCV:
        xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED );
        break;
可以发现接收数据时发生超时后,协议栈会发送EV_FRAME_RECEIVED接收完成这个信号。此时eMBPoll接收到此信号后会调用eMBRTUReceive函数。

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;
}
eMBRTUReceive函数完成了CRC校验、帧数据地址、长度的赋值,便于给上层进行处理!之后发送( void)xMBPortEventPost( EV_EXECUTE )事件。

处理数据时根据功能码调用相应的函数,这些函数存储在xFuncHandlers数组中!之后发送响应!完成一次操作!



你可能感兴趣的:(FreeModbus--完全分析--系列2)