FreeModbus版本:1.6
接收状态机xMBRTUReceiveFSM
在协议栈初始化时,会发现有两个接收函数
peMBFrameReceiveCur = eMBRTUReceive;
pxMBFrameCBByteReceived = xMBRTUReceiveFSM;
那么这两个接收函数哪个是状态机?它俩有什么区别呢?
xMBRTUReceiveFSM这个是接收状态机函数。
FSM是finite-state machine的缩写,即有限状态机。
看一下这个函数在哪里调用
pxMBFrameCBByteReceived = xMBRTUReceiveFSM;
在portserial.c文件中,会发现pxMBFrameCBByteReceived 被prvvUARTRxISR调用。
prvvUARTRxISR这个函数是串口接收中断时调用的。
串口接收中断由用户实现,比如这样
所以接收状态机的调用顺序是这样的
接收到字节 →
中断服务函数 →
prvvUARTRxISR →
pxMBFrameCBByteReceived →
xMBRTUReceiveFSM
先看一下接收状态机都有什么状态
typedef enum
{
STATE_RX_INIT, /*!< Receiver is in initial state. */
STATE_RX_IDLE, /*!< Receiver is in idle state. */
STATE_RX_RCV, /*!< Frame is beeing received. */
STATE_RX_ERROR /*!< If the frame is invalid. */
} eMBRcvState;
分别是初始化态,空闲态,接收态,错误态。
来看一下接收状态机的实现xMBRTUReceiveFSM
BOOL
xMBRTUReceiveFSM( void )
{
BOOL xTaskNeedSwitch = FALSE;
UCHAR ucByte;
assert( eSndState == STATE_TX_IDLE );
/* Always read the character. */
( 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( );
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:
usRcvBufferPos = 0;
ucRTUBuf[usRcvBufferPos++] = ucByte;
eRcvState = STATE_RX_RCV;
/* Enable t3.5 timers. */
vMBPortTimersEnable( );
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;
}
vMBPortTimersEnable( );
break;
}
return xTaskNeedSwitch;
}
初试化态:
协议栈使能后,会将接收状态机赋值为初始化态,串口接收使能,且定时器也使能(重新计数),定时器进入溢出中断,然后会将接收状态机由初始化态变为空闲态。
如果刚使能后,串口就接收到数据了怎么办?
我们无法判断这个数据是不是帧首,所以这一帧就舍掉。
当接收状态机状态为初始化态,接收到一个数据,进入接收状态机,不存储这个数据,而是重启定时器。再接收,再重启,直到这一帧完成(帧与帧之间会有个时间间隔),然后进入定时器中断,将接收状态机变为空闲态,并发布ready事件,代表协议栈就绪。
也就是说接收状态机为空闲态时,才开始接收第一个数据作为帧首。
当接收状态机为空闲态时,开始接收第一个数据作为帧首,然后将接收状态机置为接收态,重新计数定时器。
当接收状态机为接收态时,将存到的每一个数ucByte都放入缓存ucRTUBuf中,且重置定时器。
那么它是如何与协议栈的事件机制结合的呢
我们知道,在每一次进入接收状态机xMBRTUReceiveFSM,不管什么状态,都会重置定时器。
当接收状态机为接收态时,每接收到一个数据,都会重置定时器。
直到 这一帧 接收完成
然后进入定时器中断
在定时器中断里,会先发出事件 EV_FRAME_RECEIVED 即帧接收完成。然后再将接收状态机置为空闲态
然后在轮询函数eMBPoll中,看到事件EV_FRAME_RECEIVED ,开始处理事件。
使用peMBFrameReceiveCur这个函数,将一帧数据读取出来
所以peMBFrameReceiveCur和xMBRTUReceiveFSM的区别是
xMBRTUReceiveFSM用于在串口接收中断中读取数据。
peMBFrameReceiveCur用于接收完一帧后,将这一帧取出来。
处理帧数据后要发送响应帧,这时会将关闭串口接收中断,使能串口发送中断。