本篇按照FreeModbus协议栈的工作流程,对源代码进行总结解析;FreeModbus协议栈作为从机,等待主机传送的数据,当从机接收到一帧完整的报文后,对报文进行解析,然后响应主机,发送报文给主机,实现主机和从机之间的通信;

1:demo.c中三个函数,完成协议栈的准备工作;

eMBInit()函数:(mb.c)

[cpp] view plain copy

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

eMBEnable()函数:(mb.c)

[cpp] view plain copy

  1. /*函数功能
  2. *1:设置Modbus协议栈工作状态eMBState为STATE_ENABLED;
  3. *2:调用pvMBFrameStartCur()函数激活协议栈
  4. */
  5. eMBErrorCode
  6. eMBEnable( void )
  7. {
  8. eMBErrorCode    eStatus = MB_ENOERR;
  9. if( eMBState == STATE_DISABLED )
  10. {
  11. /* Activate the protocol stack. */
  12. pvMBFrameStartCur(  );  /*pvMBFrameStartCur = eMBRTUStart;调用eMBRTUStart函数*/
  13. eMBState = STATE_ENABLED;
  14. }
  15. else
  16. {
  17. eStatus = MB_EILLSTATE;
  18. }
  19. return eStatus;
  20. }

eMBRTUStart()函数:(mbrtu.c)

[cpp] view plain copy

  1. /*函数功能
  2. *1:设置接收状态机eRcvState为STATE_RX_INIT;
  3. *2:使能串口接收,禁止串口发送,作为从机,等待主机传送的数据;
  4. *3:开启定时器,3.5T时间后定时器发生第一次中断,此时eRcvState为STATE_RX_INIT,上报初始化完成事件,然后设置eRcvState为空闲STATE_RX_IDLE;
  5. *4:每次进入3.5T定时器中断,定时器被禁止,等待串口有字节接收后,才使能定时器;
  6. */
  7. void
  8. eMBRTUStart( void )
  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. EXIT_CRITICAL_SECTION( );
  20. }

eMBPoll()函数:(mb.c)

[cpp] view plain copy

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

至此:完成Modbus协议栈的初始化准备工作,eMBPoll()函数轮询等待接收完成事件发生,接收机状态eRcvState为STATE_RX_IDLE空闲;

2:FreeModbus协议栈接收一帧完整报文机制:

FreeModbus协议栈通过淳口中断接收一帧数据,用户需在串口接收中断中回调prvvUARTRxISR()函数;

prvvUARTRxISR()函数:(portserial.c)

[cpp] view plain copy

  1. static void prvvUARTRxISR( void )
  2. {
  3. pxMBFrameCBByteReceived(  );
  4. }

在第一阶段中eMBInit()函数中赋值pxMBFrameCBByteReceived = xMBRTUReceiveFSM,发生接收中断时,最终调用xMBRTUReceiveFSM函数对数据进行接收;

xMBRTUReceiveFSM()函数:(mbrtu.c)

