code

作者: 恰冯同学年少 | 来源:发表于2016-07-23 13:52 被阅读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;
    }
    

    相关文章

      网友评论

          本文标题:code

          本文链接:https://www.haomeiwen.com/subject/aohwjttx.html