• freemodbus-v1.5.0 源码分析 (转)


    FreeModbus协议栈作为从机,等待主机传送的数据,当从机接收到一帧完整的报文后,对报文进行解析,然后响应主机,发送报文给主机,实现主机和从机之间的通信。

    1、初始化协议栈---eMBInit函数(mb.c中),以RTU为例

     1 eMBErrorCode  eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
     2 {
     3 
     4 ?1/*函数功能:*1:实现RTU模式和ASCALL模式的协议栈初始化;*2:完成协议栈核心函数指针的赋值,包括Modbus协议栈的使能和禁止、报文的接收和响应、3.5T定时器中断回调函数、串口发送和接收中断回调函数;*3:eMBRTUInit完成RTU模式下串口和3.5T定时器的初始化,需用户自己移植;*4:设置Modbus协议栈的模式eMBCurrentMode为MB_RTU,设置Modbus协议栈状态eMBState为STATE_DISABLED;*/   eMBErrorCode    eStatus = MB_ENOERR;
     5 /* check preconditions */
     6     if( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) ||
     7         ( ucSlaveAddress < MB_ADDRESS_MIN ) || ( ucSlaveAddress > MB_ADDRESS_MAX ) )   //验证从机地址
     8     {
     9         eStatus = MB_EINVAL;                                  //地址错误
    10     }
    11     else
    12     {
    13         ucMBAddress = ucSlaveAddress;
    14 
    15         switch ( eMode )
    16         {
    17 #if MB_RTU_ENABLED > 0
    18         case MB_RTU:
    19             pvMBFrameStartCur = eMBRTUStart;
    20             pvMBFrameStopCur = eMBRTUStop;
    21             peMBFrameSendCur = eMBRTUSend;
    22             peMBFrameReceiveCur = eMBRTUReceive;
    23             pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
    24             pxMBFrameCBByteReceived = xMBRTUReceiveFSM;         //接收状态机,串口接受中断最终调用此函数接收数据
    25             pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;    //发送状态机,串口发送中断最终调用此函数发送数据
    26             pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;     //报文到达间隔检查,定时器中断函数最终调用次函数完成定时器中断
    27 
    28             eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );
    29             break;
    30 #endif
    31 #if MB_ASCII_ENABLED > 0
    32         case MB_ASCII:
    33             pvMBFrameStartCur = eMBASCIIStart;
    34             pvMBFrameStopCur = eMBASCIIStop;
    35             peMBFrameSendCur = eMBASCIISend;
    36             peMBFrameReceiveCur = eMBASCIIReceive;
    37             pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
    38             pxMBFrameCBByteReceived = xMBASCIIReceiveFSM;
    39             pxMBFrameCBTransmitterEmpty = xMBASCIITransmitFSM;
    40             pxMBPortCBTimerExpired = xMBASCIITimerT1SExpired;
    41 
    42             eStatus = eMBASCIIInit( ucMBAddress, ucPort, ulBaudRate, eParity );
    43             break;
    44 #endif
    45         default:
    46             eStatus = MB_EINVAL;
    47         }
    48 
    49         if( eStatus == MB_ENOERR )
    50         {
    51             if( !xMBPortEventInit(  ) )
    52             {
    53                 /* port dependent event module initalization failed. */
    54                 eStatus = MB_EPORTERR;
    55             }
    56             else
    57             {//设定当前状态
    58                 eMBCurrentMode = eMode;           //设定RTU模式
    59                 eMBState = STATE_DISABLED;        //modbus协议栈初始化状态,在此初始化为禁止
    60             }
    61         }
    62     }
    63     return eStatus;
    64 }
    View Code
    eMBInit函数中pvMBFrameStartCur、pvMBFrameStartCur等为协议栈函数的接口,对于不同的通信模式使用不同的函数进行初始化!!!此编程模式可以借鉴学习!!!
      eMBInit函数对底层驱动(串口和定时器)进行初始化。初始化完成并且成功之后对事件也进行了初始化,完成后全局变量eMBState=STATE_DISABLED。

     2、启动协议栈----eMBEnable函数(mb.c函数)

     1 eMBErrorCode
     2 eMBEnable( void )
     3 {
     4     /*函数功能:
     5      *1:实现RTU模式和ASCALL模式的协议栈初始化;
     6      *2:完成协议栈核心函数指针的赋值,包括Modbus协议栈的使能和禁止、报文的接收和响应、3.5T定时器中断回调函数、串口发送和接收中断回调函数;
     7      *3:eMBRTUInit完成RTU模式下串口和3.5T定时器的初始化,需用户自己移植;
     8      *4:设置Modbus协议栈的模式eMBCurrentMode为MB_RTU,设置Modbus协议栈状态eMBState为STATE_DISABLED;
     9      */
    10     eMBErrorCode    eStatus = MB_ENOERR;
    11 
    12     if( eMBState == STATE_DISABLED )
    13     {
    14         /* Activate the protocol stack. */
    15         pvMBFrameStartCur(  );       //激活协议栈
    16         eMBState = STATE_ENABLED;    //设置Modbus协议栈工作状态eMBState为STATE_ENABLED
    17     }
    18     else
    19     {
    20         eStatus = MB_EILLSTATE;
    21     }
    22     return eStatus;
    23 }
    View Code

    ---eMBRTUStart函数 (mbrtu.c)

     1 void
     2 eMBRTUStart( void )
     3 {
     4     /*函数功能
     5      * 1:设置接收状态机eRcvState为STATE_RX_INIT;
     6      * 2:使能串口接收,禁止串口发送,作为从机,等待主机传送的数据;
     7      * 3:开启定时器,3.5T时间后定时器发生第一次中断,此时eRcvState为STATE_RX_INIT,上报初始化完成事件,然后设置eRcvState为空闲STATE_RX_IDLE;
     8      * 4:每次进入3.5T定时器中断,定时器被禁止,等待串口有字节接收后,才使能定时器;
     9      * */
    10     ENTER_CRITICAL_SECTION(  );
    11     /* Initially the receiver is in the state STATE_RX_INIT. we start
    12      * the timer and if no character is received within t3.5 we change
    13      * to STATE_RX_IDLE. This makes sure that we delay startup of the
    14      * modbus protocol stack until the bus is free.
    15      */
    16     eRcvState = STATE_RX_INIT;
    17     vMBPortSerialEnable( TRUE, FALSE );   //开启串口接收,发送未开启
    18     vMBPortTimersEnable(  );              //启动定时器
    19 
    20     EXIT_CRITICAL_SECTION(  );
    21 }
    View Code

    eMBEnable函数启动协议栈pvMBFrameStartCur,对于RTU模式,pvMBFrameStartCur函数指针指向 eMBRTUStart,在eMBRTUStart函数中,全局变量eRcvState=STATE_RX_INIT,并使能串口和定时器。注意!!!此时定时器将开始工作!!!。eMBEnable函数中将把全局变量改为 eMBState=STATE_ENABLED。

     3、状态机轮训---eMBPoll函数(mb.c)

     1 eMBErrorCode eMBPoll( void )
     2 {
     3   /*函数功能:
     4   *1:检查协议栈状态是否使能,eMBState初值为STATE_NOT_INITIALIZED,在eMBInit()函数中被赋值为STATE_DISABLED,在eMBEnable函数中被赋值为STATE_ENABLE;
     5   *2:轮询EV_FRAME_RECEIVED事件发生,若EV_FRAME_RECEIVED事件发生,接收一帧报文数据,上报EV_EXECUTE事件,解析一帧报文,响应(发送)一帧数据给主机;
     6   */
     7 static UCHAR   *ucMBFrame;          //接收和发送报文数据缓存区
     8     static UCHAR    ucRcvAddress;       //modbus从机地址
     9     static UCHAR    ucFunctionCode;     //功能码
    10     static USHORT   usLength;           //报文长度
    11     static eMBException eException;     //错误码响应  枚举
    12 
    13     int             i;
    14     eMBErrorCode    eStatus = MB_ENOERR;
    15     eMBEventType    eEvent;                 //错误码
    16 
    17     /* Check if the protocol stack is ready. */
    18     if( eMBState != STATE_ENABLED )           //检查协议栈是否使能
    19     {
    20         return MB_EILLSTATE;                  //协议栈未使能,返回协议栈无效错误码
    21     }
    22 
    23     /* Check if there is a event available. If not return control to caller.
    24      * Otherwise we will handle the event. */
    25 
    26     if( xMBPortEventGet( &eEvent ) == TRUE )   //判断事件是否发生
    27     {
    28         switch ( eEvent )          //查询哪个事件发生
    29         {
    30         case EV_READY:
    31             break;
    32 
    33         case EV_FRAME_RECEIVED:       //接收到一帧数据,此事件发生
    34             eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength );  //接收数据,并检验报文长度和CRC校验是否正确
    35             /*
    36              * ucRcvAddress 主站要读取的从站的地址
    37              * ucMBFrame  指向PDU的头部
    38              * usLength  PDU的长度
    39              */
    40             if( eStatus == MB_ENOERR )
    41             {
    42                 /* Check if the frame is for us. If not ignore the frame. */
    43                 if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) )
    44                 {
    45                     ( void )xMBPortEventPost( EV_EXECUTE );        //修改事件标志为EV_EXECUTE执行事件
    46                 }
    47             }
    48             break;
    49 
    50         case EV_EXECUTE:                             //修改事件标志为EV_EXECUTE执行事件
    51             ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];  //提取功能码
    52             eException = MB_EX_ILLEGAL_FUNCTION;       //赋错误码初值为无效的功能码
    53             for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
    54             {
    55                 /* No more function handlers registered. Abort. */
    56                 if( xFuncHandlers[i].ucFunctionCode == 0 )
    57                 {
    58                     break;
    59                 }
    60                 else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode )    //根据报文中的功能码,处理报文
    61                 {
    62                     eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength ); //对接收到的报文进行解析
    63                     break;
    64                 }
    65             }
    66 
    67             /* If the request was not sent to the broadcast address we
    68              * return a reply. */
    69             if( ucRcvAddress != MB_ADDRESS_BROADCAST )
    70             {
    71                 if( eException != MB_EX_NONE )    //接收到的报文有错误
    72                 {
    73                     /* An exception occured. Build an error frame. */
    74                     usLength = 0;                      //响应发送数据的首字节为从机地址
    75                     ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR );  //响应发送数据帧的第二个字节,功能码最高位置1
    76                     ucMBFrame[usLength++] = eException;          //响应发送数据帧的第三个字节为错误码标识
    77                 }
    78                 if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS )
    79                 {
    80                     vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS );
    81                 }                
    82                 eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength );  //modbus从机响应函数,发送响应给主机
    83             }
    84             break;
    85 
    86         case EV_FRAME_SENT:
    87             break;
    88         }
    89     }
    90     return MB_ENOERR;
    91 }
    View Code

    在eMBPoll函数中,首先由 xMBPortEventGet( &eEvent ) == TRUE 判断时间是否发生,若无事件发生则不进入状态机;若有时间发生则进入状态机开始轮询。状态机的时间转换在定时中断服务函数中实现。

      在eMBEnable函数中启动定时器后,定时器开始工作(见 eMBRTUStart函数), 在定时器第一次超时之后将会发送xNeedPoll = xMBPortEventPost( EV_READY ) 事件,然后关闭定时器,接收机状态(全局变量)eRcvState为STATE_RX_IDLE。此时,主循环eMBPoll中将执行一次EV_READY下的操作。至此,完成Modbus协议栈的初始化准备工作,协议栈开始运行,eMBPoll()函数轮询等待接收完成事件发生。

    ---定时器中断服务函数

    1 void BOARD_GPTA_HANDLER()
    2 {
    3 //    BOOL            bTaskWoken = FALSE; 
    4 
    5     PRINTF("
    Timer Expired:
    
    
    ");
    6     vMBPortSetWithinException( TRUE );
    7     GPT_ClearStatusFlag(BOARD_GPTA_BASEADDR, gptStatusFlagOutputCompare1);  //关闭定时器 
    8 ( void )pxMBPortCBTimerExpired( ); //事件转换  vMBPortSetWithinException( FALSE );
    9 }
    View Code

    ---xMBRTUTimerT35Expired  T3.5超时函数

     1 BOOL  xMBRTUTimerT35Expired( void )
     2 {
     3     /* 函数功能
     4      * 1:从机接受完成一帧数据后,接收状态机eRcvState为STATE_RX_RCV;
     5      * 2:上报“接收到报文”事件(EV_FRAME_RECEIVED);
     6      * 3:禁止3.5T定时器,设置接收状态机eRcvState状态为STATE_RX_IDLE空闲;
     7      */
     8     BOOL            xNeedPoll = FALSE;
     9 
    10     switch ( eRcvState )          //上报modbus协议栈的事件状态给poll函数
    11     {
    12         /* Timer t35 expired. Startup phase is finished. */
    13     case STATE_RX_INIT:
    14         xNeedPoll = xMBPortEventPost( EV_READY );   //初始化完成事件
    15         break;
    16 
    17         /* A frame was received and t35 expired. Notify the listener that
    18          * a new frame was received. */
    19     case STATE_RX_RCV:                        //一帧数据接收完成
    20         xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED );    //上报协议栈事件,接收到一帧完整的数据
    21         break;
    22 
    23         /* An error occured while receiving the frame. */
    24     case STATE_RX_ERROR:
    25         break;
    26 
    27         /* Function called in an illegal state. */
    28     default:
    29         assert( ( eRcvState == STATE_RX_INIT ) ||
    30                 ( eRcvState == STATE_RX_RCV ) || ( eRcvState == STATE_RX_ERROR ) );
    31     }
    32 
    33     vMBPortTimersDisable(  );  //当接收到一帧数据后,禁止3.5T定时器,直到接受下一帧数据开始,开始计时
    34     eRcvState = STATE_RX_IDLE;  //处理完一帧数据,接收器状态为空闲
    35 
    36     return xNeedPoll;
    37 }
    View Code

    4、报文接收

      在定时器第一次中断之后,状态机为eRcvState=STATE_RX_IDLE,即读空闲状态,eMBPoll也阻塞在等待接收完成事件发生。而在eMBPoll之前的eMBRTUStart函数中已经开启了串口中断,因此在接收到数据之后,串口中断将会响应,在串口中断服务函数中将调用接收状态机函数xMBRTUReceiveFSM来接收数据。

    ---xMBRTUReceiveFSM函数

     1 BOOL  xMBRTUReceiveFSM( void )
     2 {
     3     /*函数功能
     4      *1:将接收到的数据存入ucRTUBuf[]中;
     5      *2:usRcvBufferPos为全局变量,表示接收数据的个数;
     6      *3:每接收到一个字节的数据,3.5T定时器清0
     7      */
     8 
     9     BOOL            xTaskNeedSwitch = FALSE;
    10     UCHAR           ucByte;
    11 
    12     assert( eSndState == STATE_TX_IDLE );   //确保没有数据在发送
    13 
    14     /* Always read the character. */
    15     ( void )xMBPortSerialGetByte( ( CHAR * ) & ucByte ); //从串口数据寄存器读取一个字节数据
    16 
    17     switch ( eRcvState )      //根据不同的状态转移
    18     {
    19         /* If we have received a character in the init state we have to
    20          * wait until the frame is finished.
    21          */
    22     case STATE_RX_INIT:
    23         vMBPortTimersEnable(  );       //开启3.5T定时器
    24         break;
    25 
    26         /* In the error state we wait until all characters in the
    27          * damaged frame are transmitted.
    28          */
    29     case STATE_RX_ERROR:            //数据帧被损坏,重启定时器,不保存串口接收的数据
    30         vMBPortTimersEnable(  );
    31         break;
    32 
    33         /* In the idle state we wait for a new character. If a character
    34          * is received the t1.5 and t3.5 timers are started and the
    35          * receiver is in the state STATE_RX_RECEIVCE.
    36          */
    37     case STATE_RX_IDLE:           // 接收器空闲,开始接收,进入STATE_RX_RCV状态
    38         usRcvBufferPos = 0;
    39         ucRTUBuf[usRcvBufferPos++] = ucByte;    //保存数据
    40         eRcvState = STATE_RX_RCV;
    41 
    42         /* Enable t3.5 timers. */
    43         vMBPortTimersEnable(  );          //每收到一个字节,都重启3.5T定时器
    44         break;
    45 
    46         /* We are currently receiving a frame. Reset the timer after
    47          * every character received. If more than the maximum possible
    48          * number of bytes in a modbus frame is received the frame is
    49          * ignored.
    50          */
    51     case STATE_RX_RCV:
    52         if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX )
    53         {
    54             ucRTUBuf[usRcvBufferPos++] = ucByte;   //接收数据
    55         }
    56         else
    57         {
    58             eRcvState = STATE_RX_ERROR;     //一帧报文的字节数大于最大PDU长度,忽略超出的数据
    59         }
    60         vMBPortTimersEnable(  );            //每收到一个字节,都重启3.5T定时器
    61         break;
    62     }
    63     return xTaskNeedSwitch;
    64 }
    View Code

      在串口中断前,状态机为eRcvState=STATE_RX_IDLE,接收状态机开始后,读取uart串口缓存中的数据,并进入STATE_RX_IDLE分支中存储一次数据后开启定时器,然后进入STATE_RX_RCV分支继续接收后续的数据,直至定时器超时!如果没有超时的话,状态不会转换,将还可以继续接收数据。超时之后,在T3.5超时函数xMBRTUTimerT35Expired 中将发送EV_FRAME_RECEIVED事件。然后eMBPoll函数将会调用eMBRTUReceive函数。

    1  /* A frame was received and t35 expired. Notify the listener that
    2          * a new frame was received. */
    3     case STATE_RX_RCV:                        //一帧数据接收完成
    4         xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED );    //上报协议栈事件,接收到一帧完整的数据
    5         break;
    View Code

    ---eMBRTUReceive函数

     1 eMBErrorCode
     2 eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
     3 {
     4     /*eMBPoll函数轮询到EV_FRAME_RECEIVED事件时,调用peMBFrameReceiveCur(),
     5      * 此函数是用户为函数指针peMBFrameReceiveCur()的赋值,
     6      * 此函数完成的功能:
     7      * 从一帧数据报文中,取得modbus从机地址给pucRcvAddress、PDU报文的长度给pusLength,
     8      * PDU报文的首地址给pucFrame,函数*形参全部为地址传递,
     9      */
    10     BOOL            xFrameReceived = FALSE;
    11     eMBErrorCode    eStatus = MB_ENOERR;
    12 
    13     ENTER_CRITICAL_SECTION(  );
    14     assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX );  //断言宏,判断接收到的字节数<256,如果>256,终止程序
    15 
    16     /* Length and CRC check */
    17     if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN )
    18         && ( usMBCRC16( ( UCHAR * ) ucRTUBuf, usRcvBufferPos ) == 0 ) )
    19     {
    20         /* Save the address field. All frames are passed to the upper layed
    21          * and the decision if a frame is used is done there.
    22          */
    23         *pucRcvAddress = ucRTUBuf[MB_SER_PDU_ADDR_OFF]; //保存从站地址
    24 
    25         /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
    26          * size of address field and CRC checksum.
    27          */
    28         *pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC ); //PDU长度
    29 
    30         /* Return the start of the Modbus PDU to the caller. */
    31         *pucFrame = ( UCHAR * ) & ucRTUBuf[MB_SER_PDU_PDU_OFF];  //pucFrame指向PDU起始位置
    32         xFrameReceived = TRUE;
    33     }
    34     else
    35     {
    36         eStatus = MB_EIO;
    37     }
    38 
    39     EXIT_CRITICAL_SECTION(  );
    40     return eStatus;
    41 }
    View Code

    eMBRTUReceive函数完成了CRC校验、帧数据地址和长度的赋值,便于给上层进行处理!之后eMBPoll函数发送 ( void )xMBPortEventPost( EV_EXECUTE )事件。在EV_EXECUTE 事件中,从站对接收到的数据进行处理,包括根据功能码寻找功能函数处理报文和调用eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength ) 发送应答报文。

    --- eMBRTUSend函数

     1 eMBErrorCode  eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
     2 {
     3     /*函数功能
     4      * 1:对响应报文PDU前面加上从机地址;
     5      * 2:对响应报文PDU后加上CRC校;
     6      * 3:使能发送,启动传输;*/
     7     eMBErrorCode    eStatus = MB_ENOERR;
     8     USHORT          usCRC16;
     9 
    10     ENTER_CRITICAL_SECTION(  );
    11 
    12     /* Check if the receiver is still in idle state. If not we where to
    13      * slow with processing the received frame and the master sent another
    14      * frame on the network. We have to abort sending the frame.
    15      */
    16     if( eRcvState == STATE_RX_IDLE )
    17     {
    18         /* First byte before the Modbus-PDU is the slave address. */
    19         pucSndBufferCur = ( UCHAR * ) pucFrame - 1;   //在协议数据单元前加从机地址
    20         usSndBufferCount = 1;
    21 
    22         /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
    23         pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
    24         usSndBufferCount += usLength;
    25 
    26         /* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */
    27         usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );
    28         ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
    29         ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );
    30 
    31         /* Activate the transmitter. */
    32         eSndState = STATE_TX_XMIT;   //发送状态
    33                                                           //以下为新添加
    34         xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );   //发送一个字节的数据,进入发送中断函数,启动传输
    35         pucSndBufferCur++;    /* next byte in sendbuffer. */
    36         usSndBufferCount--;
    37 
    38         vMBPortSerialEnable( FALSE, TRUE );   //使能发送,禁止接收
    39     }
    40     else
    41     {
    42         eStatus = MB_EIO;
    43     }
    44     EXIT_CRITICAL_SECTION(  );
    45     return eStatus;
    46 }
    View Code

    在 eMBRTUSend函数中会调用串口发送数据,在进入串口发送中断后会调用xMBRTUTransmitFSM发送状态机函数发送应答报文。

    ---xMBRTUTransmitFSM函数

     1 BOOL xMBRTUTransmitFSM( void )
     2 {
     3     BOOL            xNeedPoll = FALSE;
     4 
     5     assert( eRcvState == STATE_RX_IDLE );
     6 
     7     switch ( eSndState )
     8     {
     9         /* We should not get a transmitter event if the transmitter is in
    10          * idle state.  */
    11     case STATE_TX_IDLE:
    12         /* enable receiver/disable transmitter. */
    13         vMBPortSerialEnable( TRUE, FALSE );        //发送器处于空闲状态,使能接收,禁止发送
    14         break;
    15 
    16     case STATE_TX_XMIT:               //发送器处于发送状态,在从机发送函数eMBRTUSend中赋值STATE_TX_XMIT
    17         /* check if we are finished. */
    18         if( usSndBufferCount != 0 )        //发送数据
    19         {
    20             xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );
    21             pucSndBufferCur++;  /* next byte in sendbuffer. */
    22             usSndBufferCount--;
    23         }
    24         else              //传递任务,发送完成
    25         {
    26             xNeedPoll = xMBPortEventPost( EV_FRAME_SENT );   //协议栈事件状态赋值为EV_FRAME_SENT,发送完成事件,eMBPoll函数会对此事件进行处理
    27             /* Disable transmitter. This prevents another transmit buffer
    28              * empty interrupt. */
    29             vMBPortSerialEnable( TRUE, FALSE );   //使能接收,禁止发送
    30             eSndState = STATE_TX_IDLE;    //发送器状态为空闲状态
    31         }
    32         break;
    33     }
    34 
    35     return xNeedPoll;
    36 }
    View Code

    至此:协议栈准备工作,从机接受报文,解析报文,从机发送响应报文四部分结束。

    原贴链接:  http://www.cnblogs.com/wujing-hubei/p/5935142.html 

    参考:

    freemodbus库函数详解 FreeModbus源码详解-程序设计  

    Freemodbus完全分析_百度文库  

    FreeModbus学习笔记_百度文库 

  • 相关阅读:
    MapReduce案例
    Hive学习笔记九
    大数据技术之Hive
    Hive学习笔记八
    Hive学习笔记七
    Hive学习笔记六
    大数据应用技术课程实践--选题与实践方案
    15.手写数字识别-小数据集
    14.深度学习-卷积
    13.垃圾邮件分类2
  • 原文地址:https://www.cnblogs.com/TonyJia/p/12715327.html
Copyright © 2020-2023  润新知