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:
/* check if we are finished. */
if( usSndBufferCount != 0 )
{
xMBPortSerialPutByte( ( CHAR)*pucSndBufferCur );
pucSndBufferCur++; /* next byte in sendbuffer. */
usSndBufferCount--;
}
else
{
xNeedPoll = xMBPortEventPost(EV_FRAME_SENT );
/* Disable transmitter. Thisprevents another transmit buffer
* empty interrupt. */
vMBPortSerialEnable( TRUE, FALSE );
eSndState = STATE_TX_IDLE;
}
break;
}
return xNeedPoll;
}
xMBRTUTransmitFSM在eMBInit()中被赋值给了pxMBFrameCBTransmitterEmpty,而pxMBFrameCBTransmitterEmpty又被xMBPortSerialPoll调用,最后xMBPortSerialPoll被xMBPortEventGet中被调用。
xMBRTUTransmitFSM只有两个状态。
typedef enum
{
STATE_TX_IDLE, /*!< Transmitter is in idle state. */
STATE_TX_XMIT /*!< Transmitter is in transfer state. */
} eMBSndState;
在没有发送任务的时候,它是处理STATE_TX_IDLE态,在modbus协议栈初始化的时候它就是这个态,而这个STATE_TX_XMIT发送态则是用来将要发送的数据推送到发送缓冲的(这里你可以用你的串口中断来做,但我觉得用DMA会更好一些),发送完数据后又返回到STATE_TX_IDLE态,但是STATE_TX_XMIT是谁让它进入的呢?
eMBErrorCode
eMBRTUSend( UCHAR ucSlaveAddress, constUCHAR * 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;
vMBPortSerialEnable( FALSE, TRUE );
}
else
{
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION( );
return eStatus;
}
这个eMBRTUSend就是用来将xMBRTUTransmitFSM置为STATE_TX_XMIT的函数,同时它还使能串口发送功能。eMBRTUSend本身却是在eMBPoll()的EV_EXECUTE状态的第二段被调用的,就是当收到功能码时我们回应给主机的这一部分。
/* If the request wasnot 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 );
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 );
}
break;
其中的peMBFrameSendCur()就是eMBRTUSend(),在eMBInit我们将eMBRTUSend赋值给了peMBFrameSendCur()。 现在咱们终于绕出来了,发送流程也介绍清楚了。
写到这里,我估计你可能会有一些疑惑,在这个例程中真正完成发送和接收串口的代码在哪里?
BOOL
xMBPortEventGet( eMBEventType * eEvent )
{
BOOL xEventHappened =FALSE;
if( xEventInQueue )
{
*eEvent = eQueuedEvent;
xEventInQueue = FALSE;
xEventHappened = TRUE;
}
else
{
/* Poll the serial device. The serial device timeouts if no
* characters have been received within for t3.5 during an
* active transmission or if nothing happens within a specified
* amount of time. Both timeouts are configured from the timer
* init functions.
*/
( void )xMBPortSerialPoll( );
/* Check if any of the timers have expired. */
vMBPortTimerPoll( );
}
return xEventHappened;
}
其实它们就在xMBPortSerialPoll里,换句话说,每次当mb.c的状态机调用xMBPortEventGet()都在进行串口操作,要么是发送要么是接收。
BOOL
xMBPortSerialPoll( )
{
BOOL bStatus = TRUE;
DWORD dwBytesRead;
DWORD dwBytesWritten;
DWORD i;
while( bRxEnabled )
{
/* buffer wrap around. */
if( uiRxBufferPos >= BUF_SIZE )
uiRxBufferPos = 0;
if( ReadFile( g_hSerial, &ucBuffer[uiRxBufferPos],
BUF_SIZE - uiRxBufferPos,&dwBytesRead, NULL ) )
{
if( dwBytesRead == 0 )
{
/* timeout with no bytes. */
break;
}
else if( dwBytesRead > 0 )
{
vMBPortLog( MB_LOG_DEBUG, _T("SER-POLL" ),
_T( "detected end of frame (t3.5expired.)\r\n" ) );
for( i = 0; i < dwBytesRead;i++ )
{
/* Call the modbus stackand let him fill the buffers. */
( void)pxMBFrameCBByteReceived( );
}
}
}
else
{
vMBPortLog( MB_LOG_ERROR, _T("SER-POLL" ), _T( "I/O error on serial device: %s" ),
Error2String( GetLastError( ) ) );
bStatus = FALSE;
}
}
if( bTxEnabled )
{
while( bTxEnabled )
{
( void)pxMBFrameCBTransmitterEmpty( );
/* Call the modbus stack to let himfill the buffer. */
}
dwBytesWritten = 0;
if( !WriteFile
( g_hSerial, &ucBuffer[0],uiTxBufferPos, &dwBytesWritten, NULL )
|| ( dwBytesWritten !=uiTxBufferPos ) )
{
vMBPortLog( MB_LOG_ERROR, _T("SER-POLL" ), _T( "I/O error on serial device: %s" ),
Error2String(GetLastError ( ) ) );
bStatus = FALSE;
}
}
return bStatus;
}
xMBPortSerialPoll依据bRxEnabled 和 bTxEnabled 来区分到底是发送还是接收。
我看到有些人说freemodbus只能通过阻塞方式发送和接收串口数据很显然是错误的,它可以用普通串口中断或者是串口DMA来做。
|