FreeModbus源码详解(转)

原文链接:

https://blog.csdn.net/golf_research/article/details/52240739

本篇按照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.     //验证从机地址  
  14.     if( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) ||  
  15.         ( ucSlaveAddress < MB_ADDRESS_MIN ) || ( ucSlaveAddress > MB_ADDRESS_MAX ))  
  16.     {  
  17.         eStatus = MB_EINVAL;  
  18.     }  
  19.     else  
  20.     {  
  21.         ucMBAddress = ucSlaveAddress;            /*从机地址的赋值*/  
  22.               
  23.         switch ( eMode )  
  24.         {  
  25. #if MB_RTU_ENABLED > 0  
  26.         case MB_RTU:  
  27.             pvMBFrameStartCur = eMBRTUStart;      /*使能modbus协议栈*/  
  28.             pvMBFrameStopCur = eMBRTUStop;        /*禁用modbus协议栈*/  
  29.             peMBFrameSendCur = eMBRTUSend;        /*modbus从机响应函数*/  
  30.             peMBFrameReceiveCur = eMBRTUReceive;  /*modbus报文接收函数*/  
  31.             pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;  
  32.             //接收状态机  
  33.             pxMBFrameCBByteReceived =     xMBRTUReceiveFSM;   /*串口接收中断最终调用此函数接收数据*/  
  34.             //发送状态机  
  35.             pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;  /*串口发送中断最终调用此函数发送数据*/  
  36.             //报文到达间隔检查  
  37.             pxMBPortCBTimerExpired =      xMBRTUTimerT35Expired; /*定时器中断函数最终调用次函数完成定时器中断*/  
  38.             //初始化RTU  
  39.             eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );  
  40.             break;  
  41. #endif  
  42. #if MB_ASCII_ENABLED > 0  
  43.         case MB_ASCII:  
  44.             pvMBFrameStartCur = eMBASCIIStart;  
  45.             pvMBFrameStopCur = eMBASCIIStop;  
  46.             peMBFrameSendCur = eMBASCIISend;  
  47.             peMBFrameReceiveCur = eMBASCIIReceive;  
  48.             pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;  
  49.             pxMBFrameCBByteReceived = xMBASCIIReceiveFSM;  
  50.             pxMBFrameCBTransmitterEmpty = xMBASCIITransmitFSM;  
  51.             pxMBPortCBTimerExpired = xMBASCIITimerT1SExpired;  
  52.   
  53.             eStatus = eMBASCIIInit( ucMBAddress, ucPort, ulBaudRate, eParity );  
  54.             break;  
  55. #endif  
  56.         default:  
  57.             eStatus = MB_EINVAL;  
  58.         }  
  59.   
  60.         //  
  61.         if( eStatus == MB_ENOERR )  
  62.         {  
  63.             if( !xMBPortEventInit() )  
  64.             {  
  65.                 /* port dependent event module initalization failed. */  
  66.                 eStatus = MB_EPORTERR;  
  67.             }  
  68.             else  
  69.             {  
  70.                 //设定当前状态  
  71.                 eMBCurrentMode = eMode;       //设定RTU模式  
  72.                 eMBState = STATE_DISABLED;    //modbus协议栈初始化状态,在此初始化为禁止  
  73.             }  
  74.         }  
  75.     }  
  76.     return eStatus;  
  77. }  
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.   
  10.     if( eMBState == STATE_DISABLED )  
  11.     {  
  12.         /* Activate the protocol stack. */  
  13.         pvMBFrameStartCur(  );  /*pvMBFrameStartCur = eMBRTUStart;调用eMBRTUStart函数*/            
  14.         eMBState = STATE_ENABLED;  
  15.     }  
  16.     else  
  17.     {  
  18.         eStatus = MB_EILLSTATE;  
  19.     }  
  20.     return eStatus;  
  21. }  
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.   
  20.     EXIT_CRITICAL_SECTION( );  
  21. }  
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.   
  14.     int             i;  
  15.     eMBErrorCode    eStatus = MB_ENOERR;         //modbus协议栈错误码  
  16.     eMBEventType    eEvent;                      //事件标志枚举  
  17.   
  18.     /* Check if the protocol stack is ready. */  
  19.     if( eMBState != STATE_ENABLED )              //检查协议栈是否使能  
  20.     {  
  21.         return MB_EILLSTATE;                     //协议栈未使能,返回协议栈无效错误码  
  22.     }  
  23.   
  24.     /* Check if there is a event available. If not return control to caller. 
  25.      * Otherwise we will handle the event. */  
  26.       
  27.     //查询事件  
  28.     if( xMBPortEventGet( &eEvent ) == TRUE )     //查询哪个事件发生  
  29.     {  
  30.         switch ( eEvent )  
  31.         {  
  32.         case EV_READY:  
  33.             break;  
  34.   
  35.         case EV_FRAME_RECEIVED:                  /*接收到一帧数据,此事件发生*/  
  36.             eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength );  
  37.             if( eStatus == MB_ENOERR )           /*报文长度和CRC校验正确*/  
  38.             {  
  39.                 /* Check if the frame is for us. If not ignore the frame. */  
  40.                 /*判断接收到的报文数据是否可接受,如果是,处理报文数据*/  
  41.                 if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) )  
  42.                 {  
  43.                     ( void )xMBPortEventPost( EV_EXECUTE );  //修改事件标志为EV_EXECUTE执行事件  
  44.                 }  
  45.             }  
  46.             break;  
  47.   
  48.         case EV_EXECUTE:                                     //对接收到的报文进行处理事件  
  49.             ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];     //获取PDU中第一个字节,为功能码  
  50.             eException = MB_EX_ILLEGAL_FUNCTION;             //赋错误码初值为无效的功能码  
  51.             for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )  
  52.             {  
  53.                 /* No more function handlers registered. Abort. */  
  54.                 if( xFuncHandlers[i].ucFunctionCode == 0 )  
  55.                 {  
  56.                     break;  
  57.                 }  
  58.                 else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode ) /*根据报文中的功能码,处理报文*/  
  59.                 {  
  60.                     eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength );/*对接收到的报文进行解析*/  
  61.                     break;  
  62.                 }  
  63.             }  
  64.   
  65.             /* If the request was not sent to the broadcast address we 
  66.              * return a reply. */  
  67.             if( ucRcvAddress != MB_ADDRESS_BROADCAST )  
  68.             {  
  69.                 if( eException != MB_EX_NONE )     /*接收到的报文有错误*/  
  70.                 {  
  71.                     /* An exception occured. Build an error frame. */  
  72.                     usLength = 0;                                                        /*响应发送数据的首字节为从机地址*/  
  73.                     ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR ); /*响应发送数据帧的第二个字节,功能码最高位置1*/  
  74.                     ucMBFrame[usLength++] = eException;                                  /*响应发送数据帧的第三个字节为错误码标识*/  
  75.                 }  
  76.                 if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS )  
  77.                 {  
  78.                     vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS );  
  79.                 }                  
  80.                 eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength );           /*modbus从机响应函数,发送响应给主机*/  
  81.             }  
  82.             break;  
  83.   
  84.         case EV_FRAME_SENT:  
  85.             break;  
  86.         }  
  87.     }  
  88.     return MB_ENOERR;  
  89. }  

至此:完成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.   
  9.     assert( eSndState == STATE_TX_IDLE );                  /*确保没有数据在发送*/  
  10.   
  11.     ( void )xMBPortSerialGetByte( ( CHAR * ) & ucByte );   /*从串口数据寄存器读取一个字节数据*/   
  12.       
  13.     //根据不同的状态转移  
  14.     switch ( eRcvState )  
  15.     {  
  16.         /* If we have received a character in the init state we have to 
  17.          * wait until the frame is finished. 
  18.          */  
  19.     case STATE_RX_INIT:  
  20.         vMBPortTimersEnable();                             /*开启3.5T定时器*/  
  21.         break;  
  22.   
  23.         /* In the error state we wait until all characters in the 
  24.          * damaged frame are transmitted. 
  25.          */  
  26.     case STATE_RX_ERROR:                                   /*数据帧被损坏,重启定时器,不保存串口接收的数据*/  
  27.         vMBPortTimersEnable();  
  28.         break;  
  29.   
  30.         /* In the idle state we wait for a new character. If a character 
  31.          * is received the t1.5 and t3.5 timers are started and the 
  32.          * receiver is in the state STATE_RX_RECEIVCE. 
  33.          */  
  34.     case STATE_RX_IDLE:                                    /*接收器空闲,开始接收,进入STATE_RX_RCV状态*/  
  35.         usRcvBufferPos = 0;  
  36.         ucRTUBuf[usRcvBufferPos++] = ucByte;               /*保存数据*/  
  37.           
  38.         eRcvState = STATE_RX_RCV;             
  39.   
  40.         /* Enable t3.5 timers. */  
  41.         vMBPortTimersEnable();                             /*每收到一个字节,都重启3.5T定时器*/  
  42.         break;  
  43.   
  44.         /* We are currently receiving a frame. Reset the timer after 
  45.          * every character received. If more than the maximum possible 
  46.          * number of bytes in a modbus frame is received the frame is 
  47.          * ignored. 
  48.          */  
  49.     case STATE_RX_RCV:  
  50.         if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX)  
  51.         {  
  52.             ucRTUBuf[usRcvBufferPos++] = ucByte;             /*接收数据*/  
  53.         }  
  54.         else  
  55.         {  
  56.             eRcvState = STATE_RX_ERROR;                      /*一帧报文的字节数大于最大PDU长度,忽略超出的数据*/  
  57.         }  
  58.                   
  59.         vMBPortTimersEnable();                               /*每收到一个字节,都重启3.5T定时器*/  
  60.         break;  
  61.     }  
  62.     return xTaskNeedSwitch;  
  63. }  