[cpp] view plain copy

  1. /*函数功能
  2. *1:将接收到的数据存入ucRTUBuf[]中;
  3. *2:usRcvBufferPos为全局变量,表示接收数据的个数;

[cpp] view plain copy

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

当主机发送一帧完整的报文后,3.5T定时器中断发生,定时器中断最终回调xMBRTUTimerT35Expired函数;

xMBRTUTimerT35Expired()函数:(mbrtu.c)

[cpp] view plain copy

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

至此:从机接收到一帧完整的报文,存储在ucRTUBuf[MB_SER_PDU_SIZE_MAX]全局变量中,定时器禁止,接收机状态为空闲;

3:解析报文机制

在第二阶段,从机接收到一帧完整的报文后,上报“接收到报文”事件,eMBPoll函数轮询,发现“接收到报文”事件发生,调用peMBFrameReceiveCur函数,此函数指针在eMBInit被赋值eMBRTUReceive函数,最终调用eMBRTUReceive函数,从ucRTUBuf中取得从机地址、PDU单元和PDU单元的长度,然后判断从机地址地是否一致,若一致,上报“报文解析事件”EV_EXECUTE,(xMBPortEventPost( EV_EXECUTE ));“报文解析事件”发生后,根据功能码,调用xFuncHandlers[i].pxHandler( ucMBFrame, &usLength )对报文进行解析,此过程全部在eMBPoll函数中执行;

eMBPoll()函数:(mb.c)

[cpp] view plain copy

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

eMBRTUReceive()函数:(mbrtu.c)

[cpp] view plain copy

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

xMBPortEventPost()函数:(portevent.c)

[cpp] view plain copy

  1. BOOL
  2. xMBPortEventPost( eMBEventType eEvent )
  3. {
  4. xEventInQueue = TRUE;
  5. eQueuedEvent = eEvent;
  6. return TRUE;
  7. }

xFuncHandlers[i]是结构体数组,存放的是功能码以及对应的报文解析函数,原型如下:

[cpp] view plain copy

  1. typedef struct
  2. {
  3. UCHAR           ucFunctionCode;
  4. pxMBFunctionHandler pxHandler;
  5. } xMBFunctionHandler;

以下列举读线圈函数举例:

eMBFuncReadCoils()读线圈寄存器函数: (mbfunccoils.c)

[cpp] view plain copy

  1. #if MB_FUNC_READ_COILS_ENABLED > 0
  2. eMBException
  3. eMBFuncReadCoils( UCHAR * pucFrame, USHORT * usLen )
  4. {
  5. USHORT          usRegAddress;
  6. USHORT          usCoilCount;
  7. UCHAR           ucNBytes;
  8. UCHAR          *pucFrameCur;
  9. eMBException    eStatus = MB_EX_NONE;
  10. eMBErrorCode    eRegStatus;
  11. if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) )
  12. {
  13. /*线圈寄存器的起始地址*/
  14. usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 );
  15. usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] );
  16. //usRegAddress++;
  17. /*线圈寄存器个数*/
  18. usCoilCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF] << 8 );
  19. usCoilCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF + 1] );
  20. /* Check if the number of registers to read is valid. If not
  21. * return Modbus illegal data value exception.
  22. */
  23. /*判断线圈寄存器个数是否合理*/
  24. if( ( usCoilCount >= 1 ) &&
  25. ( usCoilCount < MB_PDU_FUNC_READ_COILCNT_MAX ) )
  26. {
  27. /* Set the current PDU data pointer to the beginning. */
  28. /*为发送缓冲pucFrameCur赋值*/
  29. pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
  30. *usLen = MB_PDU_FUNC_OFF;
  31. /* First byte contains the function code. */
  32. /*响应报文第一个字节赋值为功能码0x01*/
  33. *pucFrameCur++ = MB_FUNC_READ_COILS;
  34. *usLen += 1;
  35. /* Test if the quantity of coils is a multiple of 8. If not last
  36. * byte is only partially field with unused coils set to zero. */
  37. /*usCoilCount%8有余数,ucNBytes加1,不够的位填充0*/
  38. if( ( usCoilCount & 0x0007 ) != 0 )
  39. {
  40. ucNBytes = ( UCHAR )( usCoilCount / 8 + 1 );
  41. }
  42. else
  43. {
  44. ucNBytes = ( UCHAR )( usCoilCount / 8 );
  45. }
  46. *pucFrameCur++ = ucNBytes;
  47. *usLen += 1;
  48. eRegStatus =
  49. eMBRegCoilsCB( pucFrameCur, usRegAddress, usCoilCount,
  50. MB_REG_READ );
  51. /* If an error occured convert it into a Modbus exception. */
  52. if( eRegStatus != MB_ENOERR )
  53. {
  54. eStatus = prveMBError2Exception( eRegStatus );
  55. }
  56. else
  57. {
  58. /* The response contains the function code, the starting address
  59. * and the quantity of registers. We reuse the old values in the
  60. * buffer because they are still valid. */
  61. *usLen += ucNBytes;;
  62. }
  63. }
  64. else
  65. {
  66. eStatus = MB_EX_ILLEGAL_DATA_VALUE;
  67. }
  68. }
  69. else
  70. {
  71. /* Can't be a valid read coil register request because the length
  72. * is incorrect. */
  73. eStatus = MB_EX_ILLEGAL_DATA_VALUE;
  74. }
  75. return eStatus;
  76. }

