注:转载请注明出处   http://www.cnblogs.com/wujing-hubei/p/5935142.html

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

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

  1. eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
  2. {
  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;
  1. /* check preconditions */
  2. if( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) ||
  3. ( ucSlaveAddress < MB_ADDRESS_MIN ) || ( ucSlaveAddress > MB_ADDRESS_MAX ) ) //验证从机地址
  4. {
  5. eStatus = MB_EINVAL; //地址错误
  6. }
  7. else
  8. {
  9. ucMBAddress = ucSlaveAddress;
  10.  
  11. switch ( eMode )
  12. {
  13. #if MB_RTU_ENABLED > 0
  14. case MB_RTU:
  15. pvMBFrameStartCur = eMBRTUStart;
  16. pvMBFrameStopCur = eMBRTUStop;
  17. peMBFrameSendCur = eMBRTUSend;
  18. peMBFrameReceiveCur = eMBRTUReceive;
  19. pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
  20. pxMBFrameCBByteReceived = xMBRTUReceiveFSM; //接收状态机,串口接受中断最终调用此函数接收数据
  21. pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM; //发送状态机,串口发送中断最终调用此函数发送数据
  22. pxMBPortCBTimerExpired = xMBRTUTimerT35Expired; //报文到达间隔检查,定时器中断函数最终调用次函数完成定时器中断
  23.  
  24. eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );
  25. break;
  26. #endif
  27. #if MB_ASCII_ENABLED > 0
  28. case MB_ASCII:
  29. pvMBFrameStartCur = eMBASCIIStart;
  30. pvMBFrameStopCur = eMBASCIIStop;
  31. peMBFrameSendCur = eMBASCIISend;
  32. peMBFrameReceiveCur = eMBASCIIReceive;
  33. pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
  34. pxMBFrameCBByteReceived = xMBASCIIReceiveFSM;
  35. pxMBFrameCBTransmitterEmpty = xMBASCIITransmitFSM;
  36. pxMBPortCBTimerExpired = xMBASCIITimerT1SExpired;
  37.  
  38. eStatus = eMBASCIIInit( ucMBAddress, ucPort, ulBaudRate, eParity );
  39. break;
  40. #endif
  41. default:
  42. eStatus = MB_EINVAL;
  43. }
  44.  
  45. if( eStatus == MB_ENOERR )
  46. {
  47. if( !xMBPortEventInit( ) )
  48. {
  49. /* port dependent event module initalization failed. */
  50. eStatus = MB_EPORTERR;
  51. }
  52. else
  53. {//设定当前状态
  54. eMBCurrentMode = eMode; //设定RTU模式
  55. eMBState = STATE_DISABLED; //modbus协议栈初始化状态,在此初始化为禁止
  56. }
  57. }
  58. }
  59. return eStatus;
  60. }
  1.   eMBInit函数中pvMBFrameStartCurpvMBFrameStartCur等为协议栈函数的接口,对于不同的通信模式使用不同的函数进行初始化!!!此编程模式可以借鉴学习!!!
  1.   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. }

---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. }

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

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

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

  

  在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("\r\nTimer Expired:\n\n\r");
  6. vMBPortSetWithinException( TRUE );
  7. GPT_ClearStatusFlag(BOARD_GPTA_BASEADDR, gptStatusFlagOutputCompare1); //关闭定时器
  1. ( void )pxMBPortCBTimerExpired( ); //事件转换 vMBPortSetWithinException( FALSE );
    }

---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. }

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 = ;
  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. }

  在串口中断前,状态机为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;

---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 ) == ) )
  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. }

  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 - ; //在协议数据单元前加从机地址
  20. usSndBufferCount = ;
  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 >> );
  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. }

在 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 != ) //发送数据
  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. }

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

参考:

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

Freemodbus完全分析_百度文库

FreeModbus学习笔记_百度文库