当主机发送一帧完整的报文后,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.   
  11.     switch ( eRcvState )  
  12.     {  
  13.       /* Timer t35 expired. Startup phase is finished. */  
  14.             /*上报modbus协议栈的事件状态给poll函数,EV_READY:初始化完成事件*/  
  15.     case STATE_RX_INIT:                                        
  16.             xNeedPoll = xMBPortEventPost( EV_READY );              
  17.         break;  
  18.   
  19.         /* A frame was received and t35 expired. Notify the listener that 
  20.          * a new frame was received. */  
  21.     case STATE_RX_RCV:                                     /*一帧数据接收完成*/  
  22.         xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED ); /*上报协议栈事件,接收到一帧完整的数据*/  
  23.         break;  
  24.   
  25.         /* An error occured while receiving the frame. */  
  26.     case STATE_RX_ERROR:  
  27.         break;  
  28.   
  29.         /* Function called in an illegal state. */  
  30.     default:                                                
  31.         assert( ( eRcvState == STATE_RX_INIT ) ||  
  32.                 ( eRcvState == STATE_RX_RCV ) || ( eRcvState == STATE_RX_ERROR ) );  
  33.     }  
  34.       
  35.     vMBPortTimersDisable(  );        /*当接收到一帧数据后,禁止3.5T定时器,只到接受下一帧数据开始,开始计时*/   
  36.           
  37.     eRcvState = STATE_RX_IDLE;       /*处理完一帧数据,接收器状态为空闲*/  
  38.   
  39.     return xNeedPoll;  
  40. }  

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

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.   
  10.     ENTER_CRITICAL_SECTION();  
  11.     assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX );    /*断言宏,判断接收到的字节数<256,如果>256,终止程序*/  
  12.   
  13.     /* Length and CRC check */  
  14.     if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN )  
  15.         && ( usMBCRC16( ( UCHAR * ) ucRTUBuf, usRcvBufferPos ) == 0 ) )  
  16.     {  
  17.         /* Save the address field. All frames are passed to the upper layed 
  18.          * and the decision if a frame is used is done there. 
  19.          */  
  20.         *pucRcvAddress = ucRTUBuf[MB_SER_PDU_ADDR_OFF];     //取接收到的第一个字节,modbus从机地址  
  21.   
  22.         /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus 
  23.          * size of address field and CRC checksum. 
  24.          */  
  25.         *pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC ); //减3  
  26.   
  27.         /* Return the start of the Modbus PDU to the caller. */  
  28.         *pucFrame = ( UCHAR * ) & ucRTUBuf[MB_SER_PDU_PDU_OFF];  
  29.         xFrameReceived = TRUE;  
  30.     }  
  31.     else  
  32.     {  
  33.         eStatus = MB_EIO;  
  34.     }  
  35.   
  36.     EXIT_CRITICAL_SECTION();  
  37.     return eStatus;  
  38. }  
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.   
  3. eMBException  
  4. eMBFuncReadCoils( UCHAR * pucFrame, USHORT * usLen )  
  5. {  
  6.     USHORT          usRegAddress;  
  7.     USHORT          usCoilCount;  
  8.     UCHAR           ucNBytes;  
  9.     UCHAR          *pucFrameCur;  
  10.   
  11.     eMBException    eStatus = MB_EX_NONE;  
  12.     eMBErrorCode    eRegStatus;  
  13.   
  14.     if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) )  
  15.     {  
  16.         /*线圈寄存器的起始地址*/            
  17.         usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 );  
  18.         usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] );  
  19.         //usRegAddress++;  
  20.   
  21.                 /*线圈寄存器个数*/  
  22.         usCoilCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF] << 8 );  
  23.         usCoilCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF + 1] );  
  24.   
  25.         /* Check if the number of registers to read is valid. If not 
  26.          * return Modbus illegal data value exception.  
  27.          */  
  28.               /*判断线圈寄存器个数是否合理*/  
  29.         if( ( usCoilCount >= 1 ) &&  
  30.             ( usCoilCount < MB_PDU_FUNC_READ_COILCNT_MAX ) )  
  31.         {  
  32.             /* Set the current PDU data pointer to the beginning. */  
  33.                       /*为发送缓冲pucFrameCur赋值*/  
  34.             pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];  
  35.             *usLen = MB_PDU_FUNC_OFF;  
  36.   
  37.             /* First byte contains the function code. */  
  38.                       /*响应报文第一个字节赋值为功能码0x01*/  
  39.             *pucFrameCur++ = MB_FUNC_READ_COILS;    
  40.             *usLen += 1;  
  41.   
  42.             /* Test if the quantity of coils is a multiple of 8. If not last 
  43.              * byte is only partially field with unused coils set to zero. */   
  44.                         /*usCoilCount%8有余数,ucNBytes加1,不够的位填充0*/  
  45.             if( ( usCoilCount & 0x0007 ) != 0 )  
  46.             {  
  47.                 ucNBytes = ( UCHAR )( usCoilCount / 8 + 1 );  
  48.             }  
  49.             else  
  50.             {  
  51.                 ucNBytes = ( UCHAR )( usCoilCount / 8 );  
  52.             }  
  53.             *pucFrameCur++ = ucNBytes;  
  54.             *usLen += 1;  
  55.   
  56.             eRegStatus =  
  57.                 eMBRegCoilsCB( pucFrameCur, usRegAddress, usCoilCount,  
  58.                                MB_REG_READ );  
  59.   
  60.             /* If an error occured convert it into a Modbus exception. */  
  61.             if( eRegStatus != MB_ENOERR )  
  62.             {  
  63.                 eStatus = prveMBError2Exception( eRegStatus );  
  64.             }  
  65.             else  
  66.             {  
  67.                 /* The response contains the function code, the starting address 
  68.                  * and the quantity of registers. We reuse the old values in the  
  69.                  * buffer because they are still valid. */  
  70.                 *usLen += ucNBytes;;  
  71.             }  
  72.         }  
  73.         else  
  74.         {  
  75.             eStatus = MB_EX_ILLEGAL_DATA_VALUE;  
  76.         }  
  77.     }  
  78.     else  
  79.     {  
  80.         /* Can't be a valid read coil register request because the length 
  81.          * is incorrect. */  
  82.         eStatus = MB_EX_ILLEGAL_DATA_VALUE;  
  83.     }  
  84.     return eStatus;  
  85. }  

