FreeModbus源碼詳解(轉)


 學習轉載原文鏈接:

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

https://blog.csdn.net/u014748120/article/details/80313215

本篇按照FreeModbus協議棧的工作流程,對源代碼進行總結解析;FreeModbus協議棧作為從機,等待主機傳送的數據,當從機接收到一幀完整的報文后,對報文進行解析,然后響應主機,發送報文給主機,實現主機和從機之間的通信;

1:demo.c中三個函數,完成協議棧的准備工作;

eMBInit()函數:(mb.c)

 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)

 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)

 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)

 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)

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

在第一階段中eMBInit()函數中賦值pxMBFrameCBByteReceived = xMBRTUReceiveFSM,發生接收中斷時,最終調用xMBRTUReceiveFSM函數對數據進行接收;

 

xMBRTUReceiveFSM()函數:(mbrtu.c)

 1 /*函數功能  
 2 *1:將接收到的數據存入ucRTUBuf[]中;  
 3 *2:usRcvBufferPos為全局變量,表示接收數據的個數;  
 4 *3:每接收到一個字節的數據,3.5T定時器清0  
 5 */  
 6 BOOL  
 7 xMBRTUReceiveFSM( void )  
 8 {  
 9     BOOL            xTaskNeedSwitch = FALSE;  
10     UCHAR           ucByte;  
11   
12     assert( eSndState == STATE_TX_IDLE );                  /*確保沒有數據在發送*/  
13   
14     ( void )xMBPortSerialGetByte( ( CHAR * ) & ucByte );   /*從串口數據寄存器讀取一個字節數據*/   
15       
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           
41         eRcvState = STATE_RX_RCV;             
42   
43         /* Enable t3.5 timers. */  
44         vMBPortTimersEnable();                             /*每收到一個字節,都重啟3.5T定時器*/  
45         break;  
46   
47         /* We are currently receiving a frame. Reset the timer after 
48          * every character received. If more than the maximum possible 
49          * number of bytes in a modbus frame is received the frame is 
50          * ignored. 
51          */  
52     case STATE_RX_RCV:  
53         if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX)  
54         {  
55             ucRTUBuf[usRcvBufferPos++] = ucByte;             /*接收數據*/  
56         }  
57         else  
58         {  
59             eRcvState = STATE_RX_ERROR;                      /*一幀報文的字節數大於最大PDU長度,忽略超出的數據*/  
60         }  
61                   
62         vMBPortTimersEnable();                               /*每收到一個字節,都重啟3.5T定時器*/  
63         break;  
64     }  
65     return xTaskNeedSwitch;  
66 } 

當主機發送一幀完整的報文后,3.5T定時器中斷發生,定時器中斷最終回調xMBRTUTimerT35Expired函數;

 

xMBRTUTimerT35Expired()函數:(mbrtu.c)

 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)

 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)

 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) 

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

xFuncHandlers[i]是結構體數組,存放的是功能碼以及對應的報文解析函數,原型如下:

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

以下列舉讀線圈函數舉例:

 

eMBFuncReadCoils()讀線圈寄存器函數: (mbfunccoils.c)

 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()函數:

 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)

 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 }   

進入發送中斷,串口發送中斷中調用prvvUARTTxReadyISR()函數,繼續調用pxMBFrameCBTransmitterEmpty()函數,pxMBFrameCBTransmitterEmpty為函數指針,最終調用xMBRTUTransmitFSM()函數;

xMBRTUTransmitFSM()函數:(mbrtu.c)

 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 }  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM