FreeModbus学习——接收状态机xMBRTUReceiveFSM

FreeModbus版本:1.6

接收状态机xMBRTUReceiveFSM

在协议栈初始化时,会发现有两个接收函数

peMBFrameReceiveCur = eMBRTUReceive;

pxMBFrameCBByteReceived = xMBRTUReceiveFSM;

那么这两个接收函数哪个是状态机?它俩有什么区别呢?

xMBRTUReceiveFSM这个是接收状态机函数。

FSM是finite-state machine的缩写,即有限状态机。

看一下这个函数在哪里调用

pxMBFrameCBByteReceived = xMBRTUReceiveFSM;

在portserial.c文件中,会发现pxMBFrameCBByteReceived 被prvvUARTRxISR调用。

prvvUARTRxISR这个函数是串口接收中断时调用的。

串口接收中断由用户实现,比如这样

所以接收状态机的调用顺序是这样的

接收到字节 →

中断服务函数 →

prvvUARTRxISR →

pxMBFrameCBByteReceived →

xMBRTUReceiveFSM

先看一下接收状态机都有什么状态

c 复制代码
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

c 复制代码
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用于接收完一帧后,将这一帧取出来。

处理帧数据后要发送响应帧,这时会将关闭串口接收中断,使能串口发送中断。

相关推荐
淬渊阁24 分钟前
汇编学习结语
汇编·学习
炸膛坦客2 小时前
各类神经网络学习:(十)注意力机制(第2/4集),pytorch 中的多维注意力机制、自注意力机制、掩码自注意力机制、多头注意力机制
pytorch·神经网络·学习
帅弟1502 小时前
Day7 FIFO与鼠标控制
学习
DXM05213 小时前
牟乃夏《ArcGIS Engine地理信息系统开发教程》学习笔记1
开发语言·经验分享·笔记·学习·arcgis·c#·arcgis engine
穷儒公羊3 小时前
第一部分——Docker篇 第三章 构建自定义镜像
java·运维·后端·学习·docker·云原生·容器
Wythzhfrey3 小时前
51单片机Day03---让一个LED灯闪烁
c语言·单片机·嵌入式硬件·c#·51单片机
安小牛4 小时前
Kotlin 学习-集合
android·开发语言·学习·kotlin
小王努力学编程4 小时前
【Linux网络编程】UDP Echo Server的实现
linux·运维·服务器·网络·c++·学习·udp
双叶8364 小时前
(51单片机)串口通讯(串口通讯教程)(串口接收发送教程)
c语言·开发语言·c++·单片机·嵌入式硬件·microsoft·51单片机
s_little_monster5 小时前
【Linux】线程控制函数
linux·运维·服务器·经验分享·笔记·学习·学习方法