學習轉載原文鏈接:
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 }