至此:报文解析结束,得到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.   
  12.     ENTER_CRITICAL_SECTION(  );  
  13.   
  14.     /* Check if the receiver is still in idle state. If not we where to 
  15.      * slow with processing the received frame and the master sent another 
  16.      * frame on the network. We have to abort sending the frame. 
  17.      */  
  18.     if( eRcvState == STATE_RX_IDLE )  
  19.     {  
  20.         /* First byte before the Modbus-PDU is the slave address. */  
  21.               /*在协议数据单元前加从机地址*/  
  22.         pucSndBufferCur = ( UCHAR * ) pucFrame - 1;  
  23.         usSndBufferCount = 1;  
  24.   
  25.         /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */  
  26.         pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;  
  27.         usSndBufferCount += usLength;  
  28.   
  29.         /* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */  
  30.         usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );  
  31.         ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );  
  32.         ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );  
  33.   
  34.         /* Activate the transmitter. */  
  35.         eSndState = STATE_TX_XMIT;                         //发送状态  
  36.         xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );  /*发送一个字节的数据,进入发送中断函数,启动传输*/  
  37.         pucSndBufferCur++;                                 /* next byte in sendbuffer. */  
  38.         usSndBufferCount--;  
  39.         vMBPortSerialEnable( FALSE, TRUE );                /*使能发送,禁止接收*/  
  40.     }  
  41.     else  
  42.     {  
  43.         eStatus = MB_EIO;  
  44.     }  
  45.     EXIT_CRITICAL_SECTION(  );  
  46.     return eStatus;  
  47. }  

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

xMBRTUTransmitFSM()函数:(mbrtu.c)

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

你可能感兴趣的:(FreeModbus源码详解(转))