FreeModbus library 程序详解

入手一份FreeModbus 已看呆 (?????)

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

1:demo.c中三个函数,完成协议栈的准备工作; eMBInit()函数:(mb.c)
/*函数功能:
*1:实现RTU模式和ASCALL模式的协议栈初始化;
*2:完成协议栈核心函数指针的赋值,包括Modbus协议栈的使能和禁止、报文的接收和响应、3.5T定时器中断回调函数、串口发送和接收中断回调函数;
*3:eMBRTUInit完成RTU模式下串口和3.5T定时器的初始化,需用户自己移植;
*4:设置Modbus协议栈的模式eMBCurrentMode为MB_RTU,设置Modbus协议栈状态eMBState为STATE_DISABLED;
*/
eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity ) { //错误状态初始值 eMBErrorCode eStatus = MB_ENOERR; //验证从机地址 if( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) || ( ucSlaveAddress < MB_ADDRESS_MIN ) || ( ucSlaveAddress > MB_ADDRESS_MAX )) { eStatus = MB_EINVAL; } else { ucMBAddress = ucSlaveAddress; /*从机地址的赋值*/switch ( eMode ) { #if MB_RTU_ENABLED > 0 case MB_RTU: pvMBFrameStartCur = eMBRTUStart; /*使能modbus协议栈*/ pvMBFrameStopCur = eMBRTUStop; /*禁用modbus协议栈*/ peMBFrameSendCur = eMBRTUSend; /*modbus从机响应函数*/ peMBFrameReceiveCur = eMBRTUReceive; /*modbus报文接收函数*/ pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL; //接收状态机 pxMBFrameCBByteReceived = xMBRTUReceiveFSM; /*串口接收中断最终调用此函数接收数据*/ //发送状态机 pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM; /*串口发送中断最终调用此函数发送数据*/ //报文到达间隔检查 pxMBPortCBTimerExpired = xMBRTUTimerT35Expired; /*定时器中断函数最终调用次函数完成定时器中断*/ //初始化RTU eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity ); break; #endif #if MB_ASCII_ENABLED > 0 case MB_ASCII: pvMBFrameStartCur = eMBASCIIStart; pvMBFrameStopCur = eMBASCIIStop; peMBFrameSendCur = eMBASCIISend; peMBFrameReceiveCur = eMBASCIIReceive; pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL; pxMBFrameCBByteReceived = xMBASCIIReceiveFSM; pxMBFrameCBTransmitterEmpty = xMBASCIITransmitFSM; pxMBPortCBTimerExpired = xMBASCIITimerT1SExpired; eStatus = eMBASCIIInit( ucMBAddress, ucPort, ulBaudRate, eParity ); break; #endif default: eStatus = MB_EINVAL; }// if( eStatus == MB_ENOERR ) { if( !xMBPortEventInit() ) { /* port dependent event module initalization failed. */ eStatus = MB_EPORTERR; } else { //设定当前状态 eMBCurrentMode = eMode; //设定RTU模式 eMBState = STATE_DISABLED; //modbus协议栈初始化状态,在此初始化为禁止 } } } return eStatus; }

eMBEnable()函数:(mb.c)
/*函数功能
*1:设置Modbus协议栈工作状态eMBState为STATE_ENABLED;
*2:调用pvMBFrameStartCur()函数激活协议栈
*/
eMBErrorCode eMBEnable( void ) { eMBErrorCode eStatus = MB_ENOERR; if( eMBState == STATE_DISABLED ) { /* Activate the protocol stack. */ pvMBFrameStartCur( ); /*pvMBFrameStartCur = eMBRTUStart; 调用eMBRTUStart函数*/ eMBState = STATE_ENABLED; } else { eStatus = MB_EILLSTATE; } return eStatus; }

eMBRTUStart()函数:(mbrtu.c)
/*函数功能
*1:设置接收状态机eRcvState为STATE_RX_INIT;
*2:使能串口接收,禁止串口发送,作为从机,等待主机传送的数据;
*3:开启定时器,3.5T时间后定时器发生第一次中断,此时eRcvState为STATE_RX_INIT,上报初始化完成事件,然后设置eRcvState为空闲STATE_RX_IDLE;
*4:每次进入3.5T定时器中断,定时器被禁止,等待串口有字节接收后,才使能定时器;
*/
void eMBRTUStart( void ) { ENTER_CRITICAL_SECTION( ); /* Initially the receiver is in the state STATE_RX_INIT. we start * the timer and if no character is received within t3.5 we change * to STATE_RX_IDLE. This makes sure that we delay startup of the * modbus protocol stack until the bus is free. */ eRcvState = STATE_RX_INIT; vMBPortSerialEnable( TRUE, FALSE ); vMBPortTimersEnable(); EXIT_CRITICAL_SECTION( ); }

eMBPoll()函数:(mb.c)
/*函数功能:
*1:检查协议栈状态是否使能,eMBState初值为STATE_NOT_INITIALIZED,在eMBInit()函数中被赋值为STATE_DISABLED,在eMBEnable函数中被赋值为STATE_ENABLE;
*2:轮询EV_FRAME_RECEIVED事件发生,若EV_FRAME_RECEIVED事件发生,接收一帧报文数据,上报EV_EXECUTE事件,解析一帧报文,响应(发送)一帧数据给主机;
*/
eMBErrorCode eMBPoll( void ) { static UCHAR *ucMBFrame; //接收和发送报文数据缓存区 static UCHAR ucRcvAddress; //modbus从机地址 static UCHAR ucFunctionCode; //功能码 static USHORT usLength; //报文长度 static eMBException eException; //错误码响应枚举int i; eMBErrorCode eStatus = MB_ENOERR; //modbus协议栈错误码 eMBEventType eEvent; //事件标志枚举/* Check if the protocol stack is ready. */ if( eMBState != STATE_ENABLED ) //检查协议栈是否使能 { return MB_EILLSTATE; //协议栈未使能,返回协议栈无效错误码 }/* Check if there is a event available. If not return control to caller. * Otherwise we will handle the event. *///查询事件 if( xMBPortEventGet( &eEvent ) == TRUE ) //查询哪个事件发生 { switch ( eEvent ) { case EV_READY: break; case EV_FRAME_RECEIVED: /*接收到一帧数据,此事件发生*/ eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength ); if( eStatus == MB_ENOERR ) /*报文长度和CRC校验正确*/ { /* Check if the frame is for us. If not ignore the frame. */ /*判断接收到的报文数据是否可接受,如果是,处理报文数据*/ if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) ) { ( void )xMBPortEventPost( EV_EXECUTE ); //修改事件标志为EV_EXECUTE执行事件 } } break; case EV_EXECUTE: //对接收到的报文进行处理事件 ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF]; //获取PDU中第一个字节,为功能码 eException = MB_EX_ILLEGAL_FUNCTION; //赋错误码初值为无效的功能码 for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ ) { /* No more function handlers registered. Abort. */ if( xFuncHandlers[i].ucFunctionCode == 0 ) { break; } else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode ) /*根据报文中的功能码,处理报文*/ { eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength ); /*对接收到的报文进行解析*/ break; } }/* If the request was not sent to the broadcast address we * return a reply. */ if( ucRcvAddress != MB_ADDRESS_BROADCAST ) { if( eException != MB_EX_NONE ) /*接收到的报文有错误*/ { /* An exception occured. Build an error frame. */ usLength = 0; /*响应发送数据的首字节为从机地址*/ ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR ); /*响应发送数据帧的第二个字节,功能码最高位置1*/ ucMBFrame[usLength++] = eException; /*响应发送数据帧的第三个字节为错误码标识*/ } if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS ) { vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS ); } eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength ); /*modbus从机响应函数,发送响应给主机*/ } break; case EV_FRAME_SENT: break; } } return MB_ENOERR; }

至此:完成Modbus协议栈的初始化准备工作,eMBPoll()函数轮询等待接收完成事件发生,接收机状态eRcvState为STATE_RX_IDLE空闲;
2:FreeModbus协议栈接收一帧完整报文机制: FreeModbus协议栈通过淳口中断接收一帧数据,用户需在串口接收中断中回调prvvUARTRxISR()函数;
prvvUARTRxISR()函数:(portserial.c)
static void prvvUARTRxISR( void )
{
pxMBFrameCBByteReceived( );
}
在第一阶段中eMBInit()函数中赋值pxMBFrameCBByteReceived = xMBRTUReceiveFSM,发生接收中断时,最终调用xMBRTUReceiveFSM函数对数据进行接收;
xMBRTUReceiveFSM()函数:(mbrtu.c)
/*函数功能
*1:将接收到的数据存入ucRTUBuf[]中;
*2:usRcvBufferPos为全局变量,表示接收数据的个数;
*3:每接收到一个字节的数据,3.5T定时器清0
*/
BOOL xMBRTUReceiveFSM( void ) { BOOL xTaskNeedSwitch = FALSE; UCHAR ucByte; assert( eSndState == STATE_TX_IDLE ); /*确保没有数据在发送*/( void )xMBPortSerialGetByte( ( CHAR * ) & ucByte ); /*从串口数据寄存器读取一个字节数据*/ //根据不同的状态转移 switch ( eRcvState ) { /* If we have received a character in the init state we have to * wait until the frame is finished. */ case STATE_RX_INIT: vMBPortTimersEnable(); /*开启3.5T定时器*/ break; /* In the error state we wait until all characters in the * damaged frame are transmitted. */ case STATE_RX_ERROR: /*数据帧被损坏,重启定时器,不保存串口接收的数据*/ vMBPortTimersEnable(); break; /* In the idle state we wait for a new character. If a character * is received the t1.5 and t3.5 timers are started and the * receiver is in the state STATE_RX_RECEIVCE. */ case STATE_RX_IDLE: /*接收器空闲,开始接收,进入STATE_RX_RCV状态*/ usRcvBufferPos = 0; ucRTUBuf[usRcvBufferPos++] = ucByte; /*保存数据*/eRcvState = STATE_RX_RCV; /* Enable t3.5 timers. */ vMBPortTimersEnable(); /*每收到一个字节,都重启3.5T定时器*/ break; /* We are currently receiving a frame. Reset the timer after * every character received. If more than the maximum possible * number of bytes in a modbus frame is received the frame is * ignored. */ case STATE_RX_RCV: if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX) { ucRTUBuf[usRcvBufferPos++] = ucByte; /*接收数据*/ } else { eRcvState = STATE_RX_ERROR; /*一帧报文的字节数大于最大PDU长度,忽略超出的数据*/ }vMBPortTimersEnable(); /*每收到一个字节,都重启3.5T定时器*/ break; } return xTaskNeedSwitch; }

当主机发送一帧完整的报文后,3.5T定时器中断发生,定时器中断最终回调xMBRTUTimerT35Expired函数;
xMBRTUTimerT35Expired()函数:(mbrtu.c)
/*函数功能
*1:从机接受完成一帧数据后,接收状态机eRcvState为STATE_RX_RCV;
*2:上报“接收到报文”事件(EV_FRAME_RECEIVED)
*3:禁止3.5T定时器,设置接收状态机eRcvState状态为STATE_RX_IDLE空闲;
*/
BOOL xMBRTUTimerT35Expired( void ) { BOOL xNeedPoll = FALSE; switch ( eRcvState ) { /* Timer t35 expired. Startup phase is finished. */ /*上报modbus协议栈的事件状态给poll函数,EV_READY:初始化完成事件*/ case STATE_RX_INIT: xNeedPoll = xMBPortEventPost( EV_READY ); break; /* A frame was received and t35 expired. Notify the listener that * a new frame was received. */ case STATE_RX_RCV: /*一帧数据接收完成*/ xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED ); /*上报协议栈事件,接收到一帧完整的数据*/ break; /* An error occured while receiving the frame. */ case STATE_RX_ERROR: break; /* Function called in an illegal state. */ default: assert( ( eRcvState == STATE_RX_INIT ) || ( eRcvState == STATE_RX_RCV ) || ( eRcvState == STATE_RX_ERROR ) ); }vMBPortTimersDisable( ); /*当接收到一帧数据后,禁止3.5T定时器,只到接受下一帧数据开始,开始计时*/ eRcvState = STATE_RX_IDLE; /*处理完一帧数据,接收器状态为空闲*/return xNeedPoll; }

至此:从机接收到一帧完整的报文,存储在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:检查协议栈状态是否使能,eMBState初值为STATE_NOT_INITIALIZED,在eMBInit()函数中被赋值为STATE_DISABLED,在eMBEnable函数中被赋值为STATE_ENABLE;
*2:轮询EV_FRAME_RECEIVED事件发生,若EV_FRAME_RECEIVED事件发生,接收一帧报文数据,上报EV_EXECUTE事件,解析一帧报文,响应(发送)一帧数据给主机;
*/
eMBErrorCode eMBPoll( void ) { static UCHAR *ucMBFrame; //接收和发送报文数据缓存区 static UCHAR ucRcvAddress; //modbus从机地址 static UCHAR ucFunctionCode; //功能码 static USHORT usLength; //报文长度 static eMBException eException; //错误码响应枚举int i; eMBErrorCode eStatus = MB_ENOERR; //modbus协议栈错误码 eMBEventType eEvent; //事件标志枚举/* Check if the protocol stack is ready. */ if( eMBState != STATE_ENABLED ) //检查协议栈是否使能 { return MB_EILLSTATE; //协议栈未使能,返回协议栈无效错误码 }/* Check if there is a event available. If not return control to caller. * Otherwise we will handle the event. *///查询事件 if( xMBPortEventGet( &eEvent ) == TRUE ) //查询哪个事件发生 { switch ( eEvent ) { case EV_READY: break; case EV_FRAME_RECEIVED: /*接收到一帧数据,此事件发生*/ eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength ); if( eStatus == MB_ENOERR ) /*报文长度和CRC校验正确*/ { /* Check if the frame is for us. If not ignore the frame. */ /*判断接收到的报文数据是否可接受,如果是,处理报文数据*/ if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) ) { ( void )xMBPortEventPost( EV_EXECUTE ); //修改事件标志为EV_EXECUTE执行事件 } } break; case EV_EXECUTE: //对接收到的报文进行处理事件 ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF]; //获取PDU中第一个字节,为功能码 eException = MB_EX_ILLEGAL_FUNCTION; //赋错误码初值为无效的功能码 for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ ) { /* No more function handlers registered. Abort. */ if( xFuncHandlers[i].ucFunctionCode == 0 ) { break; } else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode ) /*根据报文中的功能码,处理报文*/ { eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength ); /*对接收到的报文进行解析*/ break; } }/* If the request was not sent to the broadcast address we * return a reply. */ if( ucRcvAddress != MB_ADDRESS_BROADCAST ) { if( eException != MB_EX_NONE ) /*接收到的报文有错误*/ { /* An exception occured. Build an error frame. */ usLength = 0; /*响应发送数据的首字节为从机地址*/ ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR ); /*响应发送数据帧的第二个字节,功能码最高位置1*/ ucMBFrame[usLength++] = eException; /*响应发送数据帧的第三个字节为错误码标识*/ } if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS ) { vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS ); } eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength ); /*modbus从机响应函数,发送响应给主机*/ } break; case EV_FRAME_SENT: break; } } return MB_ENOERR; } eMBRTUReceive()函数:(mbrtu.c)/*eMBPoll函数轮询到EV_FRAME_RECEIVED事件时,调用peMBFrameReceiveCur(),此函数是用户为函数指针peMBFrameReceiveCur()的赋值 *此函数完成的功能:从一帧数据报文中,取得modbus从机地址给pucRcvAddress,PDU报文的长度给pusLength,PDU报文的首地址给pucFrame,函数 *形参全部为地址传递*/ eMBErrorCode eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength ) { BOOL xFrameReceived = FALSE; eMBErrorCode eStatus = MB_ENOERR; ENTER_CRITICAL_SECTION(); assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ); /*断言宏,判断接收到的字节数<256,如果>256,终止程序*//* Length and CRC check */ if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN ) && ( usMBCRC16( ( UCHAR * ) ucRTUBuf, usRcvBufferPos ) == 0 ) ) { /* Save the address field. All frames are passed to the upper layed * and the decision if a frame is used is done there. */ *pucRcvAddress = ucRTUBuf[MB_SER_PDU_ADDR_OFF]; //取接收到的第一个字节,modbus从机地址/* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus * size of address field and CRC checksum. */ *pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC ); //减3/* Return the start of the Modbus PDU to the caller. */ *pucFrame = ( UCHAR * ) & ucRTUBuf[MB_SER_PDU_PDU_OFF]; xFrameReceived = TRUE; } else { eStatus = MB_EIO; }EXIT_CRITICAL_SECTION(); return eStatus; } xMBPortEventPost()函数:(portevent.c) BOOL xMBPortEventPost( eMBEventType eEvent ) { xEventInQueue = TRUE; eQueuedEvent = eEvent; return TRUE; }

xFuncHandlers[i]是结构体数组,存放的是功能码以及对应的报文解析函数,原型如下:
typedef struct { UCHAR ucFunctionCode; pxMBFunctionHandler pxHandler; } xMBFunctionHandler; 以下列举读线圈函数举例: eMBFuncReadCoils()读线圈寄存器函数: (mbfunccoils.c)#if MB_FUNC_READ_COILS_ENABLED > 0eMBException eMBFuncReadCoils( UCHAR * pucFrame, USHORT * usLen ) { USHORT usRegAddress; USHORT usCoilCount; UCHAR ucNBytes; UCHAR *pucFrameCur; eMBException eStatus = MB_EX_NONE; eMBErrorCode eRegStatus; if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) ) { /*线圈寄存器的起始地址*/ usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 ); usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] ); //usRegAddress++; /*线圈寄存器个数*/ usCoilCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF] << 8 ); usCoilCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF + 1] ); /* Check if the number of registers to read is valid. If not * return Modbus illegal data value exception. */ /*判断线圈寄存器个数是否合理*/ if( ( usCoilCount >= 1 ) && ( usCoilCount < MB_PDU_FUNC_READ_COILCNT_MAX ) ) { /* Set the current PDU data pointer to the beginning. */ /*为发送缓冲pucFrameCur赋值*/ pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF]; *usLen = MB_PDU_FUNC_OFF; /* First byte contains the function code. */ /*响应报文第一个字节赋值为功能码0x01*/ *pucFrameCur++ = MB_FUNC_READ_COILS; *usLen += 1; /* Test if the quantity of coils is a multiple of 8. If not last * byte is only partially field with unused coils set to zero. */ /*usCoilCount%8有余数,ucNBytes加1,不够的位填充0*/ if( ( usCoilCount & 0x0007 ) != 0 ) { ucNBytes = ( UCHAR )( usCoilCount / 8 + 1 ); } else { ucNBytes = ( UCHAR )( usCoilCount / 8 ); } *pucFrameCur++ = ucNBytes; *usLen += 1; eRegStatus = eMBRegCoilsCB( pucFrameCur, usRegAddress, usCoilCount, MB_REG_READ ); /* If an error occured convert it into a Modbus exception. */ if( eRegStatus != MB_ENOERR ) { eStatus = prveMBError2Exception( eRegStatus ); } else { /* The response contains the function code, the starting address * and the quantity of registers. We reuse the old values in the * buffer because they are still valid. */ *usLen += ucNBytes; ; } } else { eStatus = MB_EX_ILLEGAL_DATA_VALUE; } } else { /* Can' t be a valid read coil register request because the length * is incorrect. */ eStatus = MB_EX_ILLEGAL_DATA_VALUE; } return eStatus; }

至此:报文解析结束,得到ucMBFrame响应缓冲和usLength响应报文长度,等待发送报文;
4:发送响应报文 解析完一帧完整的报文后,eMBPoll()函数中调用peMBFrameSendCur()函数进行响应,eMBFrameSendCur()是函数指针,最终会调用eMBRTUSend()函数发送响应;
eMBRTUSend()函数:
/*函数功能
*1:对响应报文PDU前面加上从机地址;
*2:对响应报文PDU后加上CRC校;
*3:使能发送,启动传输;
*/
eMBErrorCode eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength ) { eMBErrorCode eStatus = MB_ENOERR; USHORT usCRC16; ENTER_CRITICAL_SECTION( ); /* Check if the receiver is still in idle state. If not we where to * slow with processing the received frame and the master sent another * frame on the network. We have to abort sending the frame. */ if( eRcvState == STATE_RX_IDLE ) { /* First byte before the Modbus-PDU is the slave address. */ /*在协议数据单元前加从机地址*/ pucSndBufferCur = ( UCHAR * ) pucFrame - 1; usSndBufferCount = 1; /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */ pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress; usSndBufferCount += usLength; /* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */ usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ); ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF ); ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 ); /* Activate the transmitter. */ eSndState = STATE_TX_XMIT; //发送状态 xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur ); /*发送一个字节的数据,进入发送中断函数,启动传输*/ pucSndBufferCur++; /* next byte in sendbuffer. */ usSndBufferCount--; vMBPortSerialEnable( FALSE, TRUE ); /*使能发送,禁止接收*/ } else { eStatus = MB_EIO; } EXIT_CRITICAL_SECTION( ); return eStatus; }

进入发送中断,串口发送中断中调用prvvUARTTxReadyISR()函数,继续调用pxMBFrameCBTransmitterEmpty()函数,pxMBFrameCBTransmitterEmpty为函数指针,最终调用xMBRTUTransmitFSM()函数;
xMBRTUTransmitFSM()函数:(mbrtu.c)
BOOL xMBRTUTransmitFSM( void ) { BOOL xNeedPoll = FALSE; assert( eRcvState == STATE_RX_IDLE ); switch ( eSndState ) { /* We should not get a transmitter event if the transmitter is in * idle state.*/ case STATE_TX_IDLE: /*发送器处于空闲状态,使能接收,禁止发送*/ /* enable receiver/disable transmitter. */ vMBPortSerialEnable( TRUE, FALSE ); break; case STATE_TX_XMIT: /*发送器处于发送状态,在从机发送函数eMBRTUSend中赋值STATE_TX_XMIT*/ /* check if we are finished. */ if( usSndBufferCount != 0 ) { //发送数据 xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur ); pucSndBufferCur++; /* next byte in sendbuffer. */ usSndBufferCount--; } else { //传递任务,发送完成 xNeedPoll = xMBPortEventPost( EV_FRAME_SENT ); /*协议栈事件状态赋值为EV_FRAME_SENT,发送完成事件,eMBPoll函数会对此事件进行处理*/ /* Disable transmitter. This prevents another transmit buffer * empty interrupt. */ vMBPortSerialEnable( TRUE, FALSE ); /*使能接收,禁止发送*/ eSndState = STATE_TX_IDLE; /*发送器状态为空闲状态*/ } break; }return xNeedPoll; }

【FreeModbus library 程序详解】至此:协议栈准备工作,从机接受报文,解析报文,从机发送响应报文四部分结束;
Over ?(^?^●)?

    推荐阅读