至此:报文解析结束,得到ucMBFrame响应缓冲和usLength响应报文长度,等待发送报文;

4:发送响应报文

解析完一帧完整的报文后,eMBPoll()函数中调用peMBFrameSendCur()函数进行响应,eMBFrameSendCur()是函数指针,最终会调用eMBRTUSend()函数发送响应;

eMBRTUSend()函数:

[cpp] view plain copy

  1. /*函数功能
  2. *1:对响应报文PDU前面加上从机地址;
  3. *2:对响应报文PDU后加上CRC校;
  4. *3:使能发送,启动传输;
  5. */
  6. eMBErrorCode
  7. eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
  8. {
  9. eMBErrorCode    eStatus = MB_ENOERR;
  10. USHORT          usCRC16;
  11. ENTER_CRITICAL_SECTION(  );
  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. /*在协议数据单元前加从机地址*/
  20. pucSndBufferCur = ( UCHAR * ) pucFrame - 1;
  21. usSndBufferCount = 1;
  22. /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
  23. pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
  24. usSndBufferCount += usLength;
  25. /* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */
  26. usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );
  27. ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
  28. ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );
  29. /* Activate the transmitter. */
  30. eSndState = STATE_TX_XMIT;                         //发送状态
  31. xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );  /*发送一个字节的数据,进入发送中断函数,启动传输*/
  32. pucSndBufferCur++;                                 /* next byte in sendbuffer. */
  33. usSndBufferCount--;
  34. vMBPortSerialEnable( FALSE, TRUE );                /*使能发送,禁止接收*/
  35. }
  36. else
  37. {
  38. eStatus = MB_EIO;
  39. }
  40. EXIT_CRITICAL_SECTION(  );
  41. return eStatus;
  42. }

进入发送中断,串口发送中断中调用prvvUARTTxReadyISR()函数,继续调用pxMBFrameCBTransmitterEmpty()函数,pxMBFrameCBTransmitterEmpty为函数指针,最终调用xMBRTUTransmitFSM()函数;

xMBRTUTransmitFSM()函数:(mbrtu.c)

