logo资料库

FreeModbus--完全分析.pdf

第1页 / 共10页
第2页 / 共10页
第3页 / 共10页
第4页 / 共10页
第5页 / 共10页
第6页 / 共10页
第7页 / 共10页
第8页 / 共10页
资料共10页,剩余部分请下载后查看
FreeModbus--完全分析 说明:freemodbus-v1.5.0 主流程 [objc] view plaincopy 1. /* ----------------------- Start implementation ---------------------------- -*/ 2. int 3. main( void ) 4. { 5. eMBErrorCode eStatus; 6. 7. eStatus = eMBInit( MB_RTU, 0x0A, 0, 38400, MB_PAR_EVEN ); 8. 9. /* Enable the Modbus Protocol Stack. */ 10. eStatus = eMBEnable( ); 11. 12. for( ;; ) 13. { 14. ( void )eMBPoll( ); 15. 16. /* Here we simply count the number of poll cycles. */ 17. usRegInputBuf[0]++; 18. } 19. } 由上述主函数可知协议栈经 eMBInit 和 eMBEnable 初始化、使能后进入 协议栈的循环 eMBPoll 中。 eMBInit 分析 首先,使用 eMBInit 初始化协议栈,根据你使用的参数 eMBMode eMode 初始化相应的函数入口! [objc] view plaincopy
1. #if MB_RTU_ENABLED > 0 2. case MB_RTU: 3. pvMBFrameStartCur = eMBRTUStart; 4. pvMBFrameStopCur = eMBRTUStop; 5. peMBFrameSendCur = eMBRTUSend; 6. peMBFrameReceiveCur = eMBRTUReceive; 7. pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL; 8. pxMBFrameCBByteReceived = xMBRTUReceiveFSM; 9. pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM; 10. pxMBPortCBTimerExpired = xMBRTUTimerT35Expired; 11. 12. eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity ) ; 13. break; 14. #endif 15. #if MB_ASCII_ENABLED > 0 16. case MB_ASCII: 17. pvMBFrameStartCur = eMBASCIIStart; 18. pvMBFrameStopCur = eMBASCIIStop; 19. peMBFrameSendCur = eMBASCIISend; 20. peMBFrameReceiveCur = eMBASCIIReceive; 21. pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL; 22. pxMBFrameCBByteReceived = xMBASCIIReceiveFSM; 23. pxMBFrameCBTransmitterEmpty = xMBASCIITransmitFSM; 24. pxMBPortCBTimerExpired = xMBASCIITimerT1SExpired; 25. 26. eStatus = eMBASCIIInit( ucMBAddress, ucPort, ulBaudRate, eParity ); 27. break; 28. #endif 以上代码中 pvMBFrameStartCur、pvMBFrameStopCur 等即协议栈函 数的接口,对于不同模式使用不同的函数进行赋值初始化!!此编写模 式可以借鉴学习!! 其中 eMBRTUInit 函数对底层驱动(串口和定时器)进行了初始化。 在上述初始化完成并且成功后对事件功能也进了初始化,最后全局变量 eMBState = STATE_DISABLED。 eMBEnable 的分析
[objc] view plaincopy 1. eMBErrorCode 2. eMBEnable( void ) 3. { 4. eMBErrorCode eStatus = MB_ENOERR; 5. 6. if( eMBState == STATE_DISABLED ) 7. { 8. /* Activate the protocol stack. */ 9. pvMBFrameStartCur( ); 10. eMBState = STATE_ENABLED; 11. } 12. else 13. { 14. eStatus = MB_EILLSTATE; 15. } 16. return eStatus; 17. } 由第一节的分析,此时将启动协议栈 pvMBFrameStartCur,查看程序 该函数指针被分配到为 eMBRTUStart。 该函数中将全局变量 eRcvState = STATE_RX_INIT,并使能串口和定 时器,注意此时的定时开始工作!!! 全局变量 eMBState =STATE_ENABLED。 eMBPoll 的分析 在此循环函数中 xMBPortEventGet(&eEvent ) == TRUE 先判断是否有事件,无事件发生则 不进入状态机! 还记得第二节定时器开始工作了吗?我们先看看该定时器如果超时了会发生什么事件! 在超时中断中我们将会调用 pxMBPortCBTimerExpired 函数,其中有以下代码: [objc] view plaincopy 1. BOOL 2. xMBRTUTimerT35Expired( void ) 3. { 4. BOOL xNeedPoll = FALSE; 5.
6. switch ( eRcvState ) 7. { 8. /* Timer t35 expired. Startup phase is finished. */ 9. case STATE_RX_INIT: 10. xNeedPoll = xMBPortEventPost( EV_READY ); 11. break; 12. 13. /* A frame was received and t35 expired. Notify the listener that 14. * a new frame was received. */ 15. case STATE_RX_RCV: 16. xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED ); 17. break; 18. 19. /* An error occured while receiving the frame. */ 20. case STATE_RX_ERROR: 21. break; 22. 23. /* Function called in an illegal state. */ 24. default: 25. assert( ( eRcvState == STATE_RX_INIT ) || 26. ( eRcvState == STATE_RX_RCV ) || ( eRcvState == STATE_RX_ERR OR ) ); 27. } 28. 29. vMBPortTimersDisable( ); 30. eRcvState = STATE_RX_IDLE; 31. 32. return xNeedPoll; 33. } 上一节分析中全局变量 eRcvState =STATE_RX_INIT,因此第二节所说的定时器第一次超 时将会发送 xNeedPoll =xMBPortEventPost( EV_READY )事件, 然后关闭定时器,全局变量 eRcvState =STATE_RX_IDLE。此时,在主循环 eMBPoll 中将 会执行一次 EV_READY 下的操作, 之后会一直执行 eMBPoll,整个协议栈开始运行! 接收数据分析 由于 FreeModbus 只支持从机模式,因此我们分析一下其在接收到数据后的操作!!! 接收数据 在上三节的操作中,我们可以知道进入 eMBPoll 循环后,串口中断是开启的。因此在接收 到数据的时候,首先响应的应该是串口中断程序。 接收中断中将会调用接收状态机: [objc] view plaincopy
1. BOOL 2. xMBRTUReceiveFSM( void ) 3. { 4. BOOL xTaskNeedSwitch = FALSE; 5. UCHAR ucByte; 6. 7. assert( eSndState == STATE_TX_IDLE ); 8. 9. /* Always read the character. */ 10. ( void )xMBPortSerialGetByte( ( CHARCHAR * ) & ucByte ); 11. 12. switch ( eRcvState ) 13. { 14. /* If we have received a character in the init state we have to 15. * wait until the frame is finished. 16. */ 17. case STATE_RX_INIT: 18. vMBPortTimersEnable( ); 19. break; 20. 21. /* In the error state we wait until all characters in the 22. * damaged frame are transmitted. 23. */ 24. case STATE_RX_ERROR: 25. vMBPortTimersEnable( ); 26. break; 27. 28. /* In the idle state we wait for a new character. If a character 29. * is received the t1.5 and t3.5 timers are started and the 30. * receiver is in the state STATE_RX_RECEIVCE. 31. */ 32. case STATE_RX_IDLE: 33. usRcvBufferPos = 0; 34. ucRTUBuf[usRcvBufferPos++] = ucByte; 35. eRcvState = STATE_RX_RCV; 36. 37. /* Enable t3.5 timers. */ 38. vMBPortTimersEnable( ); 39. break; 40. 41. /* We are currently receiving a frame. Reset the timer after 42. * every character received. If more than the maximum possible 43. * number of bytes in a modbus frame is received the frame is 44. * ignored.
45. */ 46. case STATE_RX_RCV: 47. if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ) 48. { 49. ucRTUBuf[usRcvBufferPos++] = ucByte; 50. } 51. else 52. { 53. eRcvState = STATE_RX_ERROR; 54. } 55. vMBPortTimersEnable( ); 56. break; 57. } 58. return xTaskNeedSwitch; 59. } 经过第 3 节的分析,此时全局变量 eRcvState =STATE_RX_IDLE。接收状态机开始后,读 取 UART 串口缓存中的数据, 并进入 STATE_RX_IDLE 分支中存储一次数据后开启超时定时器,进入 STATE_RX_RCV 分支继续接收后续的数据, 直至定时器超时!为什么要等待超时才能停止接收呢! [objc] view plaincopy 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; 可以发现接收数据时发生超时后,协议栈会发送 EV_FRAME_RECEIVED 接收完成这个信 号。此时 eMBPoll 接收到此信号后会调用 eMBRTUReceive 函数。 [objc] view plaincopy 1. eMBErrorCode 2. eMBRTUReceive( UCHARUCHAR * pucRcvAddress, UCHARUCHAR ** pucFrame, USHORTUSH ORT * pusLength ) 3. { 4. BOOL xFrameReceived = FALSE; 5. eMBErrorCode eStatus = MB_ENOERR; 6. 7. ENTER_CRITICAL_SECTION( ); 8. assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ); 9.
10. /* Length and CRC check */ 11. if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN ) 12. && ( usMBCRC16( ( UCHARUCHAR * ) ucRTUBuf, usRcvBufferPos ) == 0 ) ) 13. { 14. /* Save the address field. All frames are passed to the upper layed 15. * and the decision if a frame is used is done there. 16. */ 17. *pucRcvAddress = ucRTUBuf[MB_SER_PDU_ADDR_OFF]; 18. 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_SE R_PDU_SIZE_CRC ); 23. 24. /* Return the start of the Modbus PDU to the caller. */ 25. *pucFrame = ( UCHARUCHAR * ) & ucRTUBuf[MB_SER_PDU_PDU_OFF]; 26. xFrameReceived = TRUE; 27. } 28. else 29. { 30. eStatus = MB_EIO; 31. } 32. 33. EXIT_CRITICAL_SECTION( ); 34. return eStatus; 35. } eMBRTUReceive 函数完成了 CRC 校验、帧数据地址、长度的赋值,便于给上层进行处理! 之后发送( void)xMBPortEventPost( EV_EXECUTE )事件。 处理数据时根据功能码调用相应的函数,这些函数存储在 xFuncHandlers 数组中!之后发 送响应!完成一次操作! 功能码 0x04 读输入寄存器 在一个远程设备中,使用该功能码读取 1 至大约 125 的连续输入寄存 器。请求 PDU 说明了起始地址和寄存器数量。 将响应报文中的寄存器数据分成每个寄存器为两字节,在每个字节中直 接地调整二进制内容。
对于每个寄存器,第一个字节包括高位比特,并且第二个字节包括低位 比特。 实例: 以下是 FreeModbus 的代码: [objc] view plaincopy 1. eMBException 2. eMBFuncReadInputRegister( UCHARUCHAR * pucFrame, USHORTUSHORT * usLen ) 3. { 4. USHORT usRegAddress; 5. USHORT usRegCount; 6. UCHAR *pucFrameCur; 7. 8. eMBException eStatus = MB_EX_NONE; 9. eMBErrorCode eRegStatus; 10. 11. if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) ) 12. { 13. usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 ); 14. usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] ); 15. usRegAddress++; 16. 17. usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8 ); 18. usRegCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1] ); 19. 20. /* Check if the number of registers to read is valid. If not 21. * return Modbus illegal data value exception. 22. */ 23. if( ( usRegCount >= 1 )
分享到:
收藏