freemodbus-v1.5.0 源码分析的更多相关文章

  1. AFNetWorking3.0源码分析

    分析: AFNetWorking(3.0)源码分析(一)——基本框架 AFNetworking源码解析 AFNetworking2.0源码解析<一> end

  2. Solr5.0源码分析-SolrDispatchFilter

    年初,公司开发法律行业的搜索引擎.当时,我作为整个系统的核心成员,选择solr,并在solr根据我们的要求做了相应的二次开发.但是,对solr的还没有进行认真仔细的研究.最近,事情比较清闲,翻翻sol ...

  3. Solr4.8.0源码分析(25)之SolrCloud的Split流程

    Solr4.8.0源码分析(25)之SolrCloud的Split流程(一) 题记:昨天有位网友问我SolrCloud的split的机制是如何的,这个还真不知道,所以今天抽空去看了Split的原理,大 ...

  4. Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五)

    Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五) 题记:关于SolrCloud的Recovery策略已经写了四篇了,这篇应该是系统介绍Recovery策略的最后一篇了 ...

  5. Solr4.8.0源码分析(23)之SolrCloud的Recovery策略(四)

    Solr4.8.0源码分析(23)之SolrCloud的Recovery策略(四) 题记:本来计划的SolrCloud的Recovery策略的文章是3篇的,但是没想到Recovery的内容蛮多的,前面 ...

  6. Solr4.8.0源码分析(22)之SolrCloud的Recovery策略(三)

    Solr4.8.0源码分析(22)之SolrCloud的Recovery策略(三) 本文是SolrCloud的Recovery策略系列的第三篇文章,前面两篇主要介绍了Recovery的总体流程,以及P ...

  7. Solr4.8.0源码分析(21)之SolrCloud的Recovery策略(二)

    Solr4.8.0源码分析(21)之SolrCloud的Recovery策略(二) 题记:  前文<Solr4.8.0源码分析(20)之SolrCloud的Recovery策略(一)>中提 ...

  8. Solr4.8.0源码分析(20)之SolrCloud的Recovery策略(一)

    Solr4.8.0源码分析(20)之SolrCloud的Recovery策略(一) 题记: 我们在使用SolrCloud中会经常发现会有备份的shard出现状态Recoverying,这就表明Solr ...

  9. Solr4.8.0源码分析(14)之SolrCloud索引深入(1)

    Solr4.8.0源码分析(14) 之 SolrCloud索引深入(1) 上一章节<Solr In Action 笔记(4) 之 SolrCloud分布式索引基础>简要学习了SolrClo ...

  10. Solr4.8.0源码分析(15) 之 SolrCloud索引深入(2)

    Solr4.8.0源码分析(15) 之 SolrCloud索引深入(2) 上一节主要介绍了SolrCloud分布式索引的整体流程图以及索引链的实现,那么本节开始将分别介绍三个索引过程即LogUpdat ...

随机推荐

  1. Autofac和DynamicProxy2搭配实现Aop动手训练

    http://www.cnblogs.com/zhengwl/p/5433181.html Aop含义:aspect-oriented programming 实现工具介绍 Autofac是一个比较流 ...

  2. AndroidManiFast 字段意义

    每个Activity都要在本文件中注册. <Activity>下的<Intent-filter>中. 两个字段的意思是: <action android:name=&qu ...

  3. django_web代码更新

  4. STM32堆栈溢出

    在使用STM32读取SD Card的文件时,总是会卡死在读函数那里 res = f_read(&fsrc, gbuffer, sizeof(gbuffer)-1, &br); 而且出现 ...

  5. Java 8 Optional类深度解析

    身为一名Java程序员,大家可能都有这样的经历:调用一个方法得到了返回值却不能直接将返回值作为参数去调用别的方法.我们首先要判断这个返回值是否为null,只有在非空的前提下才能将其作为其他方法的参数. ...

  6. Sql Server作业

    参考资料: http://jingyan.baidu.com/article/49ad8bce7287315834d8fab4.html

  7. Struts2 自定义拦截器

    自定义拦截器(权限管理),包含了对ajax和表单请求的拦截 package com.interceptor; import java.io.IOException; import java.io.Pr ...

  8. ADB常用命令(Android Debug Bridge)

    首先要配置环境变量. adb devices 列出所有的设备 adb start-server 开启adb服务 adb kill-server    关闭adb服务 adb logcat    查看L ...

  9. CSS文档流

    文档流 将窗体自上而下分成一行行, 并在每行中按从左至右的顺序排放元素,即为文档流. 每个非浮动块级元素都独占一行, 浮动元素则按规定浮在行的一端. 若当前行容不下, 则另起新行再浮动. 内联元素也不 ...

  10. github及其他记录

    http://mvnrepository.com/artifact/org.jdom/jdom/1.1.3 https://github.com/open-power-workgroup/Hospit ...