[cpp] view plain copy

  1. BOOL
  2. xMBRTUTransmitFSM( void )
  3. {
  4. BOOL            xNeedPoll = FALSE;
  5. assert( eRcvState == STATE_RX_IDLE );
  6. switch ( eSndState )
  7. {
  8. /* We should not get a transmitter event if the transmitter is in
  9. * idle state.*/
  10. case STATE_TX_IDLE:        /*发送器处于空闲状态,使能接收,禁止发送*/
  11. /* enable receiver/disable transmitter. */
  12. vMBPortSerialEnable( TRUE, FALSE );
  13. break;
  14. case STATE_TX_XMIT:        /*发送器处于发送状态,在从机发送函数eMBRTUSend中赋值STATE_TX_XMIT*/
  15. /* check if we are finished. */
  16. if( usSndBufferCount != 0 )
  17. {
  18. //发送数据
  19. xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );
  20. pucSndBufferCur++;  /* next byte in sendbuffer. */
  21. usSndBufferCount--;
  22. }
  23. else
  24. {
  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. return xNeedPoll;
  35. }

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

FreeModbus源码详解相关推荐

  1. 【Live555】live555源码详解(九):ServerMediaSession、ServerMediaSubsession、live555MediaServer

    [Live555]live555源码详解系列笔记 继承协作关系图 下面红色表示本博客将要介绍的三个类所在的位置: ServerMediaSession.ServerMediaSubsession.Dy ...

  2. 【Live555】live555源码详解系列笔记

    [Live555]liveMedia下载.配置.编译.安装.基本概念 [Live555]live555源码详解(一):BasicUsageEnvironment.UsageEnvironment [L ...

  3. 【Live555】live555源码详解(八):testRTSPClient

    [Live555]live555源码详解系列笔记 继承协作关系图 下面红色表示本博客将要介绍的testRTSPClient实现的三个类所在的位置: ourRTSPClient.StreamClient ...

  4. 【Live555】live555源码详解(七):GenericMediaServer、RTSPServer、RTSPClient

    [Live555]live555源码详解系列笔记 继承协作关系图 下面红色表示本博客将要介绍的三个类所在的位置: GenericMediaServer.RTSPServer.RTSPClient 14 ...

  5. 【Live555】live555源码详解(六):FramedSource、RTPSource、RTPSink

    [Live555]live555源码详解系列笔记 继承协作关系图 下面红色表示本博客将要介绍的三个类所在的位置: FramedSource.RTPSource.RTPSink 11.FramedSou ...

  6. 【Live555】live555源码详解(五):MediaSource、MediaSink、MediaSession、MediaSubsession

    [Live555]live555源码详解系列笔记 继承协作关系图 下面红色表示本博客将要介绍的四个类所在的位置: MediaSource.MediaSink.MediaSession.MediaSub ...

  7. 【Live555】live555源码详解(四):Medium媒体基础类

    [Live555]live555源码详解系列笔记 7.Media Medai所依赖关系图 依赖Medai关系图 Media和UsageEnvironment关联图

  8. 【Live555】live555源码详解(二):BasicHashTable、DelayQueue、HandlerSet

    [Live555]live555源码详解系列笔记 3.BasicHashTable 哈希表 协作图: 3.1 BasicHashTable BasicHashTable 继承自 HashTable 重 ...

  9. 【Live555】live555源码详解(一):BasicUsageEnvironment、UsageEnvironment

    [Live555]live555源码详解系列笔记 类关系图 1.UsageEnvironment 详解 1.1 BasicUsageEnvironment BasicUsageEnvironment ...

最新文章

  1. 算法------判定字符是否唯一
  2. Kaggle | Bengali 比赛总结(孟加拉字符分类 )
  3. 十大滤波算法程序大全
  4. 单链表逆置 java_单链表的就地逆置--java实现(含头节点和不包含头节点)
  5. Loj2687,jzoj3320-文本编辑器【线头dp】
  6. 怎样判断一个网站是不是前后端分离的?
  7. comsol显示电场计算结果_在 COMSOL 中构建磁流体动力学多物理场模型
  8. laravel小记(composer 安装和predis使用)
  9. 香港年轻人买房压力有多大
  10. EMQ MQTT云服务器搭建 - 阿里云轻量应用服务器
  11. 真赞!阿里开源的这款分布式事务框架,不愧为民族之光
  12. ValueError: This model has not yet been built. Build the model first by calling `build()` or calling
  13. 树线段hdu 4508 美素数(线段树)
  14. Jquery 强大的表单验证操作
  15. bzoj1076 奖励关(概率dp)(状态压缩)
  16. 处女的第一次不一定会流血!很感人 我都流泪了!
  17. webstorm激活破解
  18. 无需密码自己卸载深信服EDR软件
  19. 完美解决Win10 X64非原装版系统打不开CHM文件
  20. 继续:个人微信的自动收款解决(思路)

热门文章

  1. 反恐精英的服务器存在哪个文件夹,反恐精英地图放在哪里?CS1.6地图放置位置详细介绍...
  2. CAD制图初学入门学习了解教程
  3. 56页智慧园区智能化弱电设计方案-智慧园区整体解决方案
  4. 设计原则:开闭原则(OCP)
  5. WINDOWS开机11大进程
  6. 股票 MACD指标的买入形态图解
  7. 2009年网易笔试题
  8. Lotus Notes中文档查询(转)
  9. 基于Windows api手柄映射编程
  10. 【三方面了解U盘芯